Merge branch 'master' into plugin_putin

pull/1/head
Rob Stupay 6 years ago
commit 58cfe7c7cb
  1. 45
      embark/README.md
  2. 72
      embark/index.js
  3. 2
      embark/package.json
  4. 15
      package.json
  5. 48
      src/app.js
  6. 23
      src/app/components/hidden-panel.js
  7. 24
      src/app/components/main-panel.js
  8. 111
      src/app/components/panel.js
  9. 8
      src/app/components/plugin-manager-component.js
  10. 102
      src/app/components/plugin-manager-settings.js
  11. 43
      src/app/components/swap-panel-api.js
  12. 102
      src/app/components/swap-panel-component.js
  13. 77
      src/app/components/swap-panel.js
  14. 20
      src/app/components/vertical-icons-api.js
  15. 6
      src/app/components/vertical-icons.js
  16. 1
      src/app/debugger/debuggerUI/TxBrowser.js
  17. 8
      src/app/panels/main-view.js
  18. 1
      src/app/ui/landing-page/landing-page.js
  19. 19
      src/framingService.js
  20. 2
      src/lib/panels-resize.js
  21. 19
      src/universal-dapp.js
  22. 5
      test-browser/plugin/plugin.js
  23. 5
      test-browser/plugin/remix.js
  24. 50
      test-browser/tests/compiling.js

