Merge pull request #2818 from ethereum/fix-fs

Maintain standard for root directory
pull/2860/head^2
bunsenstraat 2 years ago committed by GitHub
commit e4e5cc992c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  2. 9
      apps/remix-ide/src/app/files/fileProvider.js
  3. 13
      apps/remix-ide/src/app/files/workspaceFileProvider.js
  4. 5
      libs/remix-ui/workspace/src/lib/actions/events.ts
  5. 3
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  6. 27
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  7. 32
      libs/remix-ui/workspace/src/lib/reducers/workspace.ts
  8. 5
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  9. 1
      libs/remix-ui/workspace/src/lib/utils/constants.ts

@ -295,7 +295,7 @@ module.exports = {
scripts: { isDirectory: true }, scripts: { isDirectory: true },
tests: { isDirectory: true }, tests: { isDirectory: true },
'README.txt': { isDirectory: false } 'README.txt': { isDirectory: false }
}, null, null) }, null, '/')
}, },
'Should get all workspaces #group2': async function (browser: NightwatchBrowser) { 'Should get all workspaces #group2': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:getWorkspaces', [{name:"default_workspace",isGitRepo:false}, {name:"emptyworkspace",isGitRepo:false}, {name:"testspace",isGitRepo:false}], null, null) await clickAndCheckLog(browser, 'filePanel:getWorkspaces', [{name:"default_workspace",isGitRepo:false}, {name:"emptyworkspace",isGitRepo:false}, {name:"testspace",isGitRepo:false}], null, null)

@ -127,7 +127,13 @@ class FileProvider {
async createDir (path, cb) { async createDir (path, cb) {
const unprefixedpath = this.removePrefix(path) const unprefixedpath = this.removePrefix(path)
const paths = unprefixedpath.split('/')
await this.forceCreateDir(unprefixedpath)
if (cb) cb()
}
async forceCreateDir (path) {
const paths = path.split('/')
if (paths.length && paths[0] === '') paths.shift() if (paths.length && paths[0] === '') paths.shift()
let currentCheck = '' let currentCheck = ''
for (const value of paths) { for (const value of paths) {
@ -141,7 +147,6 @@ class FileProvider {
} }
} }
} }
if (cb) cb()
} }
// this will not add a folder as readonly but keep the original url to be able to restore it later // this will not add a folder as readonly but keep the original url to be able to restore it later

