Commit 39d47a4a authored by ioedeveloper's avatar ioedeveloper

create new file with plugin call

parent 825db4a6
...@@ -132,7 +132,10 @@ module.exports = class Filepanel extends ViewPlugin { ...@@ -132,7 +132,10 @@ module.exports = class Filepanel extends ViewPlugin {
} }
async createNewFile () { async createNewFile () {
return await this.request.createNewFile() const provider = this.fileManager.currentFileProvider()
const dir = provider.workspace || '/'
this.emit('displayNewFileInput', dir)
} }
async uploadFile (event) { async uploadFile (event) {
......
import React from 'react'
import { File } from '../types'
import { extractNameFromKey, extractParentFromKey } from '../utils'
const queuedEvents = []
const pendingEvents = {}
let provider = null
let plugin = null
let dispatch: React.Dispatch<any> = null
export const fetchDirectoryError = (error: any) => {
return {
type: 'FETCH_DIRECTORY_ERROR',
payload: error
}
}
export const fetchDirectoryRequest = (promise: Promise<any>) => {
return {
type: 'FETCH_DIRECTORY_REQUEST',
payload: promise
}
}
export const fetchDirectorySuccess = (path: string, files: File[]) => {
return {
type: 'FETCH_DIRECTORY_SUCCESS',
payload: { path, files }
}
}
export const fileSystemReset = () => {
return {
type: 'FILESYSTEM_RESET'
}
}
const normalize = (parent, filesList, newInputType?: string): any => {
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[extractNameFromKey(key)] = {
path,
name: extractNameFromKey(path).indexOf('gist-') === 0 ? extractNameFromKey(path).split('-')[1] : extractNameFromKey(path),
isDirectory: filesList[key].isDirectory,
type: extractNameFromKey(path).indexOf('gist-') === 0 ? 'gist' : 'folder'
}
} else {
files[extractNameFromKey(key)] = {
path,
name: extractNameFromKey(path),
isDirectory: filesList[key].isDirectory,
type: 'file'
}
}
})
if (newInputType === 'folder') {
const path = parent + '/blank'
folders[path] = {
path: path,
name: '',
isDirectory: true,
type: 'folder'
}
} else if (newInputType === 'file') {
const path = parent + '/blank'
files[path] = {
path: path,
name: '',
isDirectory: false,
type: 'file'
}
}
return Object.assign({}, folders, files)
}
const fetchDirectoryContent = async (provider, folderPath: string, newInputType?: string): Promise<any> => {
return new Promise((resolve) => {
provider.resolveDirectory(folderPath, (error, fileTree) => {
if (error) console.error(error)
const files = normalize(folderPath, fileTree, newInputType)
resolve({ [extractNameFromKey(folderPath)]: files })
})
})
}
export const fetchDirectory = (provider, path: string) => (dispatch: React.Dispatch<any>) => {
const promise = fetchDirectoryContent(provider, path)
dispatch(fetchDirectoryRequest(promise))
promise.then((files) => {
dispatch(fetchDirectorySuccess(path, files))
}).catch((error) => {
dispatch(fetchDirectoryError({ error }))
})
return promise
}
export const resolveDirectoryError = (error: any) => {
return {
type: 'RESOLVE_DIRECTORY_ERROR',
payload: error
}
}
export const resolveDirectoryRequest = (promise: Promise<any>) => {
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<any>) => {
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',
payload: error
}
}
export const fetchProviderRequest = (promise: Promise<any>) => {
return {
type: 'FETCH_PROVIDER_REQUEST',
payload: promise
}
}
export const fetchProviderSuccess = (provider: any) => {
return {
type: 'FETCH_PROVIDER_SUCCESS',
payload: provider
}
}
export const fileAddedSuccess = (path: string, files) => {
return {
type: 'FILE_ADDED',
payload: { path, files }
}
}
export const folderAddedSuccess = (path: string, files) => {
return {
type: 'FOLDER_ADDED',
payload: { path, files }
}
}
export const fileRemovedSuccess = (path: string, removePath: string) => {
return {
type: 'FILE_REMOVED',
payload: { path, removePath }
}
}
export const fileRenamedSuccess = (path: string, removePath: string, files) => {
return {
type: 'FILE_RENAMED',
payload: { path, removePath, files }
}
}
export const init = (fileProvider, filePanel, registry) => (reducerDispatch: React.Dispatch<any>) => {
provider = fileProvider
plugin = filePanel
dispatch = reducerDispatch
if (provider) {
provider.event.on('fileAdded', async (filePath) => {
await executeEvent('fileAdded', filePath)
})
provider.event.on('folderAdded', async (folderPath) => {
await executeEvent('folderAdded', folderPath)
})
provider.event.on('fileRemoved', async (removePath) => {
await executeEvent('fileRemoved', removePath)
})
provider.event.on('fileRenamed', async (oldPath) => {
await executeEvent('fileRenamed', oldPath)
})
provider.event.on('rootFolderChanged', async () => {
await executeEvent('rootFolderChanged')
})
provider.event.on('fileExternallyChanged', async (path: string, file: { content: string }) => {
const config = registry.get('config').api
const editor = registry.get('editor').api
if (config.get('currentFile') === path && editor.currentContent() !== file.content) {
if (provider.isReadOnly(path)) return editor.setText(file.content)
dispatch(displayNotification(
path + ' changed',
'This file has been changed outside of Remix IDE.',
'Replace by the new content', 'Keep the content displayed in Remix',
() => {
editor.setText(file.content)
}
))
}
})
provider.event.on('fileRenamedError', async () => {
dispatch(displayNotification('File Renamed Failed', '', 'Ok', 'Cancel'))
})
dispatch(fetchProviderSuccess(provider))
} else {
dispatch(fetchProviderError('No provider available'))
}
}
export const setCurrentWorkspace = (name: string) => {
return {
type: 'SET_CURRENT_WORKSPACE',
payload: name
}
}
export const addInputFieldSuccess = (path: string, files: File[]) => {
return {
type: 'ADD_INPUT_FIELD',
payload: { path, files }
}
}
export const addInputField = (provider, type: string, path: string) => (dispatch: React.Dispatch<any>) => {
const promise = fetchDirectoryContent(provider, path, type)
promise.then((files) => {
dispatch(addInputFieldSuccess(path, files))
}).catch((error) => {
console.error(error)
})
return promise
}
export const removeInputFieldSuccess = (path: string) => {
return {
type: 'REMOVE_INPUT_FIELD',
payload: { path }
}
}
export const removeInputField = (path: string) => (dispatch: React.Dispatch<any>) => {
return dispatch(removeInputFieldSuccess(path))
}
export 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 = () => {
return {
type: 'DISPLAY_NOTIFICATION'
}
}
export const closeNotificationModal = () => (dispatch: React.Dispatch<any>) => {
dispatch(hideNotification())
}
const fileAdded = async (filePath: string) => {
if (extractParentFromKey(filePath) === '/.workspaces') return
const path = extractParentFromKey(filePath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
await dispatch(fileAddedSuccess(path, data))
if (filePath.includes('_test.sol')) {
plugin.emit('newTestFileCreated', filePath)
}
}
const folderAdded = async (folderPath: string) => {
if (extractParentFromKey(folderPath) === '/.workspaces') return
const path = extractParentFromKey(folderPath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
await dispatch(folderAddedSuccess(path, data))
}
const fileRemoved = async (removePath: string) => {
const path = extractParentFromKey(removePath) || provider.workspace || provider.type || ''
await dispatch(fileRemovedSuccess(path, removePath))
}
const fileRenamed = async (oldPath: string) => {
const path = extractParentFromKey(oldPath) || provider.workspace || provider.type || ''
const data = await fetchDirectoryContent(provider, path)
await dispatch(fileRenamedSuccess(path, oldPath, data))
}
const rootFolderChanged = async () => {
const workspaceName = provider.workspace || provider.type || ''
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
}
}
...@@ -11,9 +11,10 @@ import { customAction } from '@remixproject/plugin-api/lib/file-system/file-pane ...@@ -11,9 +11,10 @@ import { customAction } from '@remixproject/plugin-api/lib/file-system/file-pane
import { contextMenuActions } from './utils' import { contextMenuActions } from './utils'
import './css/file-explorer.css' import './css/file-explorer.css'
import { extractParentFromKey } from '@remix-ui/helper'
export const FileExplorer = (props: FileExplorerProps) => { export const FileExplorer = (props: FileExplorerProps) => {
const { name, focusRoot, contextMenuItems, displayInput, externalUploads, removedContextMenuItems, resetFocus, files } = props const { name, focusRoot, contextMenuItems, externalUploads, removedContextMenuItems, resetFocus, files } = props
const [state, setState] = useState<FileExplorerState>({ const [state, setState] = useState<FileExplorerState>({
focusElement: [{ focusElement: [{
key: '', key: '',
...@@ -62,7 +63,7 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -62,7 +63,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (editRef && editRef.current) { if (editRef && editRef.current) {
editRef.current.focus() editRef.current.focus()
} }
}, 150) }, 0)
} }
}, [state.focusEdit.element]) }, [state.focusEdit.element])
...@@ -88,11 +89,12 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -88,11 +89,12 @@ export const FileExplorer = (props: FileExplorerProps) => {
}, [contextMenuItems]) }, [contextMenuItems])
useEffect(() => { useEffect(() => {
if (displayInput) { if (global.fs.focusEdit) {
handleNewFileInput() setState(prevState => {
plugin.resetNewFile() return { ...prevState, focusEdit: { element: global.fs.focusEdit, type: 'file', isNew: true, lastEdit: null } }
})
} }
}, [displayInput]) }, [global.fs.focusEdit])
useEffect(() => { useEffect(() => {
if (externalUploads) { if (externalUploads) {
...@@ -173,14 +175,6 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -173,14 +175,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
return keyPath[keyPath.length - 1] return keyPath[keyPath.length - 1]
} }
const extractParentFromKey = (key: string):string => {
if (!key) return
const keyPath = key.split('/')
keyPath.pop()
return keyPath.join('/')
}
const hasReservedKeyword = (content: string): boolean => { const hasReservedKeyword = (content: string): boolean => {
if (state.reservedKeywords.findIndex(value => content.startsWith(value)) !== -1) return true if (state.reservedKeywords.findIndex(value => content.startsWith(value)) !== -1) return true
else return false else return false
...@@ -196,22 +190,11 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -196,22 +190,11 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const createNewFile = async (newFilePath: string) => { const createNewFile = async (newFilePath: string) => {
const fileManager = state.fileManager
try { try {
const newName = await helper.createNonClashingNameAsync(newFilePath, fileManager) global.dispatchCreateNewFile(newFilePath, props.name)
const createFile = await fileManager.writeFile(newName, '') // setState(prevState => {
// return { ...prevState, focusElement: [{ key: newName, type: 'file' }] }
if (!createFile) { // })
return global.toast('Failed to create file ' + newName)
} else {
const path = newName.indexOf(props.name + '/') === 0 ? newName.replace(props.name + '/', '') : newName
await fileManager.open(path)
setState(prevState => {
return { ...prevState, focusElement: [{ key: newName, type: 'file' }] }
})
}
} catch (error) { } catch (error) {
return global.modal('File Creation Failed', typeof error === 'string' ? error : error.message, 'Close', async () => {}) return global.modal('File Creation Failed', typeof error === 'string' ? error : error.message, 'Close', async () => {})
} }
...@@ -536,12 +519,14 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -536,12 +519,14 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const label = (file: File) => { const label = (file: File) => {
const isEditable = (state.focusEdit.element === file.path) || (global.fs.focusEdit === file.path)
return ( return (
<div <div
className='remixui_items d-inline-block w-100' className='remixui_items d-inline-block w-100'
ref={state.focusEdit.element === file.path ? editRef : null} ref={ isEditable ? editRef : null}
suppressContentEditableWarning={true} suppressContentEditableWarning={true}
contentEditable={state.focusEdit.element === file.path} contentEditable={isEditable}
onKeyDown={handleEditInput} onKeyDown={handleEditInput}
onBlur={(e) => { onBlur={(e) => {
e.stopPropagation() e.stopPropagation()
......
import * as _ from 'lodash'
import { extractNameFromKey } from '../utils'
interface Action {
type: string;
payload: Record<string, any>;
}
export const fileSystemInitialState = {
files: {
files: [],
expandPath: [],
blankPath: null,
isRequesting: false,
isSuccessful: false,
error: null
},
provider: {
provider: null,
isRequesting: false,
isSuccessful: false,
error: null
},
notification: {
title: null,
message: null,
actionOk: () => {},
actionCancel: () => {},
labelOk: null,
labelCancel: null
}
}
export const fileSystemReducer = (state = fileSystemInitialState, action: Action) => {
switch (action.type) {
case 'FETCH_DIRECTORY_REQUEST': {
return {
...state,
files: {
...state.files,
isRequesting: true,
isSuccessful: false,
error: null
}
}
}
case 'FETCH_DIRECTORY_SUCCESS': {
return {
...state,
files: {
...state.files,
files: action.payload.files,
isRequesting: false,
isSuccessful: true,
error: null
}
}
}
case 'FETCH_DIRECTORY_ERROR': {
return {
...state,
files: {
...state.files,
isRequesting: false,
isSuccessful: false,
error: action.payload
}
}
}
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: resolveDirectory(state.provider.provider, action.payload.path, state.files.files, action.payload.files),
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,
provider: {
...state.provider,
isRequesting: true,
isSuccessful: false,
error: null
}
}
}
case 'FETCH_PROVIDER_SUCCESS': {
return {
...state,
provider: {
...state.provider,
provider: action.payload,
isRequesting: false,
isSuccessful: true,
error: null
}
}
}
case 'FETCH_PROVIDER_ERROR': {
return {
...state,
provider: {
...state.provider,
isRequesting: false,
isSuccessful: false,
error: action.payload
}
}
}
case 'ADD_INPUT_FIELD': {
return {
...state,
files: {
...state.files,
files: addInputField(state.provider.provider, action.payload.path, state.files.files, action.payload.files),
blankPath: action.payload.path,
isRequesting: false,
isSuccessful: true,
error: null
}
}
}
case 'REMOVE_INPUT_FIELD': {
return {
...state,
files: {
...state.files,
files: removeInputField(state.provider.provider, state.files.blankPath, state.files.files),
blankPath: null,
isRequesting: false,
isSuccessful: true,
error: null
}
}
}
case 'FILE_ADDED': {
return {
...state,
files: {
...state.files,
files: fileAdded(state.provider.provider, action.payload.path, state.files.files, action.payload.files),
expandPath: [...new Set([...state.files.expandPath, action.payload.path])],
isRequesting: false,
isSuccessful: true,
error: null
}
}
}
case 'FOLDER_ADDED': {
return {
...state,
files: {
...state.files,
files: folderAdded(state.provider.provider, action.payload.path, state.files.files, action.payload.files),
expandPath: [...new Set([...state.files.expandPath, action.payload.path])],
isRequesting: false,
isSuccessful: true,
error: null
}
}
}
case 'FILE_REMOVED': {
return {
...state,
files: {
...state.files,
files: fileRemoved(state.provider.provider, action.payload.path, action.payload.removePath, state.files.files),
isRequesting: false,
isSuccessful: true,
error: null
}
}
}
case 'FILE_RENAMED': {
return {
...state,
files: {
...state.files,
files: fileRenamed(state.provider.provider, action.payload.path, action.payload.removePath, state.files.files, action.payload.files),
isRequesting: false,
isSuccessful: true,
error: null
}
}
}
case 'DISPLAY_NOTIFICATION': {
return {
...state,
notification: {
title: action.payload.title,
message: action.payload.message,
actionOk: action.payload.actionOk || fileSystemInitialState.notification.actionOk,
actionCancel: action.payload.actionCancel || fileSystemInitialState.notification.actionCancel,
labelOk: action.payload.labelOk,
labelCancel: action.payload.labelCancel
}
}
}
case 'HIDE_NOTIFICATION': {
return {
...state,
notification: fileSystemInitialState.notification
}
}
default:
throw new Error()
}
}
const resolveDirectory = (provider, path: string, files, content) => {
const root = provider.workspace || provider.type
if (path === root) return { [root]: { ...content[root], ...files[root] } }
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]
}, [])
const prevFiles = _.get(files, _path)
files = _.set(files, _path, {
isDirectory: true,
path,
name: extractNameFromKey(path).indexOf('gist-') === 0 ? extractNameFromKey(path).split('-')[1] : extractNameFromKey(path),
type: extractNameFromKey(path).indexOf('gist-') === 0 ? 'gist' : 'folder',
child: { ...content[pathArr[pathArr.length - 1]], ...(prevFiles ? prevFiles.child : {}) }
})
return files
}
const removePath = (root, path: string, pathName, files) => {
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]
}, [])
const prevFiles = _.get(files, _path)
if (prevFiles) {
prevFiles.child && prevFiles.child[pathName] && delete prevFiles.child[pathName]
files = _.set(files, _path, {
isDirectory: true,
path,
name: extractNameFromKey(path).indexOf('gist-') === 0 ? extractNameFromKey(path).split('-')[1] : extractNameFromKey(path),
type: extractNameFromKey(path).indexOf('gist-') === 0 ? 'gist' : 'folder',
child: prevFiles ? prevFiles.child : {}
})
}
return files
}
const addInputField = (provider, path: string, files, content) => {
const root = provider.workspace || provider.type || ''
if (path === root) return { [root]: { ...content[root], ...files[root] } }
const result = resolveDirectory(provider, path, files, content)
return result
}
const removeInputField = (provider, path: string, files) => {
const root = provider.workspace || provider.type || ''
if (path === root) {
delete files[root][path + '/' + 'blank']
return files
}
return removePath(root, path, path + '/' + 'blank', files)
}
const fileAdded = (provider, path: string, files, content) => {
return resolveDirectory(provider, path, files, content)
}
const folderAdded = (provider, path: string, files, content) => {
return resolveDirectory(provider, path, files, content)
}
const fileRemoved = (provider, path: string, removedPath: string, files) => {
const root = provider.workspace || provider.type || ''
if (path === root) {
delete files[root][removedPath]
return files
}
return removePath(root, path, extractNameFromKey(removedPath), files)
}
const fileRenamed = (provider, path: string, removePath: string, files, content) => {
const root = provider.workspace || provider.type || ''
if (path === root) {
const allFiles = { [root]: { ...content[root], ...files[root] } }
delete allFiles[root][extractNameFromKey(removePath) || removePath]
return allFiles
}
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]
}, [])
const prevFiles = _.get(files, _path)
delete prevFiles.child[extractNameFromKey(removePath)]
files = _.set(files, _path, {
isDirectory: true,
path,
name: extractNameFromKey(path).indexOf('gist-') === 0 ? extractNameFromKey(path).split('-')[1] : extractNameFromKey(path),
type: extractNameFromKey(path).indexOf('gist-') === 0 ? 'gist' : 'folder',
child: { ...content[pathArr[pathArr.length - 1]], ...prevFiles.child }
})
return files
}
...@@ -19,3 +19,25 @@ export const checkSpecialChars = (name: string) => { ...@@ -19,3 +19,25 @@ export const checkSpecialChars = (name: string) => {
export const checkSlash = (name: string) => { export const checkSlash = (name: string) => {
return name.match(/\//) != null return name.match(/\//) != null
} }
export const createNonClashingNameAsync = async (name, fileManager, prefix = '') => {
if (!name) name = 'Undefined'
let counter
let ext = 'sol'
const reg = /(.*)\.([^.]+)/g
const split = reg.exec(name)
if (split) {
name = split[1]
ext = split[2]
}
let exist = true
do {
const isDuplicate = await fileManager.exists(name + counter + prefix + '.' + ext)
if (isDuplicate) counter = (counter | 0) + 1
else exist = false
} while (exist)
return name + counter + prefix + '.' + ext
}
import React from 'react' import React from 'react'
import { bufferToHex, keccakFromString } from 'ethereumjs-util' import { bufferToHex, keccakFromString } from 'ethereumjs-util'
import axios, { AxiosResponse } from 'axios' import axios, { AxiosResponse } from 'axios'
import { checkSpecialChars, checkSlash, extractParentFromKey, extractNameFromKey } from '@remix-ui/helper' import { checkSpecialChars, checkSlash, extractParentFromKey, extractNameFromKey, createNonClashingNameAsync } from '@remix-ui/helper'
import Gists from 'gists' import Gists from 'gists'
const QueryParams = require('../../../../../../apps/remix-ide/src/lib/query-params') const QueryParams = require('../../../../../../apps/remix-ide/src/lib/query-params')
...@@ -421,7 +421,6 @@ const listenOnEvents = (provider) => { ...@@ -421,7 +421,6 @@ const listenOnEvents = (provider) => {
}) })
provider.event.on('fileRenamed', async (oldPath: string, newPath: string) => { provider.event.on('fileRenamed', async (oldPath: string, newPath: string) => {
console.log('oldPath: ', oldPath, 'newPath: ', newPath)
await executeEvent('fileRenamed', oldPath, newPath) await executeEvent('fileRenamed', oldPath, newPath)
}) })
...@@ -469,9 +468,14 @@ const listenOnEvents = (provider) => { ...@@ -469,9 +468,14 @@ const listenOnEvents = (provider) => {
)) ))
} }
}) })
provider.event.on('fileRenamedError', async () => { provider.event.on('fileRenamedError', async () => {
dispatch(displayNotification('File Renamed Failed', '', 'Ok', 'Cancel')) dispatch(displayNotification('File Renamed Failed', '', 'Ok', 'Cancel'))
}) })
plugin.on('filePanel', 'displayNewFileInput', (path) => {
addInputField('file', path)(dispatch)
})
} }
export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch<any>) => { export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch<any>) => {
...@@ -750,6 +754,20 @@ export const uploadFile = (target, targetFolder: string) => async (dispatch: Rea ...@@ -750,6 +754,20 @@ export const uploadFile = (target, targetFolder: string) => async (dispatch: Rea
}) })
} }
export const createNewFile = (path: string, rootDir: string) => async (dispatch: React.Dispatch<any>) => {
const fileManager = plugin.fileManager
const newName = await createNonClashingNameAsync(path, fileManager)
const createFile = await fileManager.writeFile(newName, '')
if (!createFile) {
return dispatch(displayPopUp('Failed to create file ' + newName))
} else {
const path = newName.indexOf(rootDir + '/') === 0 ? newName.replace(rootDir + '/', '') : newName
await fileManager.open(path)
}
}
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')) {
......
...@@ -15,5 +15,6 @@ export const FileSystemContext = createContext<{ ...@@ -15,5 +15,6 @@ export const FileSystemContext = createContext<{
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> dispatchUploadFile: (target?: SyntheticEvent, targetFolder?: string) => Promise<void>,
dispatchCreateNewFile: (path: string, rootDir: string) => Promise<void>
}>(null) }>(null)
...@@ -5,7 +5,7 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line ...@@ -5,7 +5,7 @@ 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, uploadFile } from '../actions/workspace' import { initWorkspace, fetchDirectory, addInputField, removeInputField, createWorkspace, fetchWorkspaceDirectory, switchToWorkspace, renameWorkspace, deleteWorkspace, clearPopUp, publishToGist, uploadFile, createNewFile } 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'
...@@ -70,6 +70,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => { ...@@ -70,6 +70,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await uploadFile(target, targetFolder)(fsDispatch) await uploadFile(target, targetFolder)(fsDispatch)
} }
const dispatchCreateNewFile = async (path: string, rootDir: string) => {
await createNewFile(path, rootDir)(fsDispatch)
}
useEffect(() => { useEffect(() => {
if (modals.length > 0) { if (modals.length > 0) {
setFocusModal(() => { setFocusModal(() => {
...@@ -154,7 +158,8 @@ export const FileSystemProvider = (props: WorkspaceProps) => { ...@@ -154,7 +158,8 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchRenameWorkspace, dispatchRenameWorkspace,
dispatchDeleteWorkspace, dispatchDeleteWorkspace,
dispatchPublishToGist, dispatchPublishToGist,
dispatchUploadFile dispatchUploadFile,
dispatchCreateNewFile
} }
return ( return (
<FileSystemContext.Provider value={value}> <FileSystemContext.Provider value={value}>
......
...@@ -32,7 +32,8 @@ export interface BrowserState { ...@@ -32,7 +32,8 @@ export interface BrowserState {
labelCancel: string labelCancel: string
}, },
readonly: boolean, readonly: boolean,
popup: string popup: string,
focusEdit: string
} }
export const browserInitialState: BrowserState = { export const browserInitialState: BrowserState = {
...@@ -63,7 +64,8 @@ export const browserInitialState: BrowserState = { ...@@ -63,7 +64,8 @@ export const browserInitialState: BrowserState = {
labelCancel: '' labelCancel: ''
}, },
readonly: false, readonly: false,
popup: '' popup: '',
focusEdit: ''
} }
export const browserReducer = (state = browserInitialState, action: Action) => { export const browserReducer = (state = browserInitialState, action: Action) => {
...@@ -320,7 +322,8 @@ export const browserReducer = (state = browserInitialState, action: Action) => { ...@@ -320,7 +322,8 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
localhost: { localhost: {
...state.localhost, ...state.localhost,
files: state.mode === 'localhost' ? fetchDirectoryContent(state, payload) : state.localhost.files files: state.mode === 'localhost' ? fetchDirectoryContent(state, payload) : state.localhost.files
} },
focusEdit: payload.path + '/' + 'blank'
} }
} }
...@@ -336,7 +339,8 @@ export const browserReducer = (state = browserInitialState, action: Action) => { ...@@ -336,7 +339,8 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
localhost: { localhost: {
...state.localhost, ...state.localhost,
files: state.mode === 'localhost' ? fetchDirectoryContent(state, payload, payload.path + '/' + 'blank') : state.localhost.files files: state.mode === 'localhost' ? fetchDirectoryContent(state, payload, payload.path + '/' + 'blank') : state.localhost.files
} },
focusEdit: null
} }
} }
......
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