@ -0,0 +1,45 @@
# embark-remix
An Embark plugin that allows Remix to connect to a local DApp via [`remixd`](https://github.com/ethereum/remixd). This plugin serves a local copy of Remix IDE from the machine running the plugin or alternatively allows connection from the public [Remix IDE](https://remix.ethereum.org). The URL of the Remix IDE can be specified in the Embark plugin options, specified below.
## Options
To configure options for the `embark-remix` plugin, modify the `plugins` property of `embark.json` in the DApp.
### How to use default options
To pass no options to the plugin and use the defaults, simply use an empty object:
```
"plugins": {
"embark-remix": {}
}
```
This will provide the default options to the plugin (shown below).
### Available options
The available options for this plugin are below. Default options are shown below. This is equivalent to passing an empty object `{}`.
```
"plugins": {
"embark-remix": {
"readOnly": false,
"remixIde": {
"protocol": "http",
"host": "localhost",
"port": 8088
}
}
}
```
`readOnly` does not let Remix update the contents on the local filesystem.
- Default: `false`
`remixIde` specifies the URL that the Remix IDE will be served from. If this is a `localhost` URL, the plugin creates a server that is responsible for listening on this URL.
- Default: `(see above)`
If it is preferred to connect to the public Remix IDE at https://remix.ethereum.org, set the `remixIde` config to:
```
"remixIde": {
"protocol": "https",
"host": "remix.ethereum.org",
"port": false
}
```

@ -1,22 +1,66 @@
/* global path */ const httpServer = require('http-server')
var httpServer = require('http-server') const remixd = require('remixd')
var remixd = require('remixd') const path = require('path')
const merge = require('merge')
const colors = require('colors')
const DEFAULT_OPTIONS = {
protocol: 'http',
host: 'localhost',
port: '8088'
}
module.exports = (embark) => { module.exports = (embark) => {
var server = httpServer.createServer({ // plugin options
root: path.join(__dirname, '/node_modules/remix-ide') const readOnly = embark.pluginConfig.readOnly || false
}) const {protocol, host, port} = merge.recursive(DEFAULT_OPTIONS, embark.pluginConfig.remixIde)
// globals
const remixIdeUrl = `${protocol}://${host}` + `${port ? `:${port}` : ''}`
const sharedFolder = path.join(__dirname, '../../')
const sharedFolderService = remixd.services.sharedFolder
let server
// setup HTTP server
if (['localhost', '127.0.0.1', '0.0.0.0'].includes(host)) {
server = httpServer.createServer({
root: path.join(__dirname, '../../node_modules/remix-ide')
})
server.listen(port, '127.0.0.1', function () {
embark.logger.info('Remix IDE (via embark-remix plugin) available at ' + colors.underline(remixIdeUrl))
})
} else {
embark.logger.info('embark-remix is set to connect to a Remix IDE at ' + colors.underline(remixIdeUrl))
}
// setup Embark service check
embark.registerServiceCheck('Remix IDE', (cb) => { embark.registerServiceCheck('Remix IDE', (cb) => {
return cb({name: 'Remix IDE (localhost:8080)', status: 'on'}) return cb({ name: `Remix IDE ${host}:${port}`, status: 'on' })
}) })
server.listen(8080, '127.0.0.1', function () {}) // setup remixd shared folder service
var router = new remixd.Router(65520, remixd.services.sharedFolder, (webSocket) => { const sharedFolderRouter = new remixd.Router(65520, sharedFolderService, { remixIdeUrl }, (webSocket) => {
remixd.services.sharedFolder.setWebSocket(webSocket) sharedFolderService.setWebSocket(webSocket)
var sharedFolder = path.join(__dirname, '/../../') sharedFolderService.setupNotifications(sharedFolder)
remixd.services.sharedFolder.setupNotifications(sharedFolder) sharedFolderService.sharedFolder(sharedFolder, readOnly)
remixd.services.sharedFolder.sharedFolder(sharedFolder)
}) })
router.start() const killRemixD = sharedFolderRouter.start()
const kill = () => {
if (server) server.close()
embark.logger.info(colors.red('embark-remix stopped'))
process.exit()
}
if (process.platform === 'win32') {
require('readline').createInterface({
input: process.stdin,
output: process.stdout
}).on('SIGINT', function () {
process.emit('SIGINT')
})
}
process.on('SIGINT', kill) // catch ctrl-c
process.on('SIGTERM', kill) // catch kill
process.on('exit', killRemixD)
} }

@ -1,6 +1,6 @@
{ {
"name": "embark-remix", "name": "embark-remix",
"version": "0.0.1", "version": "0.0.2",
"description": "load remix IDE from embark environment", "description": "load remix IDE from embark environment",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

@ -27,6 +27,7 @@
"deep-equal": "^1.0.1", "deep-equal": "^1.0.1",
"ethereumjs-util": "^5.1.2", "ethereumjs-util": "^5.1.2",
"events": "^3.0.0", "events": "^3.0.0",
"ethers": "^4.0.27",
"execr": "^1.0.1", "execr": "^1.0.1",
"exorcist": "^0.4.0", "exorcist": "^0.4.0",
"fast-async": "6.3.1", "fast-async": "6.3.1",
@ -43,12 +44,12 @@
"npm-link-local": "^1.1.0", "npm-link-local": "^1.1.0",
"npm-run-all": "^4.0.2", "npm-run-all": "^4.0.2",
"onchange": "^3.2.1", "onchange": "^3.2.1",
"remix-analyzer": "0.3.5", "remix-analyzer": "0.3.6",
"remix-debug": "0.3.5", "remix-debug": "0.3.6",
"remix-lib": "0.4.5", "remix-lib": "0.4.6",
"remix-solidity": "0.3.5", "remix-solidity": "0.3.6",
"remix-tabs": "^1.0.0", "remix-tabs": "^1.0.0",
"remix-tests": "0.1.6", "remix-tests": "0.1.7",
"remixd": "0.1.8-alpha.6", "remixd": "0.1.8-alpha.6",
"request": "^2.83.0", "request": "^2.83.0",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
@ -66,8 +67,8 @@
"yo-yoify": "^3.7.3" "yo-yoify": "^3.7.3"
}, },
"dependencies": { "dependencies": {
"http-server": "^0.11.1",
"remix-plugin": "0.0.2-alpha.6", "remix-plugin": "0.0.2-alpha.6",
"http-server": "^0.11.1",
"remixd": "0.1.8-alpha.6" "remixd": "0.1.8-alpha.6"
}, },
"repository": { "repository": {
@ -180,7 +181,7 @@
"onchange": "onchange build/app.js -- npm-run-all lint", "onchange": "onchange build/app.js -- npm-run-all lint",
"prepublish": "mkdirp build; npm-run-all -ls downloadsolc_root build", "prepublish": "mkdirp build; npm-run-all -ls downloadsolc_root build",
"remixd": "remixd -s ./contracts --remix-ide http://127.0.0.1:8080", "remixd": "remixd -s ./contracts --remix-ide http://127.0.0.1:8080",
"selenium": "execr --silent selenium-standalone start", "selenium": "selenium-standalone start",
"selenium-install": "selenium-standalone install", "selenium-install": "selenium-standalone install",
"serve": "npx http-server .", "serve": "npx http-server .",
"serve_debugger": "npx http-server src/app/debugger/remix-debugger", "serve_debugger": "npx http-server src/app/debugger/remix-debugger",

@ -33,12 +33,6 @@ var TransactionReceiptResolver = require('./transactionReceiptResolver')
const PluginManagerComponent = require('./app/components/plugin-manager-component') const PluginManagerComponent = require('./app/components/plugin-manager-component')
const VerticalIconsComponent = require('./app/components/vertical-icons-component')
const VerticalIconsApi = require('./app/components/vertical-icons-api')
const SwapPanelComponent = require('./app/components/swap-panel-component')
const SwapPanelApi = require('./app/components/swap-panel-api')
const CompileTab = require('./app/tabs/compile-tab') const CompileTab = require('./app/tabs/compile-tab')
const SettingsTab = require('./app/tabs/settings-tab') const SettingsTab = require('./app/tabs/settings-tab')
const AnalysisTab = require('./app/tabs/analysis-tab') const AnalysisTab = require('./app/tabs/analysis-tab')
@ -55,6 +49,10 @@ import framingService from './framingService'
import { MainView } from './app/panels/main-view' import { MainView } from './app/panels/main-view'
import { ThemeModule } from './app/tabs/theme-module' import { ThemeModule } from './app/tabs/theme-module'
import { NetworkModule } from './app/tabs/network-module' import { NetworkModule } from './app/tabs/network-module'
import { SwapPanel } from './app/components/swap-panel'
import { MainPanel } from './app/components/main-panel'
import { HiddenPanel } from './app/components/hidden-panel'
import { VerticalIcons } from './app/components/vertical-icons'
var css = csjs` var css = csjs`
html { box-sizing: border-box; } html { box-sizing: border-box; }
@ -324,8 +322,6 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
const appManager = new RemixAppManager(appStore) const appManager = new RemixAppManager(appStore)
registry.put({api: appManager, name: 'appmanager'}) registry.put({api: appManager, name: 'appmanager'})
const mainPanelComponent = new SwapPanelComponent('mainPanel', appStore, appManager, { default: false, displayHeader: false })
// ----------------- file manager ---------------------------- // ----------------- file manager ----------------------------
self._components.fileManager = new FileManager() self._components.fileManager = new FileManager()
const fileManager = self._components.fileManager const fileManager = self._components.fileManager
@ -339,8 +335,20 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
const themeModule = new ThemeModule(registry) const themeModule = new ThemeModule(registry)
registry.put({api: themeModule, name: 'themeModule'}) registry.put({api: themeModule, name: 'themeModule'})
// ----------------- landing page ----------------------------
// Need to have Home initialized before VerticalIconComponent render to access profile of it for icon
const landingPage = new LandingPage(appManager, appStore)
// ----------------- Vertical Icon ----------------------------
const verticalIcons = new VerticalIcons('swapPanel', appStore, landingPage)
registry.put({api: verticalIcons, name: 'verticalicon'})
const swapPanel = new SwapPanel(appStore)
const mainPanel = new MainPanel(appStore)
const hiddenPanel = new HiddenPanel(appStore)
// ----------------- main view ---------------------- // ----------------- main view ----------------------
self._components.mainview = new MainView(appStore, appManager, mainPanelComponent) self._components.mainview = new MainView(appStore, appManager, mainPanel)
registry.put({ api: self._components.mainview, name: 'mainview' }) registry.put({ api: self._components.mainview, name: 'mainview' })
// ----------------- Renderer ----------------- // ----------------- Renderer -----------------
@ -359,27 +367,18 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// TODOs those are instanciated before hand. should be instanciated on demand // TODOs those are instanciated before hand. should be instanciated on demand
const pluginManagerComponent = new PluginManagerComponent() const pluginManagerComponent = new PluginManagerComponent()
const swapPanelComponent = new SwapPanelComponent('swapPanel', appStore, appManager, { default: true, displayHeader: true })
registry.put({api: appManager.proxy(), name: 'pluginmanager'}) registry.put({api: appManager.proxy(), name: 'pluginmanager'})
pluginManagerComponent.setApp(appManager) pluginManagerComponent.setApp(appManager)
pluginManagerComponent.setStore(appStore) pluginManagerComponent.setStore(appStore)
// Need to have Home initialized before VerticalIconComponent render to access profile of it for icon
const landingPage = new LandingPage(appManager, appStore)
// ----------------- Vertical Icon ----------------------------
const verticalIconsComponent = new VerticalIconsComponent('swapPanel', appStore, landingPage.profile)
const swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconsComponent) // eslint-disable-line
const mainPanelApi = new SwapPanelApi(mainPanelComponent, verticalIconsComponent) // eslint-disable-line
const verticalIconsApi = new VerticalIconsApi(verticalIconsComponent) // eslint-disable-line
registry.put({api: verticalIconsApi, name: 'verticalicon'})
self._components.mainview.init() self._components.mainview.init()
self._components.fileManager.init() self._components.fileManager.init()
self._view.mainpanel.appendChild(self._components.mainview.render()) self._view.mainpanel.appendChild(self._components.mainview.render())
self._view.iconpanel.appendChild(verticalIconsComponent.render()) self._view.iconpanel.appendChild(verticalIcons.render())
self._view.swappanel.appendChild(swapPanelComponent.render()) self._view.swappanel.appendChild(swapPanel.render())
document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body
let filePanel = new FilePanel() let filePanel = new FilePanel()
registry.put({api: filePanel, name: 'filepanel'}) registry.put({api: filePanel, name: 'filepanel'})
@ -423,7 +422,6 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
fileManager.api(), fileManager.api(),
sourceHighlighters.api(), sourceHighlighters.api(),
filePanel.api(), filePanel.api(),
// { profile: support.profile(), api: support },
settings.api(), settings.api(),
pluginManagerComponent.api(), pluginManagerComponent.api(),
networkModule.api(), networkModule.api(),
@ -440,7 +438,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
...appManager.plugins() ...appManager.plugins()
]) ])
framingService.start(appStore, swapPanelApi, verticalIconsApi, mainPanelApi, this._components.resizeFeature) framingService.start(appStore, swapPanel, verticalIcons, mainPanel, this._components.resizeFeature)
// The event listener needs to be registered as early as possible, because the // The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event. // parent will send the message upon the "load" event.
@ -467,7 +465,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
txLogger.event.register('debuggingRequested', (hash) => { txLogger.event.register('debuggingRequested', (hash) => {
if (!appStore.isActive('debugger')) appManager.activateOne('debugger') if (!appStore.isActive('debugger')) appManager.activateOne('debugger')
debug.debugger().debug(hash) debug.debugger().debug(hash)
verticalIconsApi.select('debugger') verticalIcons.select('debugger')
}) })
let transactionContextAPI = { let transactionContextAPI = {

@ -0,0 +1,23 @@
import { AbstractPanel } from './panel'
const csjs = require('csjs-inject')
const yo = require('yo-yo')
const css = csjs`
.pluginsContainer {
display: none;
}
`
export class HiddenPanel extends AbstractPanel {
constructor (appStore) {
super('hiddenPanel', appStore)
}
render () {
return yo`
<div class=${css.pluginsContainer}>
${this.view}
</div>`
}
}

@ -0,0 +1,24 @@
import { AbstractPanel } from './panel'
const yo = require('yo-yo')
const csjs = require('csjs-inject')
const css = csjs`
.pluginsContainer {
height: 100%;
display: flex;
overflow-y: hidden;
}
`
export class MainPanel extends AbstractPanel {
constructor (appStore, options) {
super('mainPanel', appStore, options)
}
render () {
return yo`
<div class=${css.pluginsContainer}>
${this.view}
</div>`
}
}

@ -0,0 +1,111 @@
import { EventEmitter } from 'events'
const registry = require('../../global/registry')
const csjs = require('csjs-inject')
const yo = require('yo-yo')
const css = csjs`
.plugins {
height: 100%;
}
.plugItIn {
display : none;
height : 100%;
}
.plugItIn > div {
overflow-y : auto;
height : 100%;
width : 100%;
}
.plugItIn.active {
display : block;
}
.pluginsContainer {
height: 100%;
overflow-y: hidden;
}
`
// Events are : 'toggle' | 'showing'
/** Abstract class used for hosting the view of a plugin */
export class AbstractPanel {
constructor (panelName, appStore, opts) {
this.events = new EventEmitter()
this.contents = {}
this.active = undefined
// View where the plugin HTMLElement leaves
this.view = yo`<div id="plugins" class="${css.plugins}"></div>`
appStore.event.on('activate', (name) => {
const api = appStore.getOne(name)
const profile = api.profile
if (profile.location !== panelName) return
if (!profile.location && !opts.default) return
if (profile.icon && api.render && typeof api.render === 'function') {
this.add(name, api.render())
}
})
appStore.event.on('deactivate', (name) => {
if (this.contents[name]) this.remove(name)
})
const verticalIcon = registry.get('verticalicon').api
// Toggle content
verticalIcon.events.on('toggleContent', (name) => {
if (!this.contents[name]) return
if (this.active === name) {
this.events.emit('toggle', name)
}
this.showContent(name)
this.events.emit('showing', name)
})
// Force opening
verticalIcon.events.on('showContent', (name) => {
if (!this.contents[name]) return
this.showContent(name)
this.events.emit('showing', name)
})
}
/**
* Add the plugin to the panel
* @param {String} name the name of the plugin
* @param {HTMLElement} content the HTMLContent of the plugin
*/
add (name, content) {
if (this.contents[name]) throw new Error(`Plugin ${name} already rendered`)
content.style.height = '100%'
content.style.width = '100%'
content.style.border = '0'
this.contents[name] = yo`<div class="${css.plugItIn}" >${content}</div>`
this.view.appendChild(this.contents[name])
}
/**
* Remove a plugin from the panel
* @param {String} name The name of the plugin to remove
*/
remove (name) {
const el = this.contents[name]
delete this.contents[name]
if (el) el.parentElement.removeChild(el)
}
/**
* Display the content of this specific plugin
* @param {String} name The name of the plugin to display the content
*/
showContent (name) {
if (!this.contents[name]) throw new Error(`Plugin ${name} is not yet activated`)
// hiding the current view and display the `moduleName`
if (this.active) {
this.contents[this.active].style.display = 'none'
}
this.contents[name].style.display = 'block'
this.active = name
}
}

@ -3,6 +3,7 @@ const csjs = require('csjs-inject')
const EventEmitter = require('events') const EventEmitter = require('events')
const LocalPlugin = require('./local-plugin') const LocalPlugin = require('./local-plugin')
import { Plugin, BaseApi } from 'remix-plugin' import { Plugin, BaseApi } from 'remix-plugin'
import { PluginManagerSettings } from './plugin-manager-settings'
const css = csjs` const css = csjs`
.pluginSearch { .pluginSearch {
@ -152,14 +153,16 @@ class PluginManagerComponent extends BaseApi {
</nav>` </nav>`
: '' : ''
const settings = new PluginManagerSettings().render()
const rootView = yo` const rootView = yo`
<div id='pluginManager'> <div id='pluginManager'>
<div class="form-group ${css.pluginSearch}"> <header class="form-group ${css.pluginSearch}">
<input onkeyup="${e => this.filterPlugins(e)}" class="form-control" placeholder="Search"> <input onkeyup="${e => this.filterPlugins(e)}" class="form-control" placeholder="Search">
<button onclick="${_ => this.openLocalPlugin()}" class="btn btn-sm text-info ${css.localPluginBtn}"> <button onclick="${_ => this.openLocalPlugin()}" class="btn btn-sm text-info ${css.localPluginBtn}">
Connect to a Local Plugin Connect to a Local Plugin
</button> </button>
</div> </header>
<section> <section>
${activeTile} ${activeTile}
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
@ -170,6 +173,7 @@ class PluginManagerComponent extends BaseApi {
${inactives.map(name => this.renderItem(name))} ${inactives.map(name => this.renderItem(name))}
</div> </div>
</section> </section>
${settings}
</div> </div>
` `
if (!this.views.root) this.views.root = rootView if (!this.views.root) this.views.root = rootView

@ -0,0 +1,102 @@
const yo = require('yo-yo')
const csjs = require('csjs-inject')
const modalDialog = require('../ui/modaldialog')
const css = csjs`
.permissions {
position: sticky;
bottom: 0;
display: flex;
justify-content: flex-end;
align-items: center;
padding: 5px 20px;
}
.permissions button {
padding: 2px 5px;
cursor: pointer;
}
.permissionForm h4 {
font-size: 1.3rem;
text-align: center;
}
.permissionForm h6 {
font-size: 1.1rem;
}
.permissionForm hr {
width: 80%;
}
.checkbox {
display: flex;
align-items: center;
}
.checkbox label {
margin: 0;
font-size: 1rem;
}`
export class PluginManagerSettings {
openDialog () {
const fromLocal = window.localStorage.getItem('plugins/permissions')
this.permissions = JSON.parse(fromLocal || '{}')
modalDialog('Plugin Manager Settings', this.settings(),
{ fn: () => this.onValidation() },
)
}
onValidation () {
const permissions = JSON.stringify(this.permissions)
window.localStorage.setItem('plugins/permissions', permissions)
}
settings () {
const permissionByModule = (key, permission) => {
const permissionByPlugin = (name, plugin) => {
function updatePermission () {
plugin.allow = !plugin.allow
}
const checkbox = plugin.allow
? yo`<input onchange="${updatePermission}" type="checkbox" checked id="permission-${name}" aria-describedby="module ${key} ask permission for ${name}" />`
: yo`<input onchange="${updatePermission}" type="checkbox" id="permission-${name}" aria-describedby="module ${key} ask permission for ${name}" />`
return yo`
<div class="form-group ${css.checkbox}">
${checkbox}
<label for="permission-${name}">Allow plugin ${name} to write on ${key}</label>
</div>`
}
const byModule = Object
.keys(permission)
.map(name => permissionByPlugin(name, permission[name]))
return yo`
<div>
<h6>${key} :</h6>
${byModule}
</div>`
}
const permissions = Object
.keys(this.permissions)
.map(key => permissionByModule(key, this.permissions[key]))
const title = permissions.length === 0
? yo`<h4>No Permission requested yet.</h4>`
: yo`<h4>Current Permission settings</h4>`
return yo`<form class="${css.permissionForm}">
${title}
<hr/>
${permissions}
</form>`
}
render () {
return yo`
<footer class="navbar navbar-light bg-light ${css.permissions}">
<button onclick="${() => this.openDialog()}" class="btn btn-info">Settings</button>
</footer>`
}
}

@ -1,43 +0,0 @@
import EventEmmitter from 'events'
class SwapPanelApi {
constructor (swapPanelComponent, verticalIconsComponent) {
this.event = new EventEmmitter()
this.component = swapPanelComponent
this.currentContent
verticalIconsComponent.events.on('toggleContent', (moduleName) => {
if (!swapPanelComponent.contents[moduleName]) return
if (this.currentContent === moduleName) {
this.event.emit('toggle', moduleName)
return
}
this.showContent(moduleName)
this.event.emit('showing', moduleName)
})
verticalIconsComponent.events.on('showContent', (moduleName) => {
if (!swapPanelComponent.contents[moduleName]) return
this.showContent(moduleName)
this.event.emit('showing', moduleName)
})
}
showContent (moduleName) {
this.component.showContent(moduleName)
this.currentContent = moduleName
}
/*
content: DOM element
by appManager
*/
add (profile, content) {
return this.component.add(profile.name, content)
}
remove (profile) {
return this.component.remove(profile.name)
}
}
module.exports = SwapPanelApi

@ -1,102 +0,0 @@
var yo = require('yo-yo')
var csjs = require('csjs-inject')
class SwapPanelComponent {
constructor (name, appStore, appManager, opt) {
this.name = name
this.opt = opt
this.store = appStore
// list of contents
this.contents = {}
// name of the current displayed content
this.currentNode
this.store.event.on('activate', (name) => {
const api = this.store.getOne(name)
const profile = api.profile
if (((profile.location === this.name) || (!profile.location && opt.default)) &&
profile.icon && api.render && typeof api.render === 'function') {
this.add(name, api.render())
}
})
this.store.event.on('deactivate', (name) => {
if (this.contents[name]) this.remove(name)
})
this.store.event.on('add', (api) => { })
this.store.event.on('remove', (api) => { })
}
showContent (moduleName) {
// hiding the current view and display the `moduleName`
if (this.contents[moduleName]) {
if (this.currentNode) {
this.contents[this.currentNode].style.display = 'none'
}
this.contents[moduleName].style.display = 'block'
this.currentNode = moduleName
var api = this.store.getOne(moduleName)
this.header.querySelector('h6').innerHTML = api.profile ? api.profile.displayName : ' - '
return
}
}
add (moduleName, content) {
content.style.height = '100%'
content.style.width = '100%'
content.style.border = '0'
this.contents[moduleName] = yo`<div class=${css.plugItIn} >${content}</div>`
this.view.appendChild(this.contents[moduleName])
}
remove (moduleName) {
let el = this.contents[moduleName]
if (el) el.parentElement.removeChild(el)
}
render () {
this.view = yo`
<div id='plugins' class=${css.plugins}>
</div>
`
this.header = yo`<header class="${css.swapitHeader}"><h6 class="${css.swapitTitle}"></h6></header>`
if (!this.opt.displayHeader) this.header.style.display = 'none'
return yo`<div class=${css.pluginsContainer}>
${this.header}
${this.view}
</div>`
}
}
module.exports = SwapPanelComponent
const css = csjs`
.plugins {
height : 95%;
}
.plugItIn {
display : none;
height : 100%;
}
.plugItIn > div {
overflow-y : auto;
height : 100%;
width : 100%;
}
.plugItIn.active {
display : block;
}
.pluginsContainer {
height: 100%;
overflow-y: hidden;
}
.swapitTitle {
text-transform: uppercase;
}
.swapitHeader {
height: 35px;
padding-top: 10px;
padding-left: 27px;
}
`

@ -0,0 +1,77 @@
import { AbstractPanel } from './panel'
const csjs = require('csjs-inject')
const yo = require('yo-yo')
const css = csjs`
.panel {
height: 100%;
overflow-y: hidden;
}
.swapitTitle {
text-transform: uppercase;
}
.swapitHeader {
height: 35px;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.swapitHeader h6 {
margin: 0;
}
.icons i {
height: 80%;
cursor: pointer;
}
.pluginsContainer {
height: calc(100% - 35px);
overflow: auto;
}
`
const options = {
default: true
}
export class SwapPanel extends AbstractPanel {
constructor (appStore) {
super('swapPanel', appStore, options)
this.header = this.renderHeader()
this.store = appStore
}
/**
* Display content and update the header
* @param {String} name The name of the plugin to display
*/
showContent (name) {
super.showContent(name)
yo.update(this.header, this.renderHeader())
}
/** The header of the swap panel */
renderHeader () {
let name = ' - '
if (this.active) {
const { profile } = this.store.getOne(this.active)
name = profile.displayName ? profile.displayName : profile.name
}
return yo`
<header class="${css.swapitHeader}">
<h6 class="${css.swapitTitle}">${name}</h6>
</div>
</header>`
}
render () {
return yo`
<section class="${css.panel}">
${this.header}
<div class="${css.pluginsContainer}">
${this.view}
</div>
</section>`
}
}

@ -1,20 +0,0 @@
// API
class VerticalIconsApi {
constructor (verticalIconsComponent) {
this.component = verticalIconsComponent
}
addIcon (mod) {
this.component.addIcon(mod)
}
removeIcon (mod) {
this.component.removeIcon(mod)
}
select (moduleName) {
this.component.select(moduleName)
}
}
module.exports = VerticalIconsApi

@ -6,7 +6,7 @@ let globalRegistry = require('../../global/registry')
const EventEmitter = require('events') const EventEmitter = require('events')
// Component // Component
class VerticalIconComponent { export class VerticalIcons {
constructor (name, appStore, homeProfile) { constructor (name, appStore, homeProfile) {
this.store = appStore this.store = appStore
@ -78,7 +78,7 @@ class VerticalIconComponent {
this.icons[name] = yo` this.icons[name] = yo`
<div <div
class="${css.icon}" class="${css.icon}"
onclick="${(e) => { this._iconClick(name) }}" onclick="${() => { this._iconClick(name) }}"
plugin="${name}" title="${title}" > plugin="${name}" title="${title}" >
<img class="image" src="${icon}" alt="${name}" /> <img class="image" src="${icon}" alt="${name}" />
</div>` </div>`
@ -359,8 +359,6 @@ class VerticalIconComponent {
} }
} }
module.exports = VerticalIconComponent
const css = csjs` const css = csjs`
.homeIcon { .homeIcon {
display: block; display: block;

@ -17,7 +17,6 @@ var css = csjs`
justify-content: center; justify-content: center;
} }
.txinput { .txinput {
margin: 3px;
width: inherit; width: inherit;
} }
.txbuttons { .txbuttons {

@ -28,7 +28,7 @@ var css = csjs`
` `
export class MainView { export class MainView {
constructor (appStore, appManager, mainPanelComponent) { constructor (appStore, appManager, mainPanel) {
var self = this var self = this
self.event = new EventManager() self.event = new EventManager()
self._view = {} self._view = {}
@ -38,7 +38,7 @@ export class MainView {
self._components.registry.put({api: self._components.editor, name: 'editor'}) self._components.registry.put({api: self._components.editor, name: 'editor'})
self.appStore = appStore self.appStore = appStore
self.appManager = appManager self.appManager = appManager
self.mainPanelComponent = mainPanelComponent self.mainPanel = mainPanel
} }
init () { init () {
var self = this var self = this
@ -52,7 +52,7 @@ export class MainView {
self.tabProxy = new TabProxy(self._deps.fileManager, self._components.editor, self.appStore, self.appManager) self.tabProxy = new TabProxy(self._deps.fileManager, self._components.editor, self.appStore, self.appManager)
let showApp = function (name) { let showApp = function (name) {
self._deps.fileManager.unselectCurrentFile() self._deps.fileManager.unselectCurrentFile()
self.mainPanelComponent.showContent(name) self.mainPanel.showContent(name)
self._view.editor.style.display = 'none' self._view.editor.style.display = 'none'
self._components.contextView.hide() self._components.contextView.hide()
self._view.mainPanel.style.display = 'block' self._view.mainPanel.style.display = 'block'
@ -178,7 +178,7 @@ export class MainView {
if (self._view.el) return self._view.el if (self._view.el) return self._view.el
self._view.editor = self._components.editor.render() self._view.editor = self._components.editor.render()
self._view.editor.style.display = 'none' self._view.editor.style.display = 'none'
self._view.mainPanel = self.mainPanelComponent.render() self._view.mainPanel = self.mainPanel.render()
self._view.terminal = self._components.terminal.render() self._view.terminal = self._components.terminal.render()
self._view.content = yo` self._view.content = yo`
<div class=${css.content}> <div class=${css.content}>

@ -17,7 +17,6 @@ let css = csjs`
text-decoration: underline; text-decoration: underline;
} }
.homeContainer { .homeContainer {
height: 105%!important; /* @todo should be removed once swap_it and mainview will be separated */
user-select:none; user-select:none;
} }
.jumbotronContainer { .jumbotronContainer {

@ -1,27 +1,26 @@
export default { export default {
start: (appStore, swapPanelApi, verticalIconApi, mainPanelApi, resizeFeature) => { start: (appStore, swapPanel, verticalIcon, mainPanel, resizeFeature) => {
swapPanelApi.event.on('toggle', () => { swapPanel.events.on('toggle', () => {
resizeFeature.panel1.clientWidth !== 0 ? resizeFeature.minimize() : resizeFeature.maximise() resizeFeature.panel1.clientWidth !== 0 ? resizeFeature.minimize() : resizeFeature.maximise()
}) })
swapPanelApi.event.on('showing', () => { swapPanel.events.on('showing', () => {
resizeFeature.panel1.clientWidth === 0 ? resizeFeature.maximise() : '' resizeFeature.panel1.clientWidth === 0 ? resizeFeature.maximise() : ''
}) })
mainPanelApi.event.on('toggle', () => { mainPanel.events.on('toggle', () => {
resizeFeature.maximise() resizeFeature.maximise()
}) })
// mainPanelApi.event.on('showing', (moduleName) => {})
verticalIconApi.select('fileExplorers') verticalIcon.select('fileExplorers')
mainPanelApi.showContent('home') mainPanel.showContent('home')
document.addEventListener('keypress', (e) => { document.addEventListener('keypress', (e) => {
if (e.shiftKey && e.ctrlKey) { if (e.shiftKey && e.ctrlKey) {
if (e.code === 'KeyF') { // Ctrl+Shift+F if (e.code === 'KeyF') { // Ctrl+Shift+F
verticalIconApi.select('fileExplorers') verticalIcon.select('fileExplorers')
} else if (e.code === 'KeyA') { // Ctrl+Shift+A } else if (e.code === 'KeyA') { // Ctrl+Shift+A
verticalIconApi.select('pluginManager') verticalIcon.select('pluginManager')
} else if (e.code === 'KeyS') { // Ctrl+Shift+S } else if (e.code === 'KeyS') { // Ctrl+Shift+S
verticalIconApi.select('settings') verticalIcon.select('settings')
} }
e.preventDefault() e.preventDefault()
} }

@ -4,7 +4,7 @@ const csjs = require('csjs-inject')
const css = csjs` const css = csjs`
.dragbar { .dragbar {
position : absolute; position : absolute;
top : 29px; top : 0px;
width : 0.5em; width : 0.5em;
right : 0; right : 0;
bottom : 0; bottom : 0;

@ -82,13 +82,18 @@ module.exports = class UniversalDApp extends UdappApi {
this.transactionContextAPI = transactionContextAPI this.transactionContextAPI = transactionContextAPI
} }
createVMAccount (privateKey, balance, cb) { /**
return new Promise((resolve, reject) => { * Create a VM Account
if (executionContext.getProvider() !== 'vm') return reject('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed') * @param {{privateKey: string, balance: string}} newAccount The new account to create
this._addAccount(privateKey, balance) */
privateKey = Buffer.from(privateKey, 'hex') createVMAccount (newAccount) {
resolve('0x' + ethJSUtil.privateToAddress(privateKey).toString('hex')) const { privateKey, balance } = newAccount
}) if (executionContext.getProvider() !== 'vm') {
throw new Error('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed')
}
this._addAccount(privateKey, balance)
const privKey = Buffer.from(privateKey, 'hex')
return '0x' + ethJSUtil.privateToAddress(privKey).toString('hex')
} }
newAccount (password, passwordPromptCb, cb) { newAccount (password, passwordPromptCb, cb) {

@ -44,7 +44,10 @@ window.onload = function () {
}) })
document.querySelector('input#testaccountcreation').addEventListener('click', function () { document.querySelector('input#testaccountcreation').addEventListener('click', function () {
extension.call('udapp', 'createVMAccount', ['71975fbf7fe448e004ac7ae54cad0a383c3906055a75468714156a07385e96ce', '0x56BC75E2D63100000'], extension.call('udapp', 'createVMAccount', [{
privateKey: '71975fbf7fe448e004ac7ae54cad0a383c3906055a75468714156a07385e96ce',
balance: '0x56BC75E2D63100000'
}],
function (error, result) { console.log(error, result) }) function (error, result) { console.log(error, result) })
}) })

@ -64,7 +64,10 @@ window.onload = function () {
action: 'request', action: 'request',
key: 'udapp', key: 'udapp',
type: 'createVMAccount', type: 'createVMAccount',
value: ['71975fbf7fe448e004ac7ae54cad0a383c3906055a75468714156a07385e96ce', '0x56BC75E2D63100000'], value: [{
privateKey: '71975fbf7fe448e004ac7ae54cad0a383c3906055a75468714156a07385e96ce',
balance: '0x56BC75E2D63100000'
}],
id: 38 id: 38
}), '*') }), '*')
}) })

@ -122,36 +122,36 @@ function testInputValues (browser, callback) {
}`).pause(500).testFunction('inputValue2 - transact (not payable)', }`).pause(500).testFunction('inputValue2 - transact (not payable)',
'0xd9ec6d8aa73d81755447190f52939ee3084e105b988d445a11e7ac718392ff5a', '0xd9ec6d8aa73d81755447190f52939ee3084e105b988d445a11e7ac718392ff5a',
`[vm]\nfrom:0xca3...a733c\nto:test.inputValue2(uint256[3],bytes8[4]) 0x8c1...401f5\nvalue:0 wei\ndata:0x1b7...00000\nlogs:1\nhash:0xd9e...2ff5a`, `[vm]\nfrom:0xca3...a733c\nto:test.inputValue2(uint256[3],bytes8[4]) 0x8c1...401f5\nvalue:0 wei\ndata:0x1b7...00000\nlogs:1\nhash:0xd9e...2ff5a`,
{types: 'uint256[3] _n, bytes8[4] _b8', values: '[1,2,3], ["0x1234", "0x1234","0x1234","0x1234"]'}, {types: 'uint256[3] _n, bytes8[4] _b8', values: '[1,2,3], ["0x1234000000000000", "0x1234000000000000","0x1234000000000000","0x1234000000000000"]'},
`{ `{
"0": "uint256[3]: _nret 1,2,3", "0": "uint256[3]: _nret 1,2,3",
"1": "bytes8[4]: _b8ret 0x1234000000000000,0x1234000000000000,0x1234000000000000,0x1234000000000000" "1": "bytes8[4]: _b8ret 0x1234000000000000,0x1234000000000000,0x1234000000000000,0x1234000000000000"
}`, `[ }`, `[
{ {
"from": "0x8c1ed7e19abaa9f23c476da86dc1577f1ef401f5", "from": "0x8c1ed7e19abaa9f23c476da86dc1577f1ef401f5",
"topic": "0xd30981760edbf605bda8689e945f622877f230c9a77cbfbd448aa4b7d8ac6e7f", "topic": "0xd30981760edbf605bda8689e945f622877f230c9a77cbfbd448aa4b7d8ac6e7f",
"event": "event1", "event": "event1",
"args": { "args": {
"0": "-123", "0": "-123",
"1": "123", "1": "123",
"2": { "2": {
"indexed": true, "hash": "0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658",
"hash": "0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658" "type": "Indexed"
}, },
"3": "0x12340000", "3": "0x12340000",
"4": "test _ test _ test _ test test _ test test _ test test _ test test _ test test _ test test _ test ", "4": "test _ test _ test _ test test _ test test _ test test _ test test _ test test _ test test _ test ",
"_i": "-123", "_i": "-123",
"_u": "123", "_u": "123",
"_str": { "_str": {
"indexed": true, "hash": "0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658",
"hash": "0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658" "type": "Indexed"
}, },
"_b": "0x12340000", "_b": "0x12340000",
"_notIndexed": "test _ test _ test _ test test _ test test _ test test _ test test _ test test _ test test _ test ", "_notIndexed": "test _ test _ test _ test test _ test test _ test test _ test test _ test test _ test test _ test ",
"length": 5 "length": 5
}
} }
} ]`)
]`)
.click('i[class^="clearinstance"]').perform(() => { callback(null, browser) }) .click('i[class^="clearinstance"]').perform(() => { callback(null, browser) })
}) })
} }

Loading…
Cancel
Save