Move context menu operation to reducer and removed duplicate event registration

pull/1575/head
ioedeveloper 3 years ago
parent 7ad686d1e4
commit 783e92d060
  1. 24
      apps/remix-ide/src/app/panels/file-panel.js
  2. 69
      libs/remix-ui/workspace/src/lib/actions/events.ts
  3. 16
      libs/remix-ui/workspace/src/lib/actions/payload.ts
  4. 9
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  5. 2
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  6. 105
      libs/remix-ui/workspace/src/lib/reducers/workspace.ts
  7. 17
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  8. 2
      libs/remix-ui/workspace/src/lib/types/index.ts

@ -9,7 +9,6 @@ const { GitHandle } = require('../files/git-handle.js')
const { HardhatHandle } = require('../files/hardhat-handle.js')
const { SlitherHandle } = require('../files/slither-handle.js')
const globalRegistry = require('../../global/registry')
const modalDialogCustom = require('../ui/modal-dialog-custom')
/*
Overview of APIs:
* fileManager: @args fileProviders (browser, shared-folder, swarm, github, etc ...) & config & editor
@ -53,11 +52,7 @@ module.exports = class Filepanel extends ViewPlugin {
this.gitHandle = new GitHandle()
this.hardhatHandle = new HardhatHandle()
this.slitherHandle = new SlitherHandle()
this.registeredMenuItems = []
this.removedMenuItems = []
this.request = {}
this.workspaces = []
this.initialWorkspace = null
this.appManager = appManager
this.workspaceStatus = {}
}
@ -81,26 +76,11 @@ module.exports = class Filepanel extends ViewPlugin {
* @param callback (...args) => void
*/
registerContextMenuItem (item) {
if (!item) throw new Error('Invalid register context menu argument')
if (!item.name || !item.id) throw new Error('Item name and id is mandatory')
if (!item.type && !item.path && !item.extension && !item.pattern) throw new Error('Invalid file matching criteria provided')
if (this.registeredMenuItems.filter((o) => {
return o.id === item.id && o.name === item.name
}).length) throw new Error(`Action ${item.name} already exists on ${item.id}`)
this.registeredMenuItems = [...this.registeredMenuItems, item]
this.removedMenuItems = this.removedMenuItems.filter(menuItem => item.id !== menuItem.id)
this.renderComponent()
this.emit('registerContextMenuItem', item)
}
removePluginActions (plugin) {
this.registeredMenuItems = this.registeredMenuItems.filter((item) => {
if (item.id !== plugin.name || item.sticky === true) return true
else {
this.removedMenuItems.push(item)
return false
}
})
this.renderComponent()
this.emit('removePluginActions', plugin)
}
getCurrentWorkspace () {

@ -1,6 +1,7 @@
import { extractParentFromKey } from '@remix-ui/helper'
import React from 'react'
import { displayNotification, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, rootFolderChangedSuccess } from './payload'
import { action } from '../types'
import { displayNotification, displayPopUp, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, removeContextMenuItem, rootFolderChangedSuccess, setContextMenuItem } from './payload'
import { addInputField, createWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace'
const queuedEvents = []
@ -8,8 +9,39 @@ const pendingEvents = {}
const LOCALHOST = ' - connect to localhost - '
let plugin, dispatch: React.Dispatch<any>
export const listenOnEvents = (filePanelPlugin, provider) => async (reducerDispatch: React.Dispatch<any>) => {
export const listenOnPluginEvents = (filePanelPlugin) => {
plugin = filePanelPlugin
plugin.on('filePanel', 'createWorkspace', (name: string) => {
createWorkspace(name)
})
plugin.on('filePanel', 'renameWorkspace', (oldName: string, workspaceName: string) => {
renameWorkspace(oldName, workspaceName)
})
plugin.on('filePanel', 'registerContextMenuItem', (item: action) => {
registerContextMenuItem(item)
})
plugin.on('filePanel', 'removePluginActions', (plugin) => {
removePluginActions(plugin)
})
plugin.on('filePanel', 'displayNewFileInput', (path) => {
addInputField('file', path)
})
plugin.on('filePanel', 'uploadFileEvent', (dir: string, target) => {
uploadFile(target, dir)
})
plugin.on('remixd', 'rootFolderChanged', async (path: string) => {
await executeEvent('rootFolderChanged', path)
})
}
export const listenOnProviderEvents = (provider) => async (reducerDispatch: React.Dispatch<any>) => {
dispatch = reducerDispatch
provider.event.on('fileAdded', async (filePath: string) => {
@ -29,10 +61,6 @@ export const listenOnEvents = (filePanelPlugin, provider) => async (reducerDispa
await executeEvent('fileRenamed', oldPath, newPath)
})
plugin.on('remixd', 'rootFolderChanged', async (path: string) => {
await executeEvent('rootFolderChanged', path)
})
// provider.event.on('disconnected', () => {
// dispatch(setMode('browser'))
// })
@ -77,29 +105,21 @@ export const listenOnEvents = (filePanelPlugin, provider) => async (reducerDispa
provider.event.on('fileRenamedError', async () => {
dispatch(displayNotification('File Renamed Failed', '', 'Ok', 'Cancel'))
})
}
plugin.on('filePanel', 'displayNewFileInput', (path) => {
addInputField('file', path)
})
plugin.on('filePanel', 'uploadFileEvent', (dir: string, target) => {
uploadFile(target, dir)
})
provider.event.on('createWorkspace', (name: string) => {
createWorkspace(name)
})
plugin.on('filePanel', 'createWorkspace', (name: string) => {
createWorkspace(name)
})
const registerContextMenuItem = (item: action) => {
if (!item) return dispatch(displayPopUp('Invalid register context menu argument'))
if (!item.name || !item.id) return dispatch(displayPopUp('Item name and id is mandatory'))
if (!item.type && !item.path && !item.extension && !item.pattern) return dispatch(displayPopUp('Invalid file matching criteria provided'))
dispatch(setContextMenuItem(item))
}
plugin.on('filePanel', 'renameWorkspace', (oldName: string, workspaceName: string) => {
renameWorkspace(oldName, workspaceName)
})
const removePluginActions = (plugin) => {
dispatch(removeContextMenuItem(plugin))
}
const fileAdded = async (filePath: string) => {
console.log('fileAdded: ', filePath)
await dispatch(fileAddedSuccess(filePath))
if (filePath.includes('_test.sol')) {
plugin.emit('newTestFileCreated', filePath)
@ -107,6 +127,7 @@ const fileAdded = async (filePath: string) => {
}
const folderAdded = async (folderPath: string) => {
console.log('folderAdded: ', folderPath)
const provider = plugin.fileManager.currentFileProvider()
const path = extractParentFromKey(folderPath) || provider.workspace || provider.type || ''

@ -1,3 +1,5 @@
import { action } from '../types'
export const setCurrentWorkspace = (workspace: string) => {
return {
type: 'SET_CURRENT_WORKSPACE',
@ -184,3 +186,17 @@ export const focusElement = (elements: { key: string, type: 'file' | 'folder' |
payload: elements
}
}
export const setContextMenuItem = (item: action) => {
return {
type: 'SET_CONTEXT_MENU_ITEM',
payload: item
}
}
export const removeContextMenuItem = (plugin) => {
return {
type: 'REMOVE_CONTEXT_MENU_ITEM',
payload: plugin
}
}

@ -5,7 +5,7 @@ import { checkSpecialChars, checkSlash, extractNameFromKey, createNonClashingNam
import Gists from 'gists'
import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel/type'
import { addInputFieldSuccess, createWorkspaceError, createWorkspaceRequest, createWorkspaceSuccess, displayNotification, displayPopUp, fetchDirectoryError, fetchDirectoryRequest, fetchDirectorySuccess, fetchWorkspaceDirectoryError, fetchWorkspaceDirectoryRequest, fetchWorkspaceDirectorySuccess, focusElement, hideNotification, hidePopUp, removeInputFieldSuccess, setCurrentWorkspace, setDeleteWorkspace, setMode, setRenameWorkspace, setWorkspaces } from './payload'
import { listenOnEvents } from './events'
import { listenOnPluginEvents, listenOnProviderEvents } from './events'
const QueryParams = require('../../../../../../apps/remix-ide/src/lib/query-params')
const examples = require('../../../../../../apps/remix-ide/src/app/editor/examples')
@ -43,9 +43,9 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
}
}
listenOnEvents(plugin, workspaceProvider)(dispatch)
listenOnEvents(plugin, localhostProvider)(dispatch)
// dispatch(setWorkspaces(workspaces))
listenOnPluginEvents(plugin)
listenOnProviderEvents(workspaceProvider)(dispatch)
listenOnProviderEvents(localhostProvider)(dispatch)
dispatch(setMode('browser'))
}
}
@ -106,6 +106,7 @@ export const removeInputField = async (path: string) => {
}
export const createWorkspace = async (workspaceName: string) => {
console.log('workspaceName: ', workspaceName)
const promise = createWorkspaceTemplate(workspaceName, true, 'default-template')
dispatch(createWorkspaceRequest(promise))

@ -209,7 +209,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
}
return (
<FileSystemContext.Provider value={value}>
<Workspace plugin={plugin} />
<Workspace />
<ModalDialog id='fileSystem' { ...focusModal } handleHide={ handleHideModal } />
<Toaster message={focusToaster} handleHide={handleToaster} />
</FileSystemContext.Provider>

@ -1,5 +1,5 @@
import { extractNameFromKey } from '@remix-ui/helper'
import { FileType } from '../types'
import { action, FileType } from '../types'
import * as _ from 'lodash'
interface Action {
type: string
@ -13,7 +13,12 @@ export interface BrowserState {
expandPath: string[]
isRequesting: boolean,
isSuccessful: boolean,
error: string
error: string,
contextMenu: {
registeredMenuItems: action[],
removedMenuItems: action[],
error: string
}
},
localhost: {
sharedFolder: string,
@ -21,7 +26,12 @@ export interface BrowserState {
expandPath: string[],
isRequesting: boolean,
isSuccessful: boolean,
error: string
error: string,
contextMenu: {
registeredMenuItems: action[],
removedMenuItems: action[],
error: string
}
},
mode: 'browser' | 'localhost',
notification: {
@ -46,7 +56,12 @@ export const browserInitialState: BrowserState = {
expandPath: [],
isRequesting: false,
isSuccessful: false,
error: null
error: null,
contextMenu: {
registeredMenuItems: [],
removedMenuItems: [],
error: null
}
},
localhost: {
sharedFolder: '',
@ -54,7 +69,12 @@ export const browserInitialState: BrowserState = {
expandPath: [],
isRequesting: false,
isSuccessful: false,
error: null
error: null,
contextMenu: {
registeredMenuItems: [],
removedMenuItems: [],
error: null
}
},
mode: 'browser',
notification: {
@ -465,6 +485,38 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
}
case 'SET_CONTEXT_MENU_ITEM': {
const payload = action.payload as action
return {
...state,
browser: {
...state.browser,
contextMenu: state.mode === 'browser' ? addContextMenuItem(state, payload) : state.browser.contextMenu
},
localhost: {
...state.localhost,
contextMenu: state.mode === 'localhost' ? addContextMenuItem(state, payload) : state.localhost.contextMenu
}
}
}
case 'REMOVE_CONTEXT_MENU_ITEM': {
const payload = action.payload
return {
...state,
browser: {
...state.browser,
contextMenu: state.mode === 'browser' ? removeContextMenuItem(state, payload) : state.browser.contextMenu
},
localhost: {
...state.localhost,
contextMenu: state.mode === 'localhost' ? removeContextMenuItem(state, payload) : state.localhost.contextMenu
}
}
}
default:
throw new Error()
}
@ -609,3 +661,46 @@ const splitPath = (state: BrowserState, path: string): string[] | string => {
return _path
}
const addContextMenuItem = (state: BrowserState, item: action): { registeredMenuItems: action[], removedMenuItems: action[], error: string } => {
let registeredItems = state[state.mode].contextMenu.registeredMenuItems
let removedItems = state[state.mode].contextMenu.removedMenuItems
let error = null
if (registeredItems.filter((o) => {
return o.id === item.id && o.name === item.name
}).length) {
error = `Action ${item.name} already exists on ${item.id}`
return {
registeredMenuItems: registeredItems,
removedMenuItems: removedItems,
error
}
}
registeredItems = [...registeredItems, item]
removedItems = removedItems.filter(menuItem => item.id !== menuItem.id)
return {
registeredMenuItems: registeredItems,
removedMenuItems: removedItems,
error
}
}
const removeContextMenuItem = (state: BrowserState, plugin): { registeredMenuItems: action[], removedMenuItems: action[], error: string } => {
let registeredItems = state[state.mode].contextMenu.registeredMenuItems
const removedItems = state[state.mode].contextMenu.removedMenuItems
const error = null
registeredItems = registeredItems.filter((item) => {
if (item.id !== plugin.name || item.sticky === true) return true
else {
removedItems.push(item)
return false
}
})
return {
registeredMenuItems: registeredItems,
removedMenuItems: removedItems,
error
}
}

@ -1,12 +1,12 @@
import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line
import { FileExplorer } from './components/file-explorer' // eslint-disable-line
import './css/remix-ui-workspace.css'
import { WorkspaceProps, WorkspaceState } from './types'
import { WorkspaceState } from './types'
import { FileSystemContext } from './contexts'
const canUpload = window.File || window.FileReader || window.FileList || window.Blob
export function Workspace (props: WorkspaceProps) {
export function Workspace () {
const LOCALHOST = ' - connect to localhost - '
const NO_WORKSPACE = ' - none - '
const [state] = useState<WorkspaceState>({
@ -16,6 +16,8 @@ export function Workspace (props: WorkspaceProps) {
})
const [currentWorkspace, setCurrentWorkspace] = useState<string>(NO_WORKSPACE)
const global = useContext(FileSystemContext)
const workspaceRenameInput = useRef()
const workspaceCreateInput = useRef()
useEffect(() => {
global.dispatchInitWorkspace()
@ -54,9 +56,6 @@ export function Workspace (props: WorkspaceProps) {
global.modal('Delete Current Workspace', 'Are you sure to delete the current workspace?', 'OK', onFinishDeleteWorkspace, '')
}
const workspaceRenameInput = useRef()
const workspaceCreateInput = useRef()
const onFinishRenameWorkspace = async () => {
if (workspaceRenameInput.current === undefined) return
// @ts-ignore: Object is possibly 'null'.
@ -185,8 +184,8 @@ export function Workspace (props: WorkspaceProps) {
<FileExplorer
name={currentWorkspace}
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
contextMenuItems={props.plugin.registeredMenuItems}
removedContextMenuItems={props.plugin.removedMenuItems}
contextMenuItems={global.fs.browser.contextMenu.registeredMenuItems}
removedContextMenuItems={global.fs.browser.contextMenu.removedMenuItems}
files={global.fs.browser.files}
expandPath={global.fs.browser.expandPath}
focusEdit={global.fs.focusEdit}
@ -219,8 +218,8 @@ export function Workspace (props: WorkspaceProps) {
<FileExplorer
name='localhost'
menuItems={['createNewFile', 'createNewFolder']}
contextMenuItems={props.plugin.registeredMenuItems}
removedContextMenuItems={props.plugin.removedMenuItems}
contextMenuItems={global.fs.localhost.contextMenu.registeredMenuItems}
removedContextMenuItems={global.fs.localhost.contextMenu.removedMenuItems}
files={global.fs.localhost.files}
expandPath={global.fs.localhost.expandPath}
focusEdit={global.fs.focusEdit}

@ -90,7 +90,7 @@ export interface FileExplorerMenuProps {
uploadFile: (target: EventTarget & HTMLInputElement) => void
}
export type action = { name: string, type?: Array<'folder' | 'gist' | 'file'>, path?: string[], extension?: string[], pattern?: string[], id: string, multiselect: boolean, label: string }
export type action = { name: string, type?: Array<'folder' | 'gist' | 'file'>, path?: string[], extension?: string[], pattern?: string[], id: string, multiselect: boolean, label: string, sticky?: boolean }
export type MenuItems = action[]
export interface FileExplorerContextMenuProps {

Loading…
Cancel
Save