Merge pull request #1883 from ethereum/remixdreact

remixd handle in react
remixdreact
David Disu 3 years ago committed by GitHub
commit a48d8c2d3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      apps/remix-ide-e2e/src/tests/importFromGithub.test.ts
  2. 4
      apps/remix-ide-e2e/src/tests/remixd.test.ts
  3. 2
      apps/remix-ide/src/app/panels/file-panel.js
  4. 143
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  5. 1
      apps/remix-ide/tsconfig.json
  6. 1
      libs/remix-ui/app/src/index.ts
  7. 1
      libs/remix-ui/app/src/lib/remix-app/components/modals/dialogViewPlugin.tsx
  8. 4
      libs/remix-ui/app/src/lib/remix-app/context/provider.tsx
  9. 1
      libs/remix-ui/app/src/lib/remix-app/interface/index.ts
  10. 6
      libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts
  11. 14
      libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx

@ -31,8 +31,6 @@ module.exports = {
'Display Error Message For Invalid GitHub URL Modal': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('settings')
.clickLaunchIcon('filePanel')
.scrollAndClick('*[data-id="landingPageImportFromGitHubButton"]')
.waitForElementVisible('input[data-id="homeTabModalDialogCustomPromptText"]')
.execute(() => {
@ -48,8 +46,6 @@ module.exports = {
'Import From Github For Valid URL': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('settings')
.clickLaunchIcon('filePanel')
.scrollAndClick('*[data-id="landingPageImportFromGitHubButton"]')
.waitForElementVisible('*[data-id="homeTabModalDialogCustomPromptText"]')
.clearValue('*[data-id="homeTabModalDialogCustomPromptText"]')

@ -125,9 +125,9 @@ function startRemixd (browser: NightwatchBrowser) {
.clickLaunchIcon('filePanel')
.clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager *[data-id="pluginManagerComponentActivateButtonremixd"]')
.waitForElementVisible('#modal-footer-ok', 2000)
.waitForElementVisible('*[data-id="remixdConnect-modal-footer-ok-react"]', 2000)
.pause(2000)
.click('#modal-footer-ok')
.click('*[data-id="remixdConnect-modal-footer-ok-react"]')
// .click('*[data-id="workspacesModalDialog-modal-footer-ok-react"]')
}

@ -5,7 +5,7 @@ import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { FileSystemProvider } from '@remix-ui/workspace' // eslint-disable-line
import Registry from '../state/registry'
const { RemixdHandle } = require('../files/remixd-handle.js')
import { RemixdHandle } from '../plugins/remixd-handle'
const { GitHandle } = require('../files/git-handle.js')
const { HardhatHandle } = require('../files/hardhat-handle.js')
const { SlitherHandle } = require('../files/slither-handle.js')

@ -1,24 +1,13 @@
/* eslint-disable no-unused-vars */
import React, { useRef, useState, useEffect } from 'react' // eslint-disable-line
import isElectron from 'is-electron'
import { WebsocketPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
import { version as remixdVersion } from '../../../../../libs/remixd/package.json'
var yo = require('yo-yo')
var modalDialog = require('../ui/modaldialog')
var modalDialogCustom = require('../ui/modal-dialog-custom')
var copyToClipboard = require('../ui/copy-to-clipboard')
import { PluginManager } from '@remixproject/engine'
import { AppModal, AlertModal } from '@remix-ui/app'
import { CopyToClipboard } from '@remix-ui/clipboard'
var csjs = require('csjs-inject')
var css = csjs`
.dialog {
display: flex;
flex-direction: column;
}
.dialogParagraph {
margin-bottom: 2em;
word-break: break-word;
}
`
const LOCALHOST = ' - connect to localhost - '
const profile = {
@ -32,29 +21,37 @@ const profile = {
version: packageJson.version
}
enum State {
ok,
cancel,
new
}
export class RemixdHandle extends WebsocketPlugin {
localhostProvider: any
appManager: PluginManager
state: State
constructor (localhostProvider, appManager) {
super(profile)
this.localhostProvider = localhostProvider
this.appManager = appManager
}
deactivate () {
async deactivate () {
if (super.socket) super.deactivate()
// this.appManager.deactivatePlugin('git') // plugin call doesn't work.. see issue https://github.com/ethereum/remix-plugin/issues/342
if (this.appManager.actives.includes('hardhat')) this.appManager.deactivatePlugin('hardhat')
if (this.appManager.actives.includes('slither')) this.appManager.deactivatePlugin('slither')
if (this.appManager.isActive('hardhat')) this.appManager.deactivatePlugin('hardhat')
if (this.appManager.isActive('slither')) this.appManager.deactivatePlugin('slither')
this.localhostProvider.close((error) => {
if (error) console.log(error)
})
}
activate () {
this.connectToLocalhost()
async activate () {
await this.connectToLocalhost()
}
async canceled () {
// await this.appManager.deactivatePlugin('git') // plugin call doesn't work.. see issue https://github.com/ethereum/remix-plugin/issues/342
await this.appManager.deactivatePlugin('remixd')
}
@ -65,23 +62,25 @@ export class RemixdHandle extends WebsocketPlugin {
* @param {String} txHash - hash of the transaction
*/
async connectToLocalhost () {
const connection = (error) => {
const connection = (error?:any) => {
if (error) {
console.log(error)
modalDialogCustom.alert(
'Cannot connect to the remixd daemon. ' +
'Please make sure you have the remixd running in the background.'
)
const alert:AlertModal = {
id: 'connectionAlert',
message: 'Cannot connect to the remixd daemon. Please make sure you have the remixd running in the background.'
}
this.call('modal', 'alert', alert)
this.canceled()
} else {
const intervalId = setInterval(() => {
if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed
clearInterval(intervalId)
console.log(error)
modalDialogCustom.alert(
'Connection to remixd terminated. ' +
'Please make sure remixd is still running in the background.'
)
const alert:AlertModal = {
id: 'connectionAlert',
message: 'Connection to remixd terminated.Please make sure remixd is still running in the background.'
}
this.call('modal', 'alert', alert)
this.canceled()
}
}, 3000)
@ -96,34 +95,38 @@ export class RemixdHandle extends WebsocketPlugin {
this.deactivate()
} else if (!isElectron()) {
// warn the user only if he/she is in the browser context
modalDialog(
'Connect to localhost',
remixdDialog(),
{
label: 'Connect',
fn: () => {
try {
this.localhostProvider.preInit()
super.activate()
setTimeout(() => {
if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed
connection(new Error('Connection with daemon failed.'))
} else {
connection()
}
}, 3000)
} catch (error) {
connection(error)
}
this.state = State.new
const mod:AppModal = {
id: 'remixdConnect',
title: 'Connect to localhost',
message: remixdDialog(),
okFn: () => {
this.state = State.ok
try {
this.localhostProvider.preInit()
super.activate()
setTimeout(() => {
if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed
connection(new Error('Connection with daemon failed.'))
} else {
connection()
}
}, 3000)
} catch (error) {
connection(error)
}
},
{
label: 'Cancel',
fn: () => {
this.canceled()
}
cancelFn: async () => {
this.state = State.cancel
await this.canceled()
},
okLabel: 'Connect',
cancelLabel: 'Cancel',
hideFn: async () => {
if (this.state === State.new) await this.canceled()
}
)
}
await this.call('modal', 'modal', mod)
} else {
try {
super.activate()
@ -137,31 +140,31 @@ export class RemixdHandle extends WebsocketPlugin {
function remixdDialog () {
const commandText = 'remixd -s <path-to-the-shared-folder> -u <remix-ide-instance-URL>'
return yo`
<div class=${css.dialog}>
<div class=${css.dialogParagraph}>
Access your local file system from Remix IDE using <a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">Remixd NPM package</a>.<br/><br/>
return (<>
<div className=''>
<div className='mb-2 text-break'>
Access your local file system from Remix IDE using <a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">Remixd NPM package</a>.<br/><br/>
Remixd needs to be running in the background to load the files in localhost workspace. For more info, please check the <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">Remixd tutorial</a>.
</div>
<div class=${css.dialogParagraph}>
<div className='mb-2 text-break'>
If you are just looking for the remixd command, here it is:
<br><br><b>${commandText}</b>
<span class="">${copyToClipboard(() => commandText)}</span>
<br></br><br></br><b>${commandText}</b>
<CopyToClipboard data-id='remixdCopyCommand' content={commandText}></CopyToClipboard>
</div>
<div class=${css.dialogParagraph}>
<div className='mb-2 text-break'>
When connected, a session will be started between <em>${window.location.origin}</em> and your local file system at <i>ws://127.0.0.1:65520</i>.
The shared folder will be in the "File Explorers" workspace named "localhost".
The shared folder will be in the "File Explorers" workspace named "localhost".
<br/>Read more about other <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a>
</div>
<div class=${css.dialogParagraph}>
<div className='mb-2 text-break'>
This feature is still in Alpha. We recommend to keep a backup of the shared folder.
</div>
<div class=${css.dialogParagraph}>
<h6 class="text-danger">
<div className='mb-2 text-break'>
<h6 className="text-danger">
Before using, make sure remixd version is latest i.e. <b>${remixdVersion}</b>
<br><a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">Read here how to update it</a>
<br></br><a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">Read here how to update it</a>
</h6>
</div>
</div>
`
</>)
}

@ -7,6 +7,7 @@
"allowSyntheticDefaultImports": true,
"types": ["node", "jest"],
"module": "es6",
"resolveJsonModule": true
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",

@ -2,3 +2,4 @@ export { default as RemixApp } from './lib/remix-app/remix-app'
export { dispatchModalContext } from './lib/remix-app/context/context'
export { ModalProvider } from './lib/remix-app/context/provider'
export { AppModal } from './lib/remix-app/interface/index'
export { AlertModal } from './lib/remix-app/interface/index'

@ -7,7 +7,6 @@ const DialogViewPlugin = () => {
const app = useContext(AppContext)
useEffect(() => {
console.log(modal, app)
app.modal.setDispatcher({ modal, alert, toast })
}, [])
return <></>

@ -10,10 +10,10 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
const [{ modals, toasters, focusModal, focusToaster }, dispatch] = useReducer(reducer, initialState)
const modal = (data: AppModal) => {
const { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue } = data
const { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn } = data
dispatch({
type: modalActionTypes.setModal,
payload: { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue }
payload: { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn }
})
}

@ -12,6 +12,7 @@ export interface AppModal {
cancelFn: () => void,
modalType?: ModalTypes,
defaultValue?: string
hideFn?: () => void
}
export interface AlertModal {

@ -18,7 +18,8 @@ export const modalReducer = (state: ModalState = ModalInitialState, action: Moda
cancelLabel: modalList[0].cancelLabel,
cancelFn: modalList[0].cancelFn,
modalType: modalList[0].modalType,
defaultValue: modalList[0].defaultValue
defaultValue: modalList[0].defaultValue,
hideFn: modalList[0].hideFn
}
modalList = modalList.slice()
@ -28,6 +29,9 @@ export const modalReducer = (state: ModalState = ModalInitialState, action: Moda
return { ...state, modals: modalList }
}
case modalActionTypes.handleHideModal:
if (state.focusModal.hideFn) {
state.focusModal.hideFn()
}
state.focusModal = { ...state.focusModal, hide: true, message: null }
return { ...state }

@ -12,12 +12,15 @@ export const ModalDialog = (props: ModalDialogProps) => {
const [state, setState] = useState({
toggleBtn: true
})
const calledHideFunctionOnce = useRef<boolean>()
const modal = useRef(null)
const handleHide = () => {
props.handleHide()
if (!calledHideFunctionOnce.current) { props.handleHide() }
calledHideFunctionOnce.current = true
}
useEffect(() => {
calledHideFunctionOnce.current = props.hide
modal.current.focus()
}, [props.hide])
@ -32,12 +35,9 @@ export const ModalDialog = (props: ModalDialogProps) => {
}
if (modal.current) {
modal.current.addEventListener('blur', handleBlur)
return () => {
if (modal.current) {
modal.current.removeEventListener('blur', handleBlur)
}
}
}
return () => {
modal.current.removeEventListener('blur', handleBlur)
}
}, [modal.current])

Loading…
Cancel
Save