From 783e92d0608ea8fe30bafc534bbe63fccb1e938e Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Fri, 24 Sep 2021 17:17:26 +0100 Subject: [PATCH] Move context menu operation to reducer and removed duplicate event registration --- apps/remix-ide/src/app/panels/file-panel.js | 24 +--- .../workspace/src/lib/actions/events.ts | 69 ++++++++---- .../workspace/src/lib/actions/payload.ts | 16 +++ .../workspace/src/lib/actions/workspace.ts | 9 +- .../src/lib/providers/FileSystemProvider.tsx | 2 +- .../workspace/src/lib/reducers/workspace.ts | 105 +++++++++++++++++- .../workspace/src/lib/remix-ui-workspace.tsx | 17 ++- .../remix-ui/workspace/src/lib/types/index.ts | 2 +- 8 files changed, 178 insertions(+), 66 deletions(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 0b0b46ce39..c1442aa696 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -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 () { diff --git a/libs/remix-ui/workspace/src/lib/actions/events.ts b/libs/remix-ui/workspace/src/lib/actions/events.ts index 8a716beeaa..fa1ea36a1f 100644 --- a/libs/remix-ui/workspace/src/lib/actions/events.ts +++ b/libs/remix-ui/workspace/src/lib/actions/events.ts @@ -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 -export const listenOnEvents = (filePanelPlugin, provider) => async (reducerDispatch: React.Dispatch) => { +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) => { 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 || '' diff --git a/libs/remix-ui/workspace/src/lib/actions/payload.ts b/libs/remix-ui/workspace/src/lib/actions/payload.ts index 5d6b76775a..72944e040a 100644 --- a/libs/remix-ui/workspace/src/lib/actions/payload.ts +++ b/libs/remix-ui/workspace/src/lib/actions/payload.ts @@ -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 + } +} diff --git a/libs/remix-ui/workspace/src/lib/actions/workspace.ts b/libs/remix-ui/workspace/src/lib/actions/workspace.ts index 49c4f7ecba..df349593c1 100644 --- a/libs/remix-ui/workspace/src/lib/actions/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/actions/workspace.ts @@ -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)) diff --git a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx index 8a043ca751..fece8535c3 100644 --- a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx +++ b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx @@ -209,7 +209,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => { } return ( - + diff --git a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts index cf5343b45b..72682ced2a 100644 --- a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts @@ -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 + } +} diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index eeda21814b..4f8353a67c 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -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({ @@ -16,6 +16,8 @@ export function Workspace (props: WorkspaceProps) { }) const [currentWorkspace, setCurrentWorkspace] = useState(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) { 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 {