diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index 48f5484979..169a3a20f8 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -1,3 +1,6 @@ +import React from 'react' // eslint-disable-line +import ReactDOM from 'react-dom' +import { RunTabUI } from '@remix-ui/run-tab' import { ViewPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' @@ -47,6 +50,11 @@ export class RunTab extends ViewPlugin { this.networkModule = networkModule this.fileProvider = fileProvider this.setupEvents() + this.el = document.createElement('div') + } + + onActivation () { + this.renderComponent() } setupEvents () { @@ -104,21 +112,6 @@ export class RunTab extends ViewPlugin { return this.blockchain.pendingTransactionsCount() } - renderContainer () { - this.container = yo`
` - - var el = yo` -
- ${this.settingsUI.render()} - ${this.contractDropdownUI.render()} - ${this.recorderCard.render()} - ${this.instanceContainer} -
- ` - this.container.appendChild(el) - return this.container - } - renderInstanceContainer () { this.instanceContainer = yo`
` @@ -225,6 +218,7 @@ export class RunTab extends ViewPlugin { } render () { + return this.el this.udappUI = new UniversalDAppUI(this.blockchain, this.logCallback) this.blockchain.resetAndInit(this.config, { getAddress: (cb) => { @@ -283,4 +277,10 @@ export class RunTab extends ViewPlugin { this.on('manager', 'pluginDeactivated', removePluginProvider.bind(this)) return this.renderContainer() } + + renderComponent () { + ReactDOM.render( + + , this.el) + } } diff --git a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts index 88cd0ed7a6..b082c408a5 100644 --- a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts +++ b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts @@ -62,3 +62,7 @@ export const getPathIcon = (path: string) => { ? 'fab fa-ethereum' : path.endsWith('.cairo') ? 'fab fa-ethereum' : 'far fa-file' // TODO: add cairo icon } + +export const isNumeric = (value) => { + return /^\+?(0|[1-9]\d*)$/.test(value) +} diff --git a/libs/remix-ui/run-tab/src/index.ts b/libs/remix-ui/run-tab/src/index.ts index 2d18b20958..1964da6480 100644 --- a/libs/remix-ui/run-tab/src/index.ts +++ b/libs/remix-ui/run-tab/src/index.ts @@ -1 +1 @@ -export * from './lib/remix-ui-run-tab'; +export * from './lib/run-tab' diff --git a/libs/remix-ui/run-tab/src/lib/actions/index.ts b/libs/remix-ui/run-tab/src/lib/actions/index.ts new file mode 100644 index 0000000000..85f0ddb18d --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -0,0 +1,8 @@ +let plugin, dispatch: React.Dispatch + +const initSettingsTab = (udapp) => async (reducerDispatch: React.Dispatch) => { + plugin = udapp + dispatch = reducerDispatch + + +} \ No newline at end of file diff --git a/libs/remix-ui/run-tab/src/lib/components/account.tsx b/libs/remix-ui/run-tab/src/lib/components/account.tsx new file mode 100644 index 0000000000..f82cec8c2b --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/components/account.tsx @@ -0,0 +1,149 @@ +// eslint-disable-next-line no-use-before-define +import React, { useRef, useState } from 'react' +import { CopyToClipboard } from '@remix-ui/clipboard' +import { AccountProps } from '../types' + +export function AccountUI (props: AccountProps) { + const [selectedAccount, setSelectedAccount] = useState('') + const plusBtn = useRef(null) + const plusTitle = useRef(null) + + // // TODO: unclear what's the goal of accountListCallId, feels like it can be simplified + // async fillAccountsList () { + // this.accountListCallId++ + // const callid = this.accountListCallId + // const txOrigin = this.el.querySelector('#txorigin') + // let accounts = [] + // try { + // accounts = await this.blockchain.getAccounts() + // } catch (e) { + // addTooltip(`Cannot get account list: ${e}`) + // } + // if (!accounts) accounts = [] + // if (this.accountListCallId > callid) return + // this.accountListCallId++ + // for (const loadedaddress in this.loadedAccounts) { + // if (accounts.indexOf(loadedaddress) === -1) { + // txOrigin.removeChild(txOrigin.querySelector('option[value="' + loadedaddress + '"]')) + // delete this.loadedAccounts[loadedaddress] + // } + // } + // for (const i in accounts) { + // const address = accounts[i] + // if (!this.loadedAccounts[address]) { + // txOrigin.appendChild(yo``) + // this.loadedAccounts[address] = 1 + // } + // } + // txOrigin.setAttribute('value', accounts[0]) + // } + + const updatePlusButton = () => { + // enable/disable + button + switch (props.selectExEnv) { + case 'injected': + plusBtn.current.classList.add('udapp_disableMouseEvents') + plusTitle.current.title = "Unfortunately it's not possible to create an account using injected web3. Please create the account directly from your provider (i.e metamask or other of the same type)." + + break + case 'vm': + plusBtn.current.classList.remove('udapp_disableMouseEvents') + plusTitle.current.title = 'Create a new account' + + break + + case 'web3': + this.onPersonalChange() + + break + default: { + plusBtn.current.classList.add('udapp_disableMouseEvents') + plusTitle.current.title = `Unfortunately it's not possible to create an account using an external wallet (${props.selectExEnv}).` + } + } + } + + const newAccount = () => { + // dispatch createNewBlockchainAccount + // this.blockchain.newAccount( + // '', + // (cb) => { + // modalDialogCustom.promptPassphraseCreation((error, passphrase) => { + // if (error) { + // return modalDialogCustom.alert(error) + // } + // cb(passphrase) + // }, () => {}) + // }, + // (error, address) => { + // if (error) { + // return addTooltip('Cannot create an account: ' + error) + // } + // addTooltip(`account ${address} created`) + // } + // ) + } + + const signMessage = () => { + // dispatch signMessageWithBlockchainAccounts + // this.blockchain.getAccounts((err, accounts) => { + // if (err) { + // return addTooltip(`Cannot get account list: ${err}`) + // } + + // var signMessageDialog = { title: 'Sign a message', text: 'Enter a message to sign', inputvalue: 'Message to sign' } + // var $txOrigin = this.el.querySelector('#txorigin') + // if (!$txOrigin.selectedOptions[0] && (this.blockchain.isInjectedWeb3() || this.blockchain.isWeb3Provider())) { + // return addTooltip('Account list is empty, please make sure the current provider is properly connected to remix') + // } + + // var account = $txOrigin.selectedOptions[0].value + + // var promptCb = (passphrase) => { + // const modal = modalDialogCustom.promptMulti(signMessageDialog, (message) => { + // this.blockchain.signMessage(message, account, passphrase, (err, msgHash, signedData) => { + // if (err) { + // return addTooltip(err) + // } + // modal.hide() + // modalDialogCustom.alert(yo` + //
+ // hash:
+ // ${msgHash} + //
signature:
+ // ${signedData} + //
+ // `) + // }) + // }, false) + // } + + // if (this.blockchain.isWeb3Provider()) { + // return modalDialogCustom.promptPassphrase( + // 'Passphrase to sign a message', + // 'Enter your passphrase for this account to sign the message', + // '', + // promptCb, + // false + // ) + // } + // promptCb() + // }) + } + + return ( +
+ +
+ +
+ +
+
+ ) +} diff --git a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx new file mode 100644 index 0000000000..0ec4f1c0b2 --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -0,0 +1,418 @@ +// eslint-disable-next-line no-use-before-define +import React, { SyntheticEvent, useEffect, useRef, useState } from 'react' +import { ContractDropdownProps } from '../types' +import * as ethJSUtil from 'ethereumjs-util' + +export function ContractDropdownUI (props: ContractDropdownProps) { + const [networkName, setNetworkName] = useState('') + const [abiLabel, setAbiLabel] = useState<{ + display: string, + content: string + }>({ + display: '', + content: '' + }) + const [ipfsCheckedState, setIpfsCheckedState] = useState(false) + const [loadType] = useState('other') + const atAddressButtonInput = useRef(null) + const contracts = useRef(null) + + useEffect(() => { + enableAtAddress(false) + const savedConfig = window.localStorage.getItem(`ipfs/${props.exEnvironment}/${networkName}`) + const isCheckedIPFS = savedConfig === 'true' ? true : false // eslint-disable-line + + if (isCheckedIPFS) setIpfsCheckedState(true) + setAbiLabel({ + display: 'none', + content: 'ABI file selected' + }) + }, []) + + useEffect(() => { + if (props.exEnvironment === 'vm') setNetworkName('VM') + }, [props.exEnvironment]) + + const enableAtAddress = (enable) => { + const atAddress = atAddressButtonInput.current + + if (enable) { + if (!atAddress.value || !ethJSUtil.isValidAddress(atAddress.value)) { + enableAtAddress(false) + return + } + atAddress.removeAttribute('disabled') + atAddress.setAttribute('title', 'Interact with the given contract.') + } else { + atAddress.setAttribute('disabled', true) + if (atAddress.value === '') { + atAddress.setAttribute('title', '⚠ Compile *.sol file or select *.abi file & then enter the address of deployed contract.') + } else { + atAddress.setAttribute('title', '⚠ Compile *.sol file or select *.abi file.') + } + } + } + // constructor (blockchain, dropdownLogic, logCallback, runView) { + // this.blockchain = blockchain + // this.dropdownLogic = dropdownLogic + // this.logCallback = logCallback + // this.runView = runView + // this.event = new EventManager() + + // this.listenToEvents() + // this.ipfsCheckedState = false + // this.exEnvironment = blockchain.getProvider() + // this.listenToContextChange() + // this.loadType = 'other' + // } + + // listenToEvents () { + // this.dropdownLogic.event.register('newlyCompiled', (success, data, source, compiler, compilerFullName, file) => { + // if (!this.selectContractNames) return + // this.selectContractNames.innerHTML = '' + // if (success) { + // this.dropdownLogic.getCompiledContracts(compiler, compilerFullName).forEach((contract) => { + // this.selectContractNames.appendChild(yo``) + // }) + // } + // this.enableAtAddress(success) + // this.enableContractNames(success) + // this.setInputParamsPlaceHolder() + + // if (success) { + // this.compFails.style.display = 'none' + // } else { + // this.compFails.style.display = 'block' + // } + // }) + // } + + // listenToContextChange () { + // this.blockchain.event.register('networkStatus', ({ error, network }) => { + // if (error) { + // console.log('can\'t detect network') + // return + // } + // this.exEnvironment = this.blockchain.getProvider() + // this.networkName = network.name + + // const savedConfig = window.localStorage.getItem(`ipfs/${this.exEnvironment}/${this.networkName}`) + + // // check if an already selected option exist else use default workflow + // if (savedConfig !== null) { + // this.setCheckedState(savedConfig) + // } else { + // this.setCheckedState(this.networkName === 'Main') + // } + // }) + // } + + // setCheckedState (value) { + // value = value === 'true' ? true : value === 'false' ? false : value + // this.ipfsCheckedState = value + // if (this.ipfsCheckbox) this.ipfsCheckbox.checked = value + // } + + // enableContractNames (enable) { + // if (enable) { + // if (this.selectContractNames.value === '') return + // this.selectContractNames.removeAttribute('disabled') + // this.selectContractNames.setAttribute('title', 'Select contract for Deploy or At Address.') + // } else { + // this.selectContractNames.setAttribute('disabled', true) + // if (this.loadType === 'sol') { + // this.selectContractNames.setAttribute('title', '⚠ Select and compile *.sol file to deploy or access a contract.') + // } else { + // this.selectContractNames.setAttribute('title', '⚠ Selected *.abi file allows accessing contracts, select and compile *.sol file to deploy and access one.') + // } + // } + // } + + // changeCurrentFile (currentFile) { + // if (!this.selectContractNames) return + // if (/.(.abi)$/.exec(currentFile)) { + // this.createPanel.style.display = 'none' + // this.orLabel.style.display = 'none' + // this.compFails.style.display = 'none' + // this.loadType = 'abi' + // this.contractNamesContainer.style.display = 'block' + // this.abiLabel.style.display = 'block' + // this.abiLabel.innerHTML = currentFile + // this.selectContractNames.style.display = 'none' + // this.enableContractNames(true) + // this.enableAtAddress(true) + // } else if (/.(.sol)$/.exec(currentFile) || + // /.(.vy)$/.exec(currentFile) || // vyper + // /.(.lex)$/.exec(currentFile) || // lexon + // /.(.contract)$/.exec(currentFile)) { + // this.createPanel.style.display = 'block' + // this.orLabel.style.display = 'block' + // this.contractNamesContainer.style.display = 'block' + // this.loadType = 'sol' + // this.selectContractNames.style.display = 'block' + // this.abiLabel.style.display = 'none' + // if (this.selectContractNames.value === '') this.enableAtAddress(false) + // } else { + // this.loadType = 'other' + // this.createPanel.style.display = 'block' + // this.orLabel.style.display = 'block' + // this.contractNamesContainer.style.display = 'block' + // this.selectContractNames.style.display = 'block' + // this.abiLabel.style.display = 'none' + // if (this.selectContractNames.value === '') this.enableAtAddress(false) + // } + // } + + // setInputParamsPlaceHolder () { + // this.createPanel.innerHTML = '' + // if (this.selectContractNames.selectedIndex < 0 || this.selectContractNames.children.length <= 0) { + // this.createPanel.innerHTML = 'No compiled contracts' + // return + // } + + // const selectedContract = this.getSelectedContract() + // const clickCallback = async (valArray, inputsValues) => { + // var selectedContract = this.getSelectedContract() + // this.createInstance(selectedContract, inputsValues) + // } + // const createConstructorInstance = new MultiParamManager( + // 0, + // selectedContract.getConstructorInterface(), + // clickCallback, + // selectedContract.getConstructorInputs(), + // 'Deploy', + // selectedContract.bytecodeObject, + // true + // ) + // this.createPanel.appendChild(createConstructorInstance.render()) + // this.createPanel.appendChild(this.deployCheckBox) + // } + + // getSelectedContract () { + // var contract = this.selectContractNames.children[this.selectContractNames.selectedIndex] + // var contractName = contract.getAttribute('value') + // var compilerAtributeName = contract.getAttribute('compiler') + + // return this.dropdownLogic.getSelectedContract(contractName, compilerAtributeName) + // } + + // async createInstance (selectedContract, args) { + // if (selectedContract.bytecodeObject.length === 0) { + // return modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') + // } + + // var continueCb = (error, continueTxExecution, cancelCb) => { + // if (error) { + // var msg = typeof error !== 'string' ? error.message : error + // modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). + // The transaction execution will likely fail. Do you want to force sending?
+ // ${msg} + //
`, + // { + // label: 'Send Transaction', + // fn: () => { + // continueTxExecution() + // } + // }, { + // label: 'Cancel Transaction', + // fn: () => { + // cancelCb() + // } + // }) + // } else { + // continueTxExecution() + // } + // } + + // const self = this + + // var promptCb = (okCb, cancelCb) => { + // modalDialogCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) + // } + + // var statusCb = (msg) => { + // return this.logCallback(msg) + // } + + // var finalCb = (error, contractObject, address) => { + // self.event.trigger('clearInstance') + + // if (error) { + // return this.logCallback(error) + // } + // self.event.trigger('newContractInstanceAdded', [contractObject, address, contractObject.name]) + + // const data = self.runView.compilersArtefacts.getCompilerAbstract(contractObject.contract.file) + // self.runView.compilersArtefacts.addResolvedContract(helper.addressToString(address), data) + // if (self.ipfsCheckedState) { + // _paq.push(['trackEvent', 'udapp', 'DeployAndPublish', this.networkName]) + // publishToStorage('ipfs', self.runView.fileProvider, self.runView.fileManager, selectedContract) + // } else { + // _paq.push(['trackEvent', 'udapp', 'DeployOnly', this.networkName]) + // } + // } + + // let contractMetadata + // try { + // contractMetadata = await this.runView.call('compilerMetadata', 'deployMetadataOf', selectedContract.name, selectedContract.contract.file) + // } catch (error) { + // return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) + // } + + // const compilerContracts = this.dropdownLogic.getCompilerContracts() + // const confirmationCb = this.getConfirmationCb(modalDialog, confirmDialog) + + // if (selectedContract.isOverSizeLimit()) { + // return modalDialog('Contract code size over limit', yo`
Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails.
+ // More info: eip-170 + //
`, + // { + // label: 'Force Send', + // fn: () => { + // this.deployContract(selectedContract, args, contractMetadata, compilerContracts, { continueCb, promptCb, statusCb, finalCb }, confirmationCb) + // } + // }, { + // label: 'Cancel', + // fn: () => { + // this.logCallback(`creation of ${selectedContract.name} canceled by user.`) + // } + // }) + // } + // this.deployContract(selectedContract, args, contractMetadata, compilerContracts, { continueCb, promptCb, statusCb, finalCb }, confirmationCb) + // } + + // deployContract (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { + // _paq.push(['trackEvent', 'udapp', 'DeployContractTo', this.networkName]) + // const { statusCb } = callbacks + // if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { + // return this.blockchain.deployContractAndLibraries(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) + // } + // if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) + // this.blockchain.deployContractWithLibrary(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) + // } + + // getConfirmationCb (modalDialog, confirmDialog) { + // // this code is the same as in recorder.js. TODO need to be refactored out + // const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + // if (network.name !== 'Main') { + // return continueTxExecution(null) + // } + // const amount = this.blockchain.fromWei(tx.value, true, 'ether') + // const content = confirmDialog(tx, network, amount, gasEstimation, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain)) + + // modalDialog('Confirm transaction', content, + // { + // label: 'Confirm', + // fn: () => { + // this.blockchain.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + // // TODO: check if this is check is still valid given the refactor + // if (!content.gasPriceStatus) { + // cancelCb('Given transaction fee is not correct') + // } else { + // continueTxExecution(content.txFee) + // } + // } + // }, { + // label: 'Cancel', + // fn: () => { + // return cancelCb('Transaction canceled by user.') + // } + // } + // ) + // } + + // return confirmationCb + // } + + const atAddressChanged = (event: SyntheticEvent) => { + const atAddress = atAddressButtonInput.current + const selectContractNames = contracts.current + + if (!atAddress.value) { + enableAtAddress(false) + } else { + if ((selectContractNames && !selectContractNames.getAttribute('disabled') && loadType === 'sol') || + loadType === 'abi') { + enableAtAddress(true) + } else { + enableAtAddress(false) + } + } + } + + const loadFromAddress = () => { + // trigger dispatchLoadAddress + // this.event.trigger('clearInstance') + + // let address = this.atAddressButtonInput.value + // if (!ethJSUtil.isValidChecksumAddress(address)) { + // addTooltip(yo` + // + // It seems you are not using a checksumed address. + //
A checksummed address is an address that contains uppercase letters, as specified in EIP-55. + //
Checksummed addresses are meant to help prevent users from sending transactions to the wrong address. + //
`) + // address = ethJSUtil.toChecksumAddress(address) + // } + // this.dropdownLogic.loadContractFromAddress(address, + // (cb) => { + // modalDialogCustom.confirm('At Address', `Do you really want to interact with ${address} using the current ABI definition?`, cb) + // }, + // (error, loadType, abi) => { + // if (error) { + // return modalDialogCustom.alert(error) + // } + // if (loadType === 'abi') { + // return this.event.trigger('newContractABIAdded', [abi, address]) + // } + // var selectedContract = this.getSelectedContract() + // this.event.trigger('newContractInstanceAdded', [selectedContract.object, address, this.selectContractNames.value]) + // } + // ) + } + + const handleCheckedIPFS = () => { + setIpfsCheckedState(!ipfsCheckedState) + window.localStorage.setItem(`ipfs/${props.exEnvironment}/${networkName}`, ipfsCheckedState.toString()) + } + + return ( +
+ +
+ + + { abiLabel.content } +
+
+
+
+ + +
+
+
or
+
+ + +
+
+
+ // this.selectContractNames.addEventListener('change', this.setInputParamsPlaceHolder.bind(this)) + // this.setInputParamsPlaceHolder() + ) +} diff --git a/libs/remix-ui/run-tab/src/lib/components/environment.tsx b/libs/remix-ui/run-tab/src/lib/components/environment.tsx new file mode 100644 index 0000000000..84685ef58f --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/components/environment.tsx @@ -0,0 +1,76 @@ +// eslint-disable-next-line no-use-before-define +import React, { useState } from 'react' +import { EnvironmentProps } from '../types' + +export function EnvironmentUI (props: EnvironmentProps) { + const [exEnv, setExEnv] = useState('') + + // setDropdown (selectExEnv) { + // this.selectExEnv = selectExEnv + + // const addProvider = (network) => { + // selectExEnv.appendChild(yo``) + // addTooltip(yo`${network.name} provider added`) + // } + + // const removeProvider = (name) => { + // var env = selectExEnv.querySelector(`option[value="${name}"]`) + // if (env) { + // selectExEnv.removeChild(env) + // addTooltip(yo`${name} provider removed`) + // } + // } + // this.blockchain.event.register('addProvider', provider => addProvider(provider)) + // this.blockchain.event.register('removeProvider', name => removeProvider(name)) + + // selectExEnv.addEventListener('change', (event) => { + // const provider = selectExEnv.options[selectExEnv.selectedIndex] + // const fork = provider.getAttribute('fork') // can be undefined if connected to an external source (web3 provider / injected) + // let context = provider.value + // context = context.startsWith('vm') ? 'vm' : context // context has to be 'vm', 'web3' or 'injected' + // this.setExecutionContext({ context, fork }) + // }) + + // selectExEnv.value = this._getProviderDropdownValue() + // } + + const handleChangeExEnv = (env: string) => { + setExEnv(env) + props.updateExEnv(env) + } + return ( +
+ +
+ + +
+
+ ) +} diff --git a/libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx b/libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx new file mode 100644 index 0000000000..f357659971 --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx @@ -0,0 +1,12 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' +import { GasPriceProps } from '../types' + +export function GasPriceUI (props: GasPriceProps) { + return ( +
+ + +
+ ) +} diff --git a/libs/remix-ui/run-tab/src/lib/components/network.tsx b/libs/remix-ui/run-tab/src/lib/components/network.tsx new file mode 100644 index 0000000000..f33e568e19 --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/components/network.tsx @@ -0,0 +1,15 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' +import { NetworkProps } from '../types' + +export function NetworkUI (props: NetworkProps) { + return ( +
+
+
+
+ +
+
+ ) +} diff --git a/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx b/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx new file mode 100644 index 0000000000..2acaa0febf --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx @@ -0,0 +1,156 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' +import { SettingsProps } from '../types' +import { EnvironmentUI } from './environment' +import { NetworkUI } from './network' +import { AccountUI } from './account' +import { GasPriceUI } from './gasPrice' +import { ValueUI } from './value' + +export function SettingsUI (props: SettingsProps) { + // constructor () { + // this.blockchain = blockchain + // this.event = new EventManager() + // this._components = {} + + // this.blockchain.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { + // if (!lookupOnly) this.el.querySelector('#value').value = 0 + // if (error) return + // this.updateAccountBalances() + // }) + // this._components = { + // registry: globalRegistry, + // networkModule: networkModule + // } + // this._components.registry = globalRegistry + // this._deps = { + // config: this._components.registry.get('config').api + // } + + // this._deps.config.events.on('settings/personal-mode_changed', this.onPersonalChange.bind(this)) + + // setInterval(() => { + // this.updateAccountBalances() + // }, 1000) + + // this.accountListCallId = 0 + // this.loadedAccounts = {} + // } + + // updateAccountBalances () { + // if (!this.el) return + // var accounts = $(this.el.querySelector('#txorigin')).children('option') + // accounts.each((index, account) => { + // this.blockchain.getBalanceInEther(account.value, (err, balance) => { + // if (err) return + // const updated = helper.shortenAddress(account.value, balance) + // if (updated !== account.innerText) { // check if the balance has been updated and update UI accordingly. + // account.innerText = updated + // } + // }) + // }) + // } + + // setExecutionContext (context) { + // this.blockchain.changeExecutionContext(context, () => { + // modalDialogCustom.prompt('External node request', this.web3ProviderDialogBody(), 'http://127.0.0.1:8545', (target) => { + // this.blockchain.setProviderFromEndpoint(target, context, (alertMsg) => { + // if (alertMsg) addTooltip(alertMsg) + // this.setFinalContext() + // }) + // }, this.setFinalContext.bind(this)) + // }, (alertMsg) => { + // addTooltip(alertMsg) + // }, this.setFinalContext.bind(this)) + // } + + // web3ProviderDialogBody () { + // const thePath = '' + + // return yo` + //
+ // Note: To use Geth & https://remix.ethereum.org, configure it to allow requests from Remix:(see Geth Docs on rpc server) + //
geth --http --http.corsdomain https://remix.ethereum.org
+ //
+ // To run Remix & a local Geth test node, use this command: (see Geth Docs on Dev mode) + //
geth --http --http.corsdomain="${window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir ${thePath} --dev console
+ //
+ //
+ // WARNING: It is not safe to use the --http.corsdomain flag with a wildcard: --http.corsdomain * + //
+ //
For more info: Remix Docs on Web3 Provider + //
+ //
+ // Web3 Provider Endpoint + //
+ // ` + // } + + // /** + // * generate a value used by the env dropdown list. + // * @return {String} - can return 'vm-berlin, 'vm-london', 'injected' or 'web3' + // */ + // _getProviderDropdownValue () { + // const provider = this.blockchain.getProvider() + // const fork = this.blockchain.getCurrentFork() + // return provider === 'vm' ? provider + '-' + fork : provider + // } + + // setFinalContext () { + // // set the final context. Cause it is possible that this is not the one we've originaly selected + // this.selectExEnv.value = this._getProviderDropdownValue() + // this.event.trigger('clearInstance', []) + // this.updatePlusButton() + // } + + // onPersonalChange () { + // const plusBtn = document.getElementById('remixRunPlus') + // const plusTitle = document.getElementById('remixRunPlusWraper') + // if (!this._deps.config.get('settings/personal-mode')) { + // plusBtn.classList.add(css.disableMouseEvents) + // plusTitle.title = 'Creating an account is possible only in Personal mode. Please go to Settings to enable it.' + // } else { + // plusBtn.classList.remove(css.disableMouseEvents) + // plusTitle.title = 'Create a new account' + // } + // } + + // getSelectedAccount () { + // return this.el.querySelector('#txorigin').selectedOptions[0].value + // } + + // getEnvironment () { + // return this.blockchain.getProvider() + // } + + return ( + // this.blockchain.event.register('contextChanged', (context, silent) => { + // this.setFinalContext() + // }) + + // this.blockchain.event.register('networkStatus', ({ error, network }) => { + // if (error) { + // this.netUI.innerHTML = 'can\'t detect network ' + // return + // } + // const networkProvider = this._components.networkModule.getNetworkProvider.bind(this._components.networkModule) + // this.netUI.innerHTML = (networkProvider() !== 'vm') ? `${network.name} (${network.id || '-'}) network` : '' + // }) + + // setInterval(() => { + // this.fillAccountsList() + // }, 1000) + + // this.el = el + + // this.fillAccountsList() + // return el +
+ + + + + +
+ ) +} diff --git a/libs/remix-ui/run-tab/src/lib/components/value.tsx b/libs/remix-ui/run-tab/src/lib/components/value.tsx new file mode 100644 index 0000000000..205e4137f8 --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/components/value.tsx @@ -0,0 +1,69 @@ +// eslint-disable-next-line no-use-before-define +import React, { useRef } from 'react' +import { BN } from 'ethereumjs-util' +import { isNumeric } from '@remix-ui/helper' +import { ValueProps } from '../types' + +export function ValueUI (props: ValueProps) { + const inputValue = useRef(null) + + const validateInputKey = (e) => { + // preventing not numeric keys + // preventing 000 case + if (!isNumeric(e.key) || + (e.key === '0' && !parseInt(inputValue.current.value) && inputValue.current.value.length > 0)) { + e.preventDefault() + e.stopImmediatePropagation() + } + } + + const validateValue = () => { + if (!inputValue.current.value) { + // assign 0 if given value is + // - empty + inputValue.current.value = 0 + return + } + + let v + try { + v = new BN(inputValue.current.value, 10) + inputValue.current.value = v.toString(10) + } catch (e) { + // assign 0 if given value is + // - not valid (for ex 4345-54) + // - contains only '0's (for ex 0000) copy past or edit + inputValue.current.value = 0 + } + + // if giveen value is negative(possible with copy-pasting) set to 0 + if (v.lt(0)) inputValue.current.value = 0 + } + + return ( +
+ +
+ + +
+
+ ) +} diff --git a/libs/remix-ui/run-tab/src/lib/css/run-tab.css b/libs/remix-ui/run-tab/src/lib/css/run-tab.css new file mode 100644 index 0000000000..a6ec9c23d3 --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/css/run-tab.css @@ -0,0 +1,219 @@ +.udapp_runTabView { + display: flex; + flex-direction: column; +} +.udapp_runTabView::-webkit-scrollbar { + display: none; +} +.udapp_settings { + padding: 0 24px 16px; +} +.udapp_crow { + display: block; + margin-top: 8px; +} +.udapp_col1 { + width: 30%; + float: left; + align-self: center; +} +.udapp_settingsLabel { + font-size: 11px; + margin-bottom: 4px; + text-transform: uppercase; +} +.udapp_environment { + display: flex; + align-items: center; + position: relative; + width: 100%; +} +.udapp_environment a { + margin-left: 7px; +} +.udapp_account { + display: flex; + align-items: center; +} +.udapp_account i { + margin-left: 12px; +} +.udapp_col2 { + border-radius: 3px; +} +.udapp_col2_1 { + width: 164px; + min-width: 164px; +} +.udapp_col2_2 { +} +.udapp_select { + font-weight: normal; + width: 100%; + overflow: hidden; +} +.udapp_instanceContainer { + display: flex; + flex-direction: column; + margin-bottom: 2%; + border: none; + text-align: center; + padding: 0 14px 16px; +} +.udapp_pendingTxsContainer { + display: flex; + flex-direction: column; + margin-top: 2%; + border: none; + text-align: center; +} +.udapp_container { + padding: 0 24px 16px; +} +.udapp_recorderDescription { + margin: 0 15px 15px 0; + } +.udapp_contractNames { + width: 100%; + border: 1px solid +} +.udapp_subcontainer { + display: flex; + flex-direction: row; + align-items: center; + margin-bottom: 8px; +} +.udapp_subcontainer i { + width: 16px; + display: flex; + justify-content: center; + margin-left: 1px; +} +.udapp_button button{ + flex: none; +} +.udapp_button { + display: flex; + align-items: center; + margin-top: 13px; +} +.udapp_transaction { +} +.udapp_atAddress { + margin: 0; + min-width: 100px; + width: 100px; + height: 100%; + word-break: inherit; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: 0; +} +.udapp_atAddressSect { + margin-top: 8px; + height: 32px; +} +.udapp_atAddressSect input { + height: 32px; + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; +} +.udapp_ataddressinput { + padding: .25rem; +} +.udapp_create { +} +.udapp_input { + font-size: 10px !important; +} +.udapp_noInstancesText { + font-style: italic; + text-align: left; + padding-left: 15px; +} +.udapp_pendingTxsText { + font-style: italic; + display: flex; + justify-content: space-evenly; + align-items: center; + flex-wrap: wrap; +} +.udapp_item { + margin-right: 1em; + display: flex; + align-items: center; +} +.udapp_pendingContainer { + display: flex; + align-items: baseline; +} +.udapp_pending { + height: 25px; + text-align: center; + padding-left: 10px; + border-radius: 3px; + margin-left: 5px; +} +.udapp_disableMouseEvents { + pointer-events: none; +} +.udapp_icon { + cursor: pointer; + font-size: 12px; + cursor: pointer; + margin-left: 5px; +} +.udapp_icon:hover { + font-size: 12px; + color: var(--warning); +} +.udapp_errorIcon { + color: var(--warning); + margin-left: 15px; +} +.udapp_failDesc { + color: var(--warning); + padding-left: 10px; + display: inline; +} +.udapp_network { + margin-left: 8px; + pointer-events: none; +} +.udapp_networkItem { + margin-right: 5px; +} +.udapp_transactionActions { + display: flex; + justify-content: space-evenly; + width: 145px; +} +.udapp_orLabel { + text-align: center; + text-transform: uppercase; +} +.udapp_infoDeployAction { + margin-left: 1px; + font-size: 13px; + color: var(--info); +} +.udapp_gasValueContainer { + flex-direction: row; + display: flex; +} +.udapp_gasNval { + width: 55%; + font-size: 0.8rem; +} +.udapp_gasNvalUnit { + width: 41%; + margin-left: 10px; + font-size: 0.8rem; +} +.udapp_deployDropdown { + text-align: center; + text-transform: uppercase; +} +.udapp_checkboxAlign { + padding-top: 2px; +} diff --git a/libs/remix-ui/run-tab/src/lib/remix-ui-run-tab.module.css b/libs/remix-ui/run-tab/src/lib/remix-ui-run-tab.module.css deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libs/remix-ui/run-tab/src/lib/remix-ui-run-tab.tsx b/libs/remix-ui/run-tab/src/lib/remix-ui-run-tab.tsx deleted file mode 100644 index fdac73c72d..0000000000 --- a/libs/remix-ui/run-tab/src/lib/remix-ui-run-tab.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import './remix-ui-run-tab.module.css'; - -/* eslint-disable-next-line */ -export interface RemixUiRunTabProps {} - -export function RemixUiRunTab(props: RemixUiRunTabProps) { - return ( -
-

Welcome to remix-ui-run-tab!

-
- ); -} - -export default RemixUiRunTab; diff --git a/libs/remix-ui/run-tab/src/lib/run-tab.tsx b/libs/remix-ui/run-tab/src/lib/run-tab.tsx new file mode 100644 index 0000000000..7884813a81 --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -0,0 +1,25 @@ +// eslint-disable-next-line no-use-before-define +import React, { useState } from 'react' +import { ContractDropdownUI } from './components/contractDropdownUI' +import { SettingsUI } from './components/settingsUI' +import './css/run-tab.css' +import { RunTabProps } from './types' + +export function RunTabUI (props: RunTabProps) { + const [selectExEnv, setSelectExEnv] = useState('') + + const updateExEnv = (env: string) => { + setSelectExEnv(env) + } + + return ( +
+
+ + + {/* ${this.recorderCard.render()} + ${this.instanceContainer} */} +
+
+ ) +} diff --git a/libs/remix-ui/run-tab/src/lib/types/index.ts b/libs/remix-ui/run-tab/src/lib/types/index.ts new file mode 100644 index 0000000000..c5172863a2 --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/types/index.ts @@ -0,0 +1,32 @@ +export interface RunTabProps { + plugin: any +} + +export interface SettingsProps { + selectExEnv: string, + updateExEnv: (env: string) => void +} + +export interface EnvironmentProps { + updateExEnv: (env: string) => void +} + +export interface NetworkProps { + +} + +export interface AccountProps { + selectExEnv: string +} + +export interface GasPriceProps { + +} + +export interface ValueProps { + +} + +export interface ContractDropdownProps { + exEnvironment: string +} diff --git a/libs/remix-ui/run-tab/tsconfig.json b/libs/remix-ui/run-tab/tsconfig.json index 8bd701c578..d52e31ad74 100644 --- a/libs/remix-ui/run-tab/tsconfig.json +++ b/libs/remix-ui/run-tab/tsconfig.json @@ -1,14 +1,10 @@ { "extends": "../../../tsconfig.base.json", "compilerOptions": { - "jsx": "react-jsx", + "jsx": "react", "allowJs": true, "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true + "allowSyntheticDefaultImports": true }, "files": [], "include": [],