diff --git a/libs/remix-ui/file-explorer/src/lib/actions/fileSystem.ts b/libs/remix-ui/file-explorer/src/lib/actions/fileSystem.ts index e815d0bf30..3586342875 100644 --- a/libs/remix-ui/file-explorer/src/lib/actions/fileSystem.ts +++ b/libs/remix-ui/file-explorer/src/lib/actions/fileSystem.ts @@ -1,14 +1,6 @@ import React from 'react' import { File } from '../types' -import { extractNameFromKey, extractParentFromKey } from '../utils' - -const globalRegistry = require('../../../../../../apps/remix-ide/src/global/registry') -const initializeProvider = () => { - const fileProviders = globalRegistry.get('fileproviders').api - const browser = fileProviders.browser // eslint-disable-line - const workspace = fileProviders.workspace - const localhost = fileProviders.localhost // eslint-disable-line -} +import { extractNameFromKey } from '../utils' export const fetchDirectoryError = (error: any) => { return { @@ -37,9 +29,9 @@ export const fileSystemReset = () => { } } -const normalize = (filesList): File[] => { - const folders = [] - const files = [] +const normalize = (filesList): any => { + const folders = {} + const files = {} Object.keys(filesList || {}).forEach(key => { key = key.replace(/^\/|\/$/g, '') // remove first and last slash @@ -47,36 +39,35 @@ const normalize = (filesList): File[] => { path = path.replace(/^\/|\/$/g, '') // remove first and last slash if (filesList[key].isDirectory) { - folders.push({ + folders[key] = { path, name: extractNameFromKey(path), isDirectory: filesList[key].isDirectory - }) + } } else { - files.push({ + files[key] = { path, name: extractNameFromKey(path), isDirectory: filesList[key].isDirectory - }) + } } }) - return [...folders, ...files] + return Object.assign({}, folders, files) } -const fetchDirectoryContent = async (provider, folderPath: string): Promise => { +const fetchDirectoryContent = async (provider, folderPath: string): Promise => { return new Promise((resolve) => { provider.resolveDirectory(folderPath, (error, fileTree) => { if (error) console.error(error) const files = normalize(fileTree) - resolve(files) + resolve({ [extractNameFromKey(folderPath)]: files }) }) }) } export const fetchDirectory = (provider, path: string) => (dispatch: React.Dispatch) => { - initializeProvider() const promise = fetchDirectoryContent(provider, path) dispatch(fetchDirectoryRequest(promise)) @@ -88,6 +79,39 @@ export const fetchDirectory = (provider, path: string) => (dispatch: React.Dispa return promise } +export const resolveDirectoryError = (error: any) => { + return { + type: 'RESOLVE_DIRECTORY_ERROR', + payload: error + } +} + +export const resolveDirectoryRequest = (promise: Promise) => { + return { + type: 'RESOLVE_DIRECTORY_REQUEST', + payload: promise + } +} + +export const resolveDirectorySuccess = (path: string, files: File[]) => { + return { + type: 'RESOLVE_DIRECTORY_SUCCESS', + payload: { path, files } + } +} + +export const resolveDirectory = (provider, path: string) => (dispatch: React.Dispatch) => { + const promise = fetchDirectoryContent(provider, path) + + dispatch(resolveDirectoryRequest(promise)) + promise.then((files) => { + dispatch(resolveDirectorySuccess(path, files)) + }).catch((error) => { + dispatch(resolveDirectoryError({ error })) + }) + return promise +} + export const fetchProviderError = (error: any) => { return { type: 'FETCH_PROVIDER_ERROR', @@ -109,15 +133,10 @@ export const fetchProviderSuccess = (provider: any) => { } } -export const setProvider = () => (dispatch: React.Dispatch) => { - initializeProvider() - const promise = fetchDirectoryContent(provider, path) - - dispatch(fetchDirectoryRequest(promise)) - promise.then((files) => { - dispatch(fetchDirectorySuccess(path, files)) - }).catch((error) => { - dispatch(fetchDirectoryError({ error })) - }) - return promise +export const setProvider = (provider) => (dispatch: React.Dispatch) => { + if (provider) { + dispatch(fetchProviderSuccess(provider)) + } else { + dispatch(fetchProviderError('No provider available')) + } } 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 b14760a782..e53648d28d 100644 --- a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx +++ b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx @@ -8,7 +8,7 @@ import { FileExplorerMenu } from './file-explorer-menu' // eslint-disable-line import { FileExplorerContextMenu } from './file-explorer-context-menu' // eslint-disable-line import { FileExplorerProps, File } from './types' import { fileSystemReducer, fileSystemInitialState } from './reducers/fileSystem' -import { fetchDirectory } from './actions/fileSystem' +import { fetchDirectory, setProvider, resolveDirectory } from './actions/fileSystem' import * as helper from '../../../../../apps/remix-ide/src/lib/helper' import QueryParams from '../../../../../apps/remix-ide/src/lib/query-params' @@ -17,7 +17,7 @@ import './css/file-explorer.css' const queryParams = new QueryParams() export const FileExplorer = (props: FileExplorerProps) => { - const { filesProvider, name, registry, plugin, focusRoot, contextMenuItems, displayInput, externalUploads } = props + const { name, registry, plugin, focusRoot, contextMenuItems, displayInput, externalUploads } = props const [state, setState] = useState({ focusElement: [{ key: '', @@ -26,10 +26,51 @@ export const FileExplorer = (props: FileExplorerProps) => { focusPath: null, files: [], fileManager: null, - filesProvider, ctrlKey: false, newFileName: '', - actions: [], + actions: [{ + id: 'newFile', + name: 'New File', + type: ['folder'], + path: [], + extension: [], + pattern: [] + }, { + id: 'newFolder', + name: 'New Folder', + type: ['folder'], + path: [], + extension: [], + pattern: [] + }, { + id: 'rename', + name: 'Rename', + type: ['file', 'folder'], + path: [], + extension: [], + pattern: [] + }, { + id: 'delete', + name: 'Delete', + type: ['file', 'folder'], + path: [], + extension: [], + pattern: [] + }, { + id: 'pushChangesToGist', + name: 'Push changes to gist', + type: [], + path: [], + extension: [], + pattern: ['^browser/gists/([0-9]|[a-z])*$'] + }, { + id: 'run', + name: 'Run', + type: [], + path: [], + extension: ['.js'], + pattern: [] + }], focusContext: { element: null, x: null, @@ -65,6 +106,20 @@ export const FileExplorer = (props: FileExplorerProps) => { const [fileSystem, dispatch] = useReducer(fileSystemReducer, fileSystemInitialState) const editRef = useRef(null) + useEffect(() => { + if (props.filesProvider) { + setProvider(props.filesProvider)(dispatch) + } + }, [props.filesProvider]) + + useEffect(() => { + const provider = fileSystem.provider.provider + + if (provider) { + fetchDirectory(provider, props.name)(dispatch) + } + }, [fileSystem.provider.provider]) + useEffect(() => { if (state.focusEdit.element) { setTimeout(() => { @@ -77,83 +132,21 @@ export const FileExplorer = (props: FileExplorerProps) => { useEffect(() => { (async () => { - await fetchDirectory(filesProvider, name)(dispatch) const fileManager = registry.get('filemanager').api - const actions = [{ - id: 'newFile', - name: 'New File', - type: ['folder'], - path: [], - extension: [], - pattern: [] - }, { - id: 'newFolder', - name: 'New Folder', - type: ['folder'], - path: [], - extension: [], - pattern: [] - }, { - id: 'rename', - name: 'Rename', - type: ['file', 'folder'], - path: [], - extension: [], - pattern: [] - }, { - id: 'delete', - name: 'Delete', - type: ['file', 'folder'], - path: [], - extension: [], - pattern: [] - }, { - id: 'pushChangesToGist', - name: 'Push changes to gist', - type: [], - path: [], - extension: [], - pattern: ['^browser/gists/([0-9]|[a-z])*$'] - }, { - id: 'run', - name: 'Run', - type: [], - path: [], - extension: ['.js'], - pattern: [] - }] setState(prevState => { - return { ...prevState, fileManager, actions, expandPath: [name] } + return { ...prevState, fileManager, expandPath: [name] } }) })() }, [name]) - useEffect(() => { - if (state.fileManager) { - filesProvider.event.register('fileExternallyChanged', fileExternallyChanged) - filesProvider.event.register('fileRenamedError', fileRenamedError) - filesProvider.event.register('rootFolderChanged', rootFolderChanged) - } - }, [state.fileManager]) - // useEffect(() => { - // const { expandPath } = state - // const expandFn = async () => { - // let files = state.files - - // for (let i = 0; i < expandPath.length; i++) { - // files = await resolveDirectory(expandPath[i], files) - // await setState(prevState => { - // return { ...prevState, files } - // }) - // } - // } - - // if (expandPath && expandPath.length > 0) { - // expandFn() + // if (state.fileManager) { + // filesProvider.event.register('fileExternallyChanged', fileExternallyChanged) + // filesProvider.event.register('fileRenamedError', fileRenamedError) + // filesProvider.event.register('rootFolderChanged', rootFolderChanged) // } - // }, [state.expandPath]) + // }, [state.fileManager]) // useEffect(() => { // // unregister event to update state in callback @@ -262,44 +255,6 @@ export const FileExplorer = (props: FileExplorerProps) => { // return dir // } - const fetchDirectoryContent = async (folderPath: string): Promise => { - console.log('folderPath: ', folderPath) - return new Promise((resolve) => { - filesProvider.resolveDirectory(folderPath, (_error, fileTree) => { - const files = normalize(fileTree) - - console.log('files: ', files) - resolve(files) - }) - }) - } - - const normalize = (filesList): File[] => { - const folders = [] - const files = [] - - Object.keys(filesList || {}).forEach(key => { - key = key.replace(/^\/|\/$/g, '') // remove first and last slash - let path = key - path = path.replace(/^\/|\/$/g, '') // remove first and last slash - - if (filesList[key].isDirectory) { - folders.push({ - path, - name: extractNameFromKey(path), - isDirectory: filesList[key].isDirectory - }) - } else { - files.push({ - path, - name: extractNameFromKey(path), - isDirectory: filesList[key].isDirectory - }) - } - }) - - return [...folders, ...files] - } const extractNameFromKey = (key: string):string => { const keyPath = key.split('/') @@ -771,6 +726,7 @@ export const FileExplorer = (props: FileExplorerProps) => { setState(prevState => { return { ...prevState, focusElement: [{ key: path, type: 'folder' }], expandPath } }) + resolveDirectory(path)(dispatch) } } @@ -1033,8 +989,8 @@ export const FileExplorer = (props: FileExplorerProps) => {
{ - fileSystem.files.files.map((file, index) => { - return renderFiles(file, index) + fileSystem.files.files[props.name] && Object.keys(fileSystem.files.files[props.name]).map((key, index) => { + return renderFiles(fileSystem.files.files[props.name][key], index) }) } diff --git a/libs/remix-ui/file-explorer/src/lib/reducers/fileSystem.ts b/libs/remix-ui/file-explorer/src/lib/reducers/fileSystem.ts index 7f39c28c2f..2f4a88331a 100644 --- a/libs/remix-ui/file-explorer/src/lib/reducers/fileSystem.ts +++ b/libs/remix-ui/file-explorer/src/lib/reducers/fileSystem.ts @@ -1,4 +1,4 @@ -import { extractNameFromKey, extractParentFromKey } from '../utils' +import { File } from '../types' interface Action { type: string; payload: Record; @@ -7,6 +7,7 @@ interface Action { export const fileSystemInitialState = { files: { files: [], + activeDirectory: {}, expandPath: [], isRequesting: false, isSuccessful: false, @@ -57,6 +58,41 @@ export const fileSystemReducer = (state = fileSystemInitialState, action: Action } } } + case 'RESOLVE_DIRECTORY_REQUEST': { + return { + ...state, + files: { + ...state.files, + isRequesting: true, + isSuccessful: false, + error: null + } + } + } + case 'RESOLVE_DIRECTORY_SUCCESS': { + return { + ...state, + files: { + ...state.files, + files: action.payload.files, + expandPath: [...state.files.expandPath, action.payload.path], + isRequesting: false, + isSuccessful: true, + error: null + } + } + } + case 'RESOLVE_DIRECTORY_ERROR': { + return { + ...state, + files: { + ...state.files, + isRequesting: false, + isSuccessful: false, + error: action.payload + } + } + } case 'FETCH_PROVIDER_REQUEST': { return { ...state, @@ -91,7 +127,45 @@ export const fileSystemReducer = (state = fileSystemInitialState, action: Action } } } + case 'ADD_EMPTY_FILE': { + return { + ...state, + files: { + ...state.files, + files: [] + } + } + } default: throw new Error() } } + +const addEmptyFile = (path: string, files: File[]): File[] => { + if (path === name) { + files.push({ + path: 'browser/blank', + name: '', + isDirectory: false + }) + return files + } + return files.map(file => { + if (file.child) { + if (file.path === path) { + file.child = [...file.child, { + path: file.path + '/blank', + name: '', + isDirectory: false + }] + return file + } else { + file.child = addEmptyFile(path, file.child) + + return file + } + } else { + return file + } + }) +}