diff --git a/apps/remix-ide-e2e/src/tests/plugin_api.ts b/apps/remix-ide-e2e/src/tests/plugin_api.ts index a05197f4b6..9a4d7b74b2 100644 --- a/apps/remix-ide-e2e/src/tests/plugin_api.ts +++ b/apps/remix-ide-e2e/src/tests/plugin_api.ts @@ -295,7 +295,7 @@ module.exports = { scripts: { isDirectory: true }, tests: { isDirectory: true }, 'README.txt': { isDirectory: false } - }, null, null) + }, null, '/') }, '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) diff --git a/apps/remix-ide/src/app/files/fileProvider.js b/apps/remix-ide/src/app/files/fileProvider.js index 1a8639149f..fe8c803bf3 100644 --- a/apps/remix-ide/src/app/files/fileProvider.js +++ b/apps/remix-ide/src/app/files/fileProvider.js @@ -127,7 +127,13 @@ class FileProvider { async createDir (path, cb) { 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() let currentCheck = '' 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 diff --git a/apps/remix-ide/src/app/files/workspaceFileProvider.js b/apps/remix-ide/src/app/files/workspaceFileProvider.js index 3fcd635fcf..6193e7b486 100644 --- a/apps/remix-ide/src/app/files/workspaceFileProvider.js +++ b/apps/remix-ide/src/app/files/workspaceFileProvider.js @@ -2,7 +2,6 @@ const EventManager = require('events') const FileProvider = require('./fileProvider') -const pathModule = require('path') class WorkspaceFileProvider extends FileProvider { constructor () { @@ -31,16 +30,10 @@ class WorkspaceFileProvider extends FileProvider { } removePrefix (path) { + if (!path) path = '/' path = path.replace(/^\/|\/$/g, '') // remove first and last slash path = path.replace(/^\.\/+/, '') // remove ./ from start of string 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) let ret = this.workspacesPath + '/' + this.workspace + '/' + (path === '/' ? '' : path) @@ -80,8 +73,10 @@ class WorkspaceFileProvider extends FileProvider { async createWorkspace (name) { try { if (!name) name = 'default_workspace' + const path = this.workspacesPath + '/' + name + + await super.forceCreateDir(path) this.setWorkspace(name) - await super.createDir(name) this.event.emit('createWorkspace', name) } catch (e) { throw new Error(e) diff --git a/libs/remix-ui/workspace/src/lib/actions/events.ts b/libs/remix-ui/workspace/src/lib/actions/events.ts index ce3c6982f3..74b767cf73 100644 --- a/libs/remix-ui/workspace/src/lib/actions/events.ts +++ b/libs/remix-ui/workspace/src/lib/actions/events.ts @@ -2,6 +2,7 @@ import { fileDecoration } from '@remix-ui/file-decorators' import { extractParentFromKey } from '@remix-ui/helper' import React from 'react' 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 { addInputField, createWorkspace, deleteWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace' @@ -164,7 +165,7 @@ const fileAdded = async (filePath: string) => { const folderAdded = async (folderPath: string) => { const provider = plugin.fileManager.currentFileProvider() - const path = extractParentFromKey(folderPath) || provider.workspace || provider.type || '' + const path = extractParentFromKey(folderPath) || ROOT_PATH const promise = new Promise((resolve) => { provider.resolveDirectory(path, (error, fileTree) => { @@ -188,7 +189,7 @@ const fileRemoved = async (removePath: string) => { const fileRenamed = async (oldPath: string) => { const provider = plugin.fileManager.currentFileProvider() - const path = extractParentFromKey(oldPath) || provider.workspace || provider.type || '' + const path = extractParentFromKey(oldPath) || ROOT_PATH const promise = new Promise((resolve) => { provider.resolveDirectory(path, (error, fileTree) => { if (error) console.error(error) diff --git a/libs/remix-ui/workspace/src/lib/actions/workspace.ts b/libs/remix-ui/workspace/src/lib/actions/workspace.ts index 221e0e1a09..3ef203644e 100644 --- a/libs/remix-ui/workspace/src/lib/actions/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/actions/workspace.ts @@ -7,6 +7,7 @@ import { checkSlash, checkSpecialChars } from '@remix-ui/helper' import { JSONStandardInput, WorkspaceTemplate } from '../types' import { QueryParams } from '@remix-project/remix-lib' import * as templateWithContent from '@remix-project/remix-ws-templates' +import { ROOT_PATH } from '../utils/constants' const LOCALHOST = ' - connect to localhost - ' @@ -346,7 +347,7 @@ export const cloneRepository = async (url: string) => { const isActive = await plugin.call('manager', 'isActive', 'dgit') if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit') - await fetchWorkspaceDirectory(repoName) + await fetchWorkspaceDirectory(ROOT_PATH) dispatch(cloneRepositorySuccess()) }).catch(() => { const cloneModal = { diff --git a/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx b/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx index d8f9f01a37..131babab53 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx @@ -11,6 +11,7 @@ import { checkSpecialChars, extractNameFromKey, extractParentFromKey, joinPath } // eslint-disable-next-line @typescript-eslint/no-unused-vars import { FileRender } from './file-render' import { Drag } from "@remix-ui/drag-n-drop" +import { ROOT_PATH } from '../utils/constants' export const FileExplorer = (props: FileExplorerProps) => { const { name, contextMenuItems, removedContextMenuItems, files, fileState } = props @@ -32,7 +33,7 @@ export const FileExplorer = (props: FileExplorerProps) => { }, mouseOverElement: null, showContextMenu: false, - reservedKeywords: [name, 'gist-'], + reservedKeywords: [ROOT_PATH, 'gist-'], copyElement: [] }) const [canPaste, setCanPaste] = useState(false) @@ -137,14 +138,14 @@ export const FileExplorer = (props: FileExplorerProps) => { if (props.focusElement[0]) { 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 === 'file' && props.focusElement[0].key) return extractParentFromKey(props.focusElement[0].key) ? extractParentFromKey(props.focusElement[0].key) : name - else return 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 ROOT_PATH } } const createNewFile = async (newFilePath: string) => { try { - props.dispatchCreateNewFile(newFilePath, props.name) + props.dispatchCreateNewFile(newFilePath, ROOT_PATH) } catch (error) { 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) => { try { - props.dispatchCreateNewFolder(newFolderPath, props.name) + props.dispatchCreateNewFolder(newFolderPath, ROOT_PATH) } catch (e) { 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) => { - let parentFolder = getFocusedFolder() + const parentFolder = getFocusedFolder() const expandPath = [...new Set([...props.expandPath, parentFolder])] - parentFolder = parentFolder === name ? '/' : parentFolder props.dispatchHandleExpandPath(expandPath) props.dispatchUploadFile(target, parentFolder) } @@ -235,7 +235,6 @@ export const FileExplorer = (props: FileExplorerProps) => { } const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => { - path = path.indexOf(props.name + '/') === 0 ? path.replace(props.name + '/', '') : path if (!state.ctrlKey) { props.dispatchHandleClickFile(path, type) } else { @@ -379,7 +378,7 @@ export const FileExplorer = (props: FileExplorerProps) => { } 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 }) => { 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 let expandPath = [] - if (!props.expandPath.includes(props.name)) { - expandPath = [props.name, ...new Set([...props.expandPath])] + if (!props.expandPath.includes(ROOT_PATH)) { + expandPath = [ROOT_PATH, ...new Set([...props.expandPath])] } 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) } @@ -447,8 +446,8 @@ export const FileExplorer = (props: FileExplorerProps) => {
{ - files[props.name] && Object.keys(files[props.name]).map((key, index) => } => { 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) { 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 } => { if (!payload.fileTree) return state.mode === 'browser' ? state.browser.files : state[state.mode].files if (state.mode === 'browser') { - if (payload.path === state.browser.currentWorkspace) { - let files = normalize(payload.fileTree, payload.path, payload.type) - files = _.merge(files, state.browser.files[state.browser.currentWorkspace]) + if (payload.path === ROOT_PATH) { + let files = normalize(payload.fileTree, ROOT_PATH, payload.type) + files = _.merge(files, state.browser.files[ROOT_PATH]) if (deletePath) delete files[deletePath] - return { [state.browser.currentWorkspace]: files } + return { [ROOT_PATH]: files } } else { let files = state.browser.files const _path = splitPath(state, payload.path) @@ -755,14 +756,11 @@ const fetchDirectoryContent = (state: BrowserState, payload: { fileTree, path: s return files } } else { - if (payload.path === '/') { - const files = normalize(payload.fileTree, payload.path, payload.type) - return { [state.mode]: files } - } 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 (payload.path === ROOT_PATH) { + let files = normalize(payload.fileTree, ROOT_PATH, payload.type) + files = _.merge(files, state.localhost.files[ROOT_PATH]) if (deletePath) delete files[deletePath] - return { [state.mode]: files } + return { [ROOT_PATH]: files } } else { let files = state.localhost.files 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 } => { - if (state.mode === 'browser') { - const files = normalize(payload.fileTree, payload.path) + const files = normalize(payload.fileTree, ROOT_PATH) - return { [payload.path]: files } - } else { - return fetchDirectoryContent(state, payload) - } + return { [ROOT_PATH]: files } } const normalize = (filesList, directory?: string, newInputType?: 'folder' | 'file'): Record => { @@ -846,7 +840,7 @@ const normalize = (filesList, directory?: string, newInputType?: 'folder' | 'fil } 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) if (pathArr[0] !== root) pathArr.unshift(root) 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 2d2ce7dd28..3eb94b7dee 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -4,6 +4,7 @@ import { CustomMenu, CustomToggle } from '@remix-ui/helper' import { FileExplorer } from './components/file-explorer' // eslint-disable-line import { FileSystemContext } from './contexts' import './css/remix-ui-workspace.css' +import { ROOT_PATH } from './utils/constants' 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.browser.currentWorkspace) setCurrentWorkspace(global.fs.browser.currentWorkspace) else setCurrentWorkspace(NO_WORKSPACE) - global.dispatchFetchWorkspaceDirectory(global.fs.browser.currentWorkspace) + global.dispatchFetchWorkspaceDirectory(ROOT_PATH) } else if (global.fs.mode === 'localhost') { - global.dispatchFetchWorkspaceDirectory('/') + global.dispatchFetchWorkspaceDirectory(ROOT_PATH) setCurrentWorkspace(LOCALHOST) } }, [global.fs.browser.currentWorkspace, global.fs.localhost.sharedFolder, global.fs.mode]) diff --git a/libs/remix-ui/workspace/src/lib/utils/constants.ts b/libs/remix-ui/workspace/src/lib/utils/constants.ts new file mode 100644 index 0000000000..9c934c37ad --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/utils/constants.ts @@ -0,0 +1 @@ +export const ROOT_PATH = '/'