diff --git a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx index b03efe9f6b..fd59f1ede2 100644 --- a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx +++ b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx @@ -64,12 +64,16 @@ export const FileExplorer = (props: FileExplorerProps) => { }, [fileSystem.notification.message]) useEffect(() => { - if (fileSystem.files.expandPath.length > 0) { + if (global.fs.mode === 'browser') { setState(prevState => { - return { ...prevState, expandPath: [...new Set([...prevState.expandPath, ...fileSystem.files.expandPath])] } + return { ...prevState, expandPath: [...new Set([...prevState.expandPath, ...global.fs.browser.expandPath])] } + }) + } else if (global.fs.mode === 'localhost') { + setState(prevState => { + return { ...prevState, expandPath: [...new Set([...prevState.expandPath, ...global.fs.localhost.expandPath])] } }) } - }, [fileSystem.files.expandPath]) + }, [global.fs.browser.expandPath, global.fs.localhost.expandPath]) useEffect(() => { if (state.focusEdit.element) { diff --git a/libs/remix-ui/workspace/src/lib/actions/workspace.ts b/libs/remix-ui/workspace/src/lib/actions/workspace.ts index 99a9201e00..2e9fd350de 100644 --- a/libs/remix-ui/workspace/src/lib/actions/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/actions/workspace.ts @@ -5,8 +5,9 @@ import { checkSpecialChars, checkSlash } from '@remix-ui/helper' const QueryParams = require('../../../../../../apps/remix-ide/src/lib/query-params') const examples = require('../../../../../../apps/remix-ide/src/app/editor/examples') -// const queuedEvents = [] -// const pendingEvents = {} +const queuedEvents = [] +const pendingEvents = {} + let plugin, dispatch: React.Dispatch const setCurrentWorkspace = (workspace: string) => { @@ -51,36 +52,31 @@ const fetchDirectorySuccess = (path: string, files) => { } } -export const displayNotification = (title: string, message: string, labelOk: string, labelCancel: string, actionOk?: (...args) => void, actionCancel?: (...args) => void) => { +const displayNotification = (title: string, message: string, labelOk: string, labelCancel: string, actionOk?: (...args) => void, actionCancel?: (...args) => void) => { return { type: 'DISPLAY_NOTIFICATION', payload: { title, message, labelOk, labelCancel, actionOk, actionCancel } } } -export const hideNotification = () => { +const hideNotification = () => { return { type: 'HIDE_NOTIFICATION' } } -export const fetchDirectory = (mode: 'browser' | 'localhost', path: string) => (dispatch: React.Dispatch) => { - const provider = mode === 'browser' ? plugin.fileProviders.workspace : plugin.fileProviders.localhost - const promise = new Promise((resolve) => { - provider.resolveDirectory(path, (error, fileTree) => { - if (error) console.error(error) - - resolve(fileTree) - }) - }) +const fileAddedSuccess = (filePath: string) => { + return { + type: 'FILE_ADDED_SUCCESS', + payload: filePath + } +} - dispatch(fetchDirectoryRequest(promise)) - promise.then((fileTree) => { - dispatch(fetchDirectorySuccess(path, fileTree)) - }).catch((error) => { - dispatch(fetchDirectoryError({ error })) - }) - return promise +const folderAddedSuccess = (folderPath: string) => { + return { + type: 'FOLDER_ADDED_SUCCESS', + payload: folderPath + } } const createWorkspaceTemplate = async (workspaceName: string, setDefaults = true, template: 'gist-template' | 'code-template' | 'default-template' = 'default-template') => { @@ -129,7 +125,7 @@ const createWorkspaceTemplate = async (workspaceName: string, setDefaults = true const data = response.data if (!data.files) { - return dispatch(displayNotification('Gist load error', 'No files found', 'OK', null, () => {}, null)) + return dispatch(displayNotification('Gist load error', 'No files found', 'OK', null, () => { dispatch(hideNotification()) }, null)) } const obj = {} @@ -148,7 +144,7 @@ const createWorkspaceTemplate = async (workspaceName: string, setDefaults = true } }) } catch (e) { - dispatch(displayNotification('Gist load error', e.message, 'OK', null, () => {}, null)) + dispatch(displayNotification('Gist load error', e.message, 'OK', null, () => { dispatch(hideNotification()) }, null)) console.error(e) } break @@ -200,6 +196,16 @@ const getWorkspaces = async (): Promise | undefined => { } } +const listenOnEvents = (provider) => { + provider.event.on('fileAdded', async (filePath) => { + await executeEvent('fileAdded', filePath) + }) + + provider.event.on('folderAdded', async (folderPath) => { + await executeEvent('folderAdded', folderPath) + }) +} + export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch) => { if (filePanelPlugin) { plugin = filePanelPlugin @@ -226,12 +232,9 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React. } } } - // provider.event.on('fileAdded', async (filePath) => { - // await executeEvent('fileAdded', filePath) - // }) - // provider.event.on('folderAdded', async (folderPath) => { - // await executeEvent('folderAdded', folderPath) - // }) + + listenOnEvents(provider) + // provider.event.on('fileRemoved', async (removePath) => { // await executeEvent('fileRemoved', removePath) // }) @@ -290,16 +293,35 @@ export const initLocalhost = (filePanelPlugin) => async (dispatch: React.Dispatc } } -// const fileAdded = async (filePath: string) => { -// if (extractParentFromKey(filePath) === '/.workspaces') return -// const path = extractParentFromKey(filePath) || provider.workspace || provider.type || '' -// const data = await fetchDirectoryContent(provider, path) +export const fetchDirectory = (path: string) => (dispatch: React.Dispatch) => { + const provider = plugin.fileManager.currentFileProvider() + const promise = new Promise((resolve) => { + provider.resolveDirectory(path, (error, fileTree) => { + if (error) console.error(error) -// await dispatch(fileAddedSuccess(path, data)) -// if (filePath.includes('_test.sol')) { -// plugin.emit('newTestFileCreated', filePath) -// } -// } + resolve(fileTree) + }) + }) + + dispatch(fetchDirectoryRequest(promise)) + promise.then((fileTree) => { + dispatch(fetchDirectorySuccess(path, fileTree)) + }).catch((error) => { + dispatch(fetchDirectoryError({ error })) + }) + return promise +} + +const fileAdded = async (filePath: string) => { + await dispatch(fileAddedSuccess(filePath)) + if (filePath.includes('_test.sol')) { + plugin.emit('newTestFileCreated', filePath) + } +} + +const folderAdded = async (folderPath: string) => { + await dispatch(folderAddedSuccess(folderPath)) +} // const folderAdded = async (folderPath: string) => { // if (extractParentFromKey(folderPath) === '/.workspaces') return @@ -328,60 +350,60 @@ export const initLocalhost = (filePanelPlugin) => async (dispatch: React.Dispatc // await fetchDirectory(provider, workspaceName)(dispatch) // } -// const executeEvent = async (eventName: 'fileAdded' | 'folderAdded' | 'fileRemoved' | 'fileRenamed' | 'rootFolderChanged', path?: string) => { -// if (Object.keys(pendingEvents).length) { -// return queuedEvents.push({ eventName, path }) -// } -// pendingEvents[eventName + path] = { eventName, path } -// switch (eventName) { -// case 'fileAdded': -// await fileAdded(path) -// delete pendingEvents[eventName + path] -// if (queuedEvents.length) { -// const next = queuedEvents.pop() - -// await executeEvent(next.eventName, next.path) -// } -// break - -// case 'folderAdded': -// await folderAdded(path) -// delete pendingEvents[eventName + path] -// if (queuedEvents.length) { -// const next = queuedEvents.pop() - -// await executeEvent(next.eventName, next.path) -// } -// break - -// case 'fileRemoved': -// await fileRemoved(path) -// delete pendingEvents[eventName + path] -// if (queuedEvents.length) { -// const next = queuedEvents.pop() - -// await executeEvent(next.eventName, next.path) -// } -// break - -// case 'fileRenamed': -// await fileRenamed(path) -// delete pendingEvents[eventName + path] -// if (queuedEvents.length) { -// const next = queuedEvents.pop() - -// await executeEvent(next.eventName, next.path) -// } -// break - -// case 'rootFolderChanged': -// await rootFolderChanged() -// delete pendingEvents[eventName + path] -// if (queuedEvents.length) { -// const next = queuedEvents.pop() - -// await executeEvent(next.eventName, next.path) -// } -// break -// } -// } +const executeEvent = async (eventName: 'fileAdded' | 'folderAdded' | 'fileRemoved' | 'fileRenamed' | 'rootFolderChanged', path?: string) => { + if (Object.keys(pendingEvents).length) { + return queuedEvents.push({ eventName, path }) + } + pendingEvents[eventName + path] = { eventName, path } + switch (eventName) { + case 'fileAdded': + await fileAdded(path) + delete pendingEvents[eventName + path] + if (queuedEvents.length) { + const next = queuedEvents.pop() + + await executeEvent(next.eventName, next.path) + } + break + + case 'folderAdded': + await folderAdded(path) + delete pendingEvents[eventName + path] + if (queuedEvents.length) { + const next = queuedEvents.pop() + + await executeEvent(next.eventName, next.path) + } + break + + // case 'fileRemoved': + // await fileRemoved(path) + // delete pendingEvents[eventName + path] + // if (queuedEvents.length) { + // const next = queuedEvents.pop() + + // await executeEvent(next.eventName, next.path) + // } + // break + + // case 'fileRenamed': + // await fileRenamed(path) + // delete pendingEvents[eventName + path] + // if (queuedEvents.length) { + // const next = queuedEvents.pop() + + // await executeEvent(next.eventName, next.path) + // } + // break + + // case 'rootFolderChanged': + // await rootFolderChanged() + // delete pendingEvents[eventName + path] + // if (queuedEvents.length) { + // const next = queuedEvents.pop() + + // await executeEvent(next.eventName, next.path) + // } + // break + } +} diff --git a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx index 1165156fc8..a32430db90 100644 --- a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx +++ b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx @@ -32,7 +32,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => { } const dispatchFetchDirectory = async (path: string) => { - await fetchDirectory(fs.mode, path)(fsDispatch) + await fetchDirectory(path)(fsDispatch) } useEffect(() => { diff --git a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts index f6057aac86..561386a0b2 100644 --- a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts @@ -1,4 +1,5 @@ import { extractNameFromKey, File } from '@remix-ui/file-explorer' +import * as _ from 'lodash' interface Action { type: string payload: any @@ -7,13 +8,15 @@ export interface BrowserState { browser: { currentWorkspace: string, workspaces: string[], - files: { [x: string]: Record } + files: { [x: string]: Record }, + expandPath: string[] isRequesting: boolean, isSuccessful: boolean, error: string }, localhost: { files: { [x: string]: Record }, + expandPath: string[], isRequesting: boolean, isSuccessful: boolean, error: string @@ -34,12 +37,14 @@ export const browserInitialState: BrowserState = { currentWorkspace: '', workspaces: [], files: {}, + expandPath: [], isRequesting: false, isSuccessful: false, error: null }, localhost: { files: {}, + expandPath: [], isRequesting: false, isSuccessful: false, error: null @@ -152,6 +157,43 @@ export const browserReducer = (state = browserInitialState, action: Action) => { notification: browserInitialState.notification } } + + case 'FILE_ADDED_SUCCESS': { + const payload = action.payload as string + + return { + ...state, + browser: { + ...state.browser, + files: fileAdded(state, payload), + expandPath: [...new Set([...state.browser.expandPath, payload])] + }, + localhost: { + ...state.localhost, + files: fileAdded(state, payload), + expandPath: [...new Set([...state.localhost.expandPath, payload])] + } + } + } + + case 'FOLDER_ADDED_SUCCESS': { + const payload = action.payload as string + + return { + ...state, + browser: { + ...state.browser, + files: folderAdded(state, payload), + expandPath: [...new Set([...state.browser.expandPath, payload])] + }, + localhost: { + ...state.localhost, + files: folderAdded(state, payload), + expandPath: [...new Set([...state.localhost.expandPath, payload])] + } + } + } + default: throw new Error() } @@ -211,3 +253,32 @@ const normalize = (filesList): Record => { return Object.assign({}, folders, files) } + +const fileAdded = (state: BrowserState, path: string): { [x: string]: Record } => { + let files = state.mode === 'browser' ? state.browser.files : state.localhost.files + const _path = splitPath(state, path) + + files = _.set(files, _path) + return files +} + +const folderAdded = (state: BrowserState, path: string): { [x: string]: Record } => { + let files = state.mode === 'browser' ? state.browser.files : state.localhost.files + const _path = splitPath(state, path) + + files = _.set(files, _path) + return files +} + +const splitPath = (state: BrowserState, path: string): string[] | string => { + const root = state.mode === 'browser' ? state.browser.currentWorkspace : 'localhost' + + const pathArr: string[] = path.split('/').filter(value => value) + + if (pathArr[0] !== root) pathArr.unshift(root) + const _path = pathArr.map((key, index) => index > 1 ? ['child', key] : key).reduce((acc: string[], cur) => { + return Array.isArray(cur) ? [...acc, ...cur] : [...acc, cur] + }, []) + + return _path +} 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 f09e111c0a..c1ff44d550 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -29,7 +29,7 @@ export function Workspace (props: WorkspaceProps) { useEffect(() => { if (global.fs.browser.currentWorkspace) { setCurrentWorkspace(global.fs.browser.currentWorkspace) - global.dispatchFetchDirectory(global.fs.browser.currentWorkspace) + // global.dispatchFetchDirectory(global.fs.browser.currentWorkspace) } }, [global.fs.browser.currentWorkspace])