@ -2,7 +2,6 @@
const EventManager = require('events') const EventManager = require('events')
const FileProvider = require('./fileProvider') const FileProvider = require('./fileProvider')
const pathModule = require('path')
class WorkspaceFileProvider extends FileProvider { class WorkspaceFileProvider extends FileProvider {
constructor () { constructor () {
@ -31,16 +30,10 @@ class WorkspaceFileProvider extends FileProvider {
} }
removePrefix (path) { removePrefix (path) {
if (!path) path = '/'
path = path.replace(/^\/|\/$/g, '') // remove first and last slash path = path.replace(/^\/|\/$/g, '') // remove first and last slash
path = path.replace(/^\.\/+/, '') // remove ./ from start of string path = path.replace(/^\.\/+/, '') // remove ./ from start of string
if (path.startsWith(this.workspacesPath + '/' + this.workspace)) return path if (path.startsWith(this.workspacesPath + '/' + this.workspace)) return path
const splitPath = path.split('/')
if (splitPath[0] === this.workspace) {
splitPath[0] = this.workspacesPath + '/' + this.workspace
path = splitPath.join('/')
return path
}
path = super.removePrefix(path) path = super.removePrefix(path)
let ret = this.workspacesPath + '/' + this.workspace + '/' + (path === '/' ? '' : path) let ret = this.workspacesPath + '/' + this.workspace + '/' + (path === '/' ? '' : path)
@ -80,8 +73,10 @@ class WorkspaceFileProvider extends FileProvider {
async createWorkspace (name) { async createWorkspace (name) {
try { try {
if (!name) name = 'default_workspace' if (!name) name = 'default_workspace'
const path = this.workspacesPath + '/' + name
await super.forceCreateDir(path)
this.setWorkspace(name) this.setWorkspace(name)
await super.createDir(name)
this.event.emit('createWorkspace', name) this.event.emit('createWorkspace', name)
} catch (e) { } catch (e) {
throw new Error(e) throw new Error(e)

@ -2,6 +2,7 @@ import { fileDecoration } from '@remix-ui/file-decorators'
import { extractParentFromKey } from '@remix-ui/helper' import { extractParentFromKey } from '@remix-ui/helper'
import React from 'react' import React from 'react'
import { action, WorkspaceTemplate } from '../types' import { action, WorkspaceTemplate } from '../types'
import { ROOT_PATH } from '../utils/constants'
import { displayNotification, displayPopUp, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, loadLocalhostError, loadLocalhostRequest, loadLocalhostSuccess, removeContextMenuItem, removeFocus, rootFolderChangedSuccess, setContextMenuItem, setMode, setReadOnlyMode, setFileDecorationSuccess } from './payload' import { displayNotification, displayPopUp, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, loadLocalhostError, loadLocalhostRequest, loadLocalhostSuccess, removeContextMenuItem, removeFocus, rootFolderChangedSuccess, setContextMenuItem, setMode, setReadOnlyMode, setFileDecorationSuccess } from './payload'
import { addInputField, createWorkspace, deleteWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace' import { addInputField, createWorkspace, deleteWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace'
@ -164,7 +165,7 @@ const fileAdded = async (filePath: string) => {
const folderAdded = async (folderPath: string) => { const folderAdded = async (folderPath: string) => {
const provider = plugin.fileManager.currentFileProvider() const provider = plugin.fileManager.currentFileProvider()
const path = extractParentFromKey(folderPath) || provider.workspace || provider.type || '' const path = extractParentFromKey(folderPath) || ROOT_PATH
const promise = new Promise((resolve) => { const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => { provider.resolveDirectory(path, (error, fileTree) => {
@ -188,7 +189,7 @@ const fileRemoved = async (removePath: string) => {
const fileRenamed = async (oldPath: string) => { const fileRenamed = async (oldPath: string) => {
const provider = plugin.fileManager.currentFileProvider() const provider = plugin.fileManager.currentFileProvider()
const path = extractParentFromKey(oldPath) || provider.workspace || provider.type || '' const path = extractParentFromKey(oldPath) || ROOT_PATH
const promise = new Promise((resolve) => { const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => { provider.resolveDirectory(path, (error, fileTree) => {
if (error) console.error(error) if (error) console.error(error)

@ -7,6 +7,7 @@ import { checkSlash, checkSpecialChars } from '@remix-ui/helper'
import { JSONStandardInput, WorkspaceTemplate } from '../types' import { JSONStandardInput, WorkspaceTemplate } from '../types'
import { QueryParams } from '@remix-project/remix-lib' import { QueryParams } from '@remix-project/remix-lib'
import * as templateWithContent from '@remix-project/remix-ws-templates' import * as templateWithContent from '@remix-project/remix-ws-templates'
import { ROOT_PATH } from '../utils/constants'
const LOCALHOST = ' - connect to localhost - ' const LOCALHOST = ' - connect to localhost - '
@ -346,7 +347,7 @@ export const cloneRepository = async (url: string) => {
const isActive = await plugin.call('manager', 'isActive', 'dgit') const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit') if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
await fetchWorkspaceDirectory(repoName) await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(cloneRepositorySuccess()) dispatch(cloneRepositorySuccess())
}).catch(() => { }).catch(() => {
const cloneModal = { const cloneModal = {

@ -11,6 +11,7 @@ import { checkSpecialChars, extractNameFromKey, extractParentFromKey, joinPath }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileRender } from './file-render' import { FileRender } from './file-render'
import { Drag } from "@remix-ui/drag-n-drop" import { Drag } from "@remix-ui/drag-n-drop"
import { ROOT_PATH } from '../utils/constants'
export const FileExplorer = (props: FileExplorerProps) => { export const FileExplorer = (props: FileExplorerProps) => {
const { name, contextMenuItems, removedContextMenuItems, files, fileState } = props const { name, contextMenuItems, removedContextMenuItems, files, fileState } = props
@ -32,7 +33,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
}, },
mouseOverElement: null, mouseOverElement: null,
showContextMenu: false, showContextMenu: false,
reservedKeywords: [name, 'gist-'], reservedKeywords: [ROOT_PATH, 'gist-'],
copyElement: [] copyElement: []
}) })
const [canPaste, setCanPaste] = useState(false) const [canPaste, setCanPaste] = useState(false)
@ -137,14 +138,14 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (props.focusElement[0]) { if (props.focusElement[0]) {
if (props.focusElement[0].type === 'folder' && props.focusElement[0].key) return props.focusElement[0].key if (props.focusElement[0].type === 'folder' && props.focusElement[0].key) return props.focusElement[0].key
else if (props.focusElement[0].type === 'gist' && props.focusElement[0].key) return props.focusElement[0].key else if (props.focusElement[0].type === 'gist' && props.focusElement[0].key) return props.focusElement[0].key
else if (props.focusElement[0].type === 'file' && props.focusElement[0].key) return extractParentFromKey(props.focusElement[0].key) ? extractParentFromKey(props.focusElement[0].key) : name else if (props.focusElement[0].type === 'file' && props.focusElement[0].key) return extractParentFromKey(props.focusElement[0].key) ? extractParentFromKey(props.focusElement[0].key) : ROOT_PATH
else return name else return ROOT_PATH
} }
} }
const createNewFile = async (newFilePath: string) => { const createNewFile = async (newFilePath: string) => {
try { try {
props.dispatchCreateNewFile(newFilePath, props.name) props.dispatchCreateNewFile(newFilePath, ROOT_PATH)
} catch (error) { } catch (error) {
return props.modal('File Creation Failed', typeof error === 'string' ? error : error.message, 'Close', async () => {}) return props.modal('File Creation Failed', typeof error === 'string' ? error : error.message, 'Close', async () => {})
} }
@ -152,7 +153,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
const createNewFolder = async (newFolderPath: string) => { const createNewFolder = async (newFolderPath: string) => {
try { try {
props.dispatchCreateNewFolder(newFolderPath, props.name) props.dispatchCreateNewFolder(newFolderPath, ROOT_PATH)
} catch (e) { } catch (e) {
return props.modal('Folder Creation Failed', typeof e === 'string' ? e : e.message, 'Close', async () => {}) return props.modal('Folder Creation Failed', typeof e === 'string' ? e : e.message, 'Close', async () => {})
} }
@ -174,10 +175,9 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const uploadFile = (target) => { const uploadFile = (target) => {
let parentFolder = getFocusedFolder() const parentFolder = getFocusedFolder()
const expandPath = [...new Set([...props.expandPath, parentFolder])] const expandPath = [...new Set([...props.expandPath, parentFolder])]
parentFolder = parentFolder === name ? '/' : parentFolder
props.dispatchHandleExpandPath(expandPath) props.dispatchHandleExpandPath(expandPath)
props.dispatchUploadFile(target, parentFolder) props.dispatchUploadFile(target, parentFolder)
} }
@ -235,7 +235,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => { const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => {
path = path.indexOf(props.name + '/') === 0 ? path.replace(props.name + '/', '') : path
if (!state.ctrlKey) { if (!state.ctrlKey) {
props.dispatchHandleClickFile(path, type) props.dispatchHandleClickFile(path, type)
} else { } else {
@ -379,7 +378,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const handlePasteClick = (dest: string, destType: string) => { const handlePasteClick = (dest: string, destType: string) => {
dest = destType === 'file' ? extractParentFromKey(dest) || props.name : dest dest = destType === 'file' ? extractParentFromKey(dest) || ROOT_PATH : dest
state.copyElement.map(({ key, type }) => { state.copyElement.map(({ key, type }) => {
type === 'file' ? copyFile(key, dest) : copyFolder(key, dest) type === 'file' ? copyFile(key, dest) : copyFolder(key, dest)
}) })
@ -402,10 +401,10 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (e && (e.target as any).getAttribute('data-id') === 'fileExplorerFileUpload') return // we don't want to let propagate the input of type file if (e && (e.target as any).getAttribute('data-id') === 'fileExplorerFileUpload') return // we don't want to let propagate the input of type file
let expandPath = [] let expandPath = []
if (!props.expandPath.includes(props.name)) { if (!props.expandPath.includes(ROOT_PATH)) {
expandPath = [props.name, ...new Set([...props.expandPath])] expandPath = [ROOT_PATH, ...new Set([...props.expandPath])]
} else { } else {
expandPath = [...new Set(props.expandPath.filter(key => key && (typeof key === 'string') && !key.startsWith(props.name)))] expandPath = [...new Set(props.expandPath.filter(key => key && (typeof key === 'string')))]
} }
props.dispatchHandleExpandPath(expandPath) props.dispatchHandleExpandPath(expandPath)
} }
@ -447,8 +446,8 @@ export const FileExplorer = (props: FileExplorerProps) => {
<div className='pb-2'> <div className='pb-2'>
<TreeView id='treeViewMenu'> <TreeView id='treeViewMenu'>
{ {
files[props.name] && Object.keys(files[props.name]).map((key, index) => <FileRender files[ROOT_PATH] && Object.keys(files[ROOT_PATH]).map((key, index) => <FileRender
file={files[props.name][key]} file={files[ROOT_PATH][key]}
fileDecorations={fileState} fileDecorations={fileState}
index={index} index={index}
focusContext={state.focusContext} focusContext={state.focusContext}

@ -2,6 +2,7 @@ import { extractNameFromKey } from '@remix-ui/helper'
import { action, FileType } from '../types' import { action, FileType } from '../types'
import * as _ from 'lodash' import * as _ from 'lodash'
import { fileDecoration } from '@remix-ui/file-decorators' import { fileDecoration } from '@remix-ui/file-decorators'
import { ROOT_PATH } from '../utils/constants'
interface Action { interface Action {
type: string type: string
payload: any payload: any
@ -693,7 +694,7 @@ const fileRemoved = (state: BrowserState, path: string): { [x: string]: Record<s
const removeInputField = (state: BrowserState, path: string): { [x: string]: Record<string, FileType> } => { const removeInputField = (state: BrowserState, path: string): { [x: string]: Record<string, FileType> } => {
let files = state.mode === 'browser' ? state.browser.files : state.localhost.files let files = state.mode === 'browser' ? state.browser.files : state.localhost.files
const root = state.mode === 'browser' ? state.browser.currentWorkspace : state.mode const root = state.mode === 'browser' ? ROOT_PATH : state.mode
if (path === root) { if (path === root) {
delete files[root][path + '/' + 'blank'] delete files[root][path + '/' + 'blank']
@ -720,11 +721,11 @@ const removeInputField = (state: BrowserState, path: string): { [x: string]: Rec
const fetchDirectoryContent = (state: BrowserState, payload: { fileTree, path: string, type?: 'file' | 'folder' }, deletePath?: string): { [x: string]: Record<string, FileType> } => { const fetchDirectoryContent = (state: BrowserState, payload: { fileTree, path: string, type?: 'file' | 'folder' }, deletePath?: string): { [x: string]: Record<string, FileType> } => {
if (!payload.fileTree) return state.mode === 'browser' ? state.browser.files : state[state.mode].files if (!payload.fileTree) return state.mode === 'browser' ? state.browser.files : state[state.mode].files
if (state.mode === 'browser') { if (state.mode === 'browser') {
if (payload.path === state.browser.currentWorkspace) { if (payload.path === ROOT_PATH) {
let files = normalize(payload.fileTree, payload.path, payload.type) let files = normalize(payload.fileTree, ROOT_PATH, payload.type)
files = _.merge(files, state.browser.files[state.browser.currentWorkspace]) files = _.merge(files, state.browser.files[ROOT_PATH])
if (deletePath) delete files[deletePath] if (deletePath) delete files[deletePath]
return { [state.browser.currentWorkspace]: files } return { [ROOT_PATH]: files }
} else { } else {
let files = state.browser.files let files = state.browser.files
const _path = splitPath(state, payload.path) const _path = splitPath(state, payload.path)
@ -755,14 +756,11 @@ const fetchDirectoryContent = (state: BrowserState, payload: { fileTree, path: s
return files return files
} }
} else { } else {
if (payload.path === '/') { if (payload.path === ROOT_PATH) {
const files = normalize(payload.fileTree, payload.path, payload.type) let files = normalize(payload.fileTree, ROOT_PATH, payload.type)
return { [state.mode]: files } files = _.merge(files, state.localhost.files[ROOT_PATH])
} else if (payload.path === state.mode) {
let files = normalize(payload.fileTree, payload.path, payload.type)
files = _.merge(files, state[state.mode].files[state.mode])
if (deletePath) delete files[deletePath] if (deletePath) delete files[deletePath]
return { [state.mode]: files } return { [ROOT_PATH]: files }
} else { } else {
let files = state.localhost.files let files = state.localhost.files
const _path = splitPath(state, payload.path) const _path = splitPath(state, payload.path)
@ -787,13 +785,9 @@ const fetchDirectoryContent = (state: BrowserState, payload: { fileTree, path: s
} }
const fetchWorkspaceDirectoryContent = (state: BrowserState, payload: { fileTree, path: string }): { [x: string]: Record<string, FileType> } => { const fetchWorkspaceDirectoryContent = (state: BrowserState, payload: { fileTree, path: string }): { [x: string]: Record<string, FileType> } => {
if (state.mode === 'browser') { const files = normalize(payload.fileTree, ROOT_PATH)
const files = normalize(payload.fileTree, payload.path)
return { [payload.path]: files } return { [ROOT_PATH]: files }
} else {
return fetchDirectoryContent(state, payload)
}
} }
const normalize = (filesList, directory?: string, newInputType?: 'folder' | 'file'): Record<string, FileType> => { const normalize = (filesList, directory?: string, newInputType?: 'folder' | 'file'): Record<string, FileType> => {
@ -846,7 +840,7 @@ const normalize = (filesList, directory?: string, newInputType?: 'folder' | 'fil
} }
const splitPath = (state: BrowserState, path: string): string[] | string => { const splitPath = (state: BrowserState, path: string): string[] | string => {
const root = state.mode === 'browser' ? state.browser.currentWorkspace : 'localhost' const root = ROOT_PATH
const pathArr: string[] = (path || '').split('/').filter(value => value) const pathArr: string[] = (path || '').split('/').filter(value => value)
if (pathArr[0] !== root) pathArr.unshift(root) if (pathArr[0] !== root) pathArr.unshift(root)

@ -4,6 +4,7 @@ import { CustomMenu, CustomToggle } from '@remix-ui/helper'
import { FileExplorer } from './components/file-explorer' // eslint-disable-line import { FileExplorer } from './components/file-explorer' // eslint-disable-line
import { FileSystemContext } from './contexts' import { FileSystemContext } from './contexts'
import './css/remix-ui-workspace.css' import './css/remix-ui-workspace.css'
import { ROOT_PATH } from './utils/constants'
const canUpload = window.File || window.FileReader || window.FileList || window.Blob const canUpload = window.File || window.FileReader || window.FileList || window.Blob
@ -28,9 +29,9 @@ export function Workspace () {
if (global.fs.mode === 'browser') { if (global.fs.mode === 'browser') {
if (global.fs.browser.currentWorkspace) setCurrentWorkspace(global.fs.browser.currentWorkspace) if (global.fs.browser.currentWorkspace) setCurrentWorkspace(global.fs.browser.currentWorkspace)
else setCurrentWorkspace(NO_WORKSPACE) else setCurrentWorkspace(NO_WORKSPACE)
global.dispatchFetchWorkspaceDirectory(global.fs.browser.currentWorkspace) global.dispatchFetchWorkspaceDirectory(ROOT_PATH)
} else if (global.fs.mode === 'localhost') { } else if (global.fs.mode === 'localhost') {
global.dispatchFetchWorkspaceDirectory('/') global.dispatchFetchWorkspaceDirectory(ROOT_PATH)
setCurrentWorkspace(LOCALHOST) setCurrentWorkspace(LOCALHOST)
} }
}, [global.fs.browser.currentWorkspace, global.fs.localhost.sharedFolder, global.fs.mode]) }, [global.fs.browser.currentWorkspace, global.fs.localhost.sharedFolder, global.fs.mode])

Loading…
Cancel
Save