Commit 825db4a6 authored by ioedeveloper's avatar ioedeveloper

Upload file

parent f7db2794
...@@ -13,13 +13,12 @@ import { contextMenuActions } from './utils' ...@@ -13,13 +13,12 @@ import { contextMenuActions } from './utils'
import './css/file-explorer.css' import './css/file-explorer.css'
export const FileExplorer = (props: FileExplorerProps) => { export const FileExplorer = (props: FileExplorerProps) => {
const { name, plugin, focusRoot, contextMenuItems, displayInput, externalUploads, removedContextMenuItems, resetFocus, files } = props const { name, focusRoot, contextMenuItems, displayInput, externalUploads, removedContextMenuItems, resetFocus, files } = props
const [state, setState] = useState<FileExplorerState>({ const [state, setState] = useState<FileExplorerState>({
focusElement: [{ focusElement: [{
key: '', key: '',
type: 'folder' type: 'folder'
}], }],
fileManager: null,
ctrlKey: false, ctrlKey: false,
newFileName: '', newFileName: '',
actions: contextMenuActions, actions: contextMenuActions,
...@@ -36,7 +35,6 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -36,7 +35,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
lastEdit: '' lastEdit: ''
}, },
expandPath: [name], expandPath: [name],
toasterMsg: '',
mouseOverElement: null, mouseOverElement: null,
showContextMenu: false, showContextMenu: false,
reservedKeywords: [name, 'gist-'], reservedKeywords: [name, 'gist-'],
...@@ -205,7 +203,7 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -205,7 +203,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
const createFile = await fileManager.writeFile(newName, '') const createFile = await fileManager.writeFile(newName, '')
if (!createFile) { if (!createFile) {
return toast('Failed to create file ' + newName) return global.toast('Failed to create file ' + newName)
} else { } else {
const path = newName.indexOf(props.name + '/') === 0 ? newName.replace(props.name + '/', '') : newName const path = newName.indexOf(props.name + '/') === 0 ? newName.replace(props.name + '/', '') : newName
...@@ -239,7 +237,7 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -239,7 +237,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const deletePath = async (path: string | string[]) => { const deletePath = async (path: string | string[]) => {
if (global.fs.readonly) return toast('cannot delete file. ' + name + ' is a read only explorer') if (global.fs.readonly) return global.toast('cannot delete file. ' + name + ' is a read only explorer')
if (!Array.isArray(path)) path = [path] if (!Array.isArray(path)) path = [path]
global.modal(`Delete ${path.length > 1 ? 'items' : 'item'}`, deleteMessage(path), 'OK', async () => { global.modal(`Delete ${path.length > 1 ? 'items' : 'item'}`, deleteMessage(path), 'OK', async () => {
...@@ -251,7 +249,7 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -251,7 +249,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
} catch (e) { } catch (e) {
const isDir = await state.fileManager.isDirectory(p) const isDir = await state.fileManager.isDirectory(p)
toast(`Failed to remove ${isDir ? 'folder' : 'file'} ${p}.`) global.toast(`Failed to remove ${isDir ? 'folder' : 'file'} ${p}.`)
} }
} }
}, 'Cancel', () => {}) }, 'Cancel', () => {})
...@@ -273,55 +271,13 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -273,55 +271,13 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const uploadFile = (target) => { const uploadFile = (target) => {
const filesProvider = fileSystem.provider.provider
// TODO The file explorer is merely a view on the current state of
// the files module. Please ask the user here if they want to overwrite
// a file and then just use `files.add`. The file explorer will
// pick that up via the 'fileAdded' event from the files module.
const parentFolder = getFocusedFolder() const parentFolder = getFocusedFolder()
const expandPath = [...new Set([...state.expandPath, parentFolder])] const expandPath = [...new Set([...state.expandPath, parentFolder])]
setState(prevState => { setState(prevState => {
return { ...prevState, expandPath } return { ...prevState, expandPath }
});
[...target.files].forEach((file) => {
const loadFile = (name: string): void => {
const fileReader = new FileReader()
fileReader.onload = async function (event) {
if (helper.checkSpecialChars(file.name)) {
global.modal('File Upload Failed', 'Special characters are not allowed', 'Close', async () => {})
return
}
const success = await filesProvider.set(name, event.target.result)
if (!success) {
return global.modal('File Upload Failed', 'Failed to create file ' + name, 'Close', async () => {})
}
const config = registry.get('config').api
const editor = registry.get('editor').api
if ((config.get('currentFile') === name) && (editor.currentContent() !== event.target.result)) {
editor.setText(event.target.result)
}
}
fileReader.readAsText(file)
}
const name = `${parentFolder}/${file.name}`
filesProvider.exists(name).then(exist => {
if (!exist) {
loadFile(name)
} else {
global.modal('Confirm overwrite', `The file ${name} already exists! Would you like to overwrite it?`, 'OK', () => {
loadFile(name)
}, 'Cancel', () => {})
}
}).catch(error => {
if (error) console.log(error)
})
}) })
global.dispatchUploadFile(target, parentFolder)
} }
const copyFile = (src: string, dest: string) => { const copyFile = (src: string, dest: string) => {
...@@ -377,12 +333,6 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -377,12 +333,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
plugin.call(cmd.id, cmd.name, cmd) plugin.call(cmd.id, cmd.name, cmd)
} }
const toast = (message: string) => {
setState(prevState => {
return { ...prevState, toasterMsg: message }
})
}
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 path = path.indexOf(props.name + '/') === 0 ? path.replace(props.name + '/', '') : path
if (!state.ctrlKey) { if (!state.ctrlKey) {
...@@ -564,7 +514,7 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -564,7 +514,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
return { ...prevState, copyElement: [{ key: path, type }] } return { ...prevState, copyElement: [{ key: path, type }] }
}) })
setCanPaste(true) setCanPaste(true)
toast(`Copied to clipboard ${path}`) global.toast(`Copied to clipboard ${path}`)
} }
const handlePasteClick = (dest: string, destType: string) => { const handlePasteClick = (dest: string, destType: string) => {
...@@ -735,7 +685,6 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -735,7 +685,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
</div> </div>
</TreeViewItem> </TreeViewItem>
</TreeView> </TreeView>
<Toaster message={state.toasterMsg} />
{ state.showContextMenu && { state.showContextMenu &&
<FileExplorerContextMenu <FileExplorerContextMenu
actions={state.focusElement.length > 1 ? state.actions.filter(item => item.multiselect) : state.actions.filter(item => !item.multiselect)} actions={state.focusElement.length > 1 ? state.actions.filter(item => item.multiselect) : state.actions.filter(item => !item.multiselect)}
......
...@@ -5,7 +5,6 @@ export type MenuItems = action[] // eslint-disable-line no-use-before-define ...@@ -5,7 +5,6 @@ export type MenuItems = action[] // eslint-disable-line no-use-before-define
export interface FileExplorerProps { export interface FileExplorerProps {
name: string, name: string,
menuItems?: string[], menuItems?: string[],
plugin: any,
focusRoot: boolean, focusRoot: boolean,
contextMenuItems: MenuItems, contextMenuItems: MenuItems,
removedContextMenuItems: MenuItems, removedContextMenuItems: MenuItems,
...@@ -63,7 +62,6 @@ export interface FileExplorerState { ...@@ -63,7 +62,6 @@ export interface FileExplorerState {
key: string key: string
type: 'folder' | 'file' | 'gist' type: 'folder' | 'file' | 'gist'
}[] }[]
fileManager: any
ctrlKey: boolean ctrlKey: boolean
newFileName: string newFileName: string
actions: { actions: {
...@@ -89,7 +87,6 @@ export interface FileExplorerState { ...@@ -89,7 +87,6 @@ export interface FileExplorerState {
lastEdit: string lastEdit: string
} }
expandPath: string[] expandPath: string[]
toasterMsg: string
mouseOverElement: string mouseOverElement: string
showContextMenu: boolean showContextMenu: boolean
reservedKeywords: string[] reservedKeywords: string[]
......
...@@ -254,7 +254,7 @@ const createWorkspaceTemplate = async (workspaceName: string, setDefaults = true ...@@ -254,7 +254,7 @@ const createWorkspaceTemplate = async (workspaceName: string, setDefaults = true
provider.lastLoadedGistId = gistId provider.lastLoadedGistId = gistId
} else { } else {
displayNotification('', errorLoadingFile.message || errorLoadingFile, 'OK', null, () => {}, null) dispatch(displayNotification('', errorLoadingFile.message || errorLoadingFile, 'OK', null, () => {}, null))
} }
}) })
} catch (e) { } catch (e) {
...@@ -697,7 +697,7 @@ export const publishToGist = (path?: string, type?: string) => async (dispatch: ...@@ -697,7 +697,7 @@ export const publishToGist = (path?: string, type?: string) => async (dispatch:
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error)
displayNotification('Publish to gist Failed', 'Failed to create gist: ' + error.message, 'Close', null, async () => {}) dispatch(displayNotification('Publish to gist Failed', 'Failed to create gist: ' + error.message, 'Close', null, async () => {}))
} }
} }
...@@ -705,6 +705,51 @@ export const clearPopUp = () => async (dispatch: React.Dispatch<any>) => { ...@@ -705,6 +705,51 @@ export const clearPopUp = () => async (dispatch: React.Dispatch<any>) => {
dispatch(hidePopUp()) dispatch(hidePopUp())
} }
export const uploadFile = (target, targetFolder: string) => async (dispatch: React.Dispatch<any>) => {
// TODO The file explorer is merely a view on the current state of
// the files module. Please ask the user here if they want to overwrite
// a file and then just use `files.add`. The file explorer will
// pick that up via the 'fileAdded' event from the files module.
[...target.files].forEach((file) => {
const workspaceProvider = plugin.fileProviders.workspace
const loadFile = (name: string): void => {
const fileReader = new FileReader()
fileReader.onload = async function (event) {
if (checkSpecialChars(file.name)) {
dispatch(displayNotification('File Upload Failed', 'Special characters are not allowed', 'Close', null, async () => {}))
return
}
const success = await workspaceProvider.set(name, event.target.result)
if (!success) {
return dispatch(displayNotification('File Upload Failed', 'Failed to create file ' + name, 'Close', null, async () => {}))
}
const config = plugin.registry.get('config').api
const editor = plugin.registry.get('editor').api
if ((config.get('currentFile') === name) && (editor.currentContent() !== event.target.result)) {
editor.setText(event.target.result)
}
}
fileReader.readAsText(file)
}
const name = `${targetFolder}/${file.name}`
workspaceProvider.exists(name).then(exist => {
if (!exist) {
loadFile(name)
} else {
dispatch(displayNotification('Confirm overwrite', `The file ${name} already exists! Would you like to overwrite it?`, 'OK', null, () => {
loadFile(name)
}, () => {}))
}
}).catch(error => {
if (error) console.log(error)
})
})
}
const fileAdded = async (filePath: string) => { const fileAdded = async (filePath: string) => {
await dispatch(fileAddedSuccess(filePath)) await dispatch(fileAddedSuccess(filePath))
if (filePath.includes('_test.sol')) { if (filePath.includes('_test.sol')) {
......
import { createContext } from 'react' import { createContext, SyntheticEvent } from 'react'
import { BrowserState } from '../reducers/workspace' import { BrowserState } from '../reducers/workspace'
export const FileSystemContext = createContext<{ export const FileSystemContext = createContext<{
...@@ -14,5 +14,6 @@ export const FileSystemContext = createContext<{ ...@@ -14,5 +14,6 @@ export const FileSystemContext = createContext<{
dispatchSwitchToWorkspace: (name: string) => Promise<void>, dispatchSwitchToWorkspace: (name: string) => Promise<void>,
dispatchRenameWorkspace: (oldName: string, workspaceName: string) => Promise<void>, dispatchRenameWorkspace: (oldName: string, workspaceName: string) => Promise<void>,
dispatchDeleteWorkspace: (workspaceName: string) => Promise<void>, dispatchDeleteWorkspace: (workspaceName: string) => Promise<void>,
dispatchPublishToGist: (path?: string, type?: string) => Promise<void> dispatchPublishToGist: (path?: string, type?: string) => Promise<void>,
dispatchUploadFile: (target?: SyntheticEvent, targetFolder?: string) => Promise<void>
}>(null) }>(null)
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import React, { useReducer, useState, useEffect } from 'react' import React, { useReducer, useState, useEffect, SyntheticEvent } from 'react'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileSystemContext } from '../contexts' import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace' import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, addInputField, removeInputField, createWorkspace, fetchWorkspaceDirectory, switchToWorkspace, renameWorkspace, deleteWorkspace, clearPopUp, publishToGist } from '../actions/workspace' import { initWorkspace, fetchDirectory, addInputField, removeInputField, createWorkspace, fetchWorkspaceDirectory, switchToWorkspace, renameWorkspace, deleteWorkspace, clearPopUp, publishToGist, uploadFile } from '../actions/workspace'
import { Modal, WorkspaceProps } from '../types' import { Modal, WorkspaceProps } from '../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Workspace } from '../remix-ui-workspace' import { Workspace } from '../remix-ui-workspace'
...@@ -66,6 +66,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => { ...@@ -66,6 +66,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await publishToGist(path, type)(fsDispatch) await publishToGist(path, type)(fsDispatch)
} }
const dispatchUploadFile = async (target?: SyntheticEvent, targetFolder?: string) => {
await uploadFile(target, targetFolder)(fsDispatch)
}
useEffect(() => { useEffect(() => {
if (modals.length > 0) { if (modals.length > 0) {
setFocusModal(() => { setFocusModal(() => {
...@@ -149,7 +153,8 @@ export const FileSystemProvider = (props: WorkspaceProps) => { ...@@ -149,7 +153,8 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchSwitchToWorkspace, dispatchSwitchToWorkspace,
dispatchRenameWorkspace, dispatchRenameWorkspace,
dispatchDeleteWorkspace, dispatchDeleteWorkspace,
dispatchPublishToGist dispatchPublishToGist,
dispatchUploadFile
} }
return ( return (
<FileSystemContext.Provider value={value}> <FileSystemContext.Provider value={value}>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment