Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
B
baas-ide
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
guxukai
baas-ide
Commits
39d47a4a
Commit
39d47a4a
authored
Sep 16, 2021
by
ioedeveloper
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
create new file with plugin call
parent
825db4a6
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
79 additions
and
773 deletions
+79
-773
file-panel.js
apps/remix-ide/src/app/panels/file-panel.js
+4
-1
fileSystem.ts
libs/remix-ui/file-explorer/src/lib/actions/fileSystem.ts
+0
-384
file-explorer.tsx
libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
+16
-31
fileSystem.ts
libs/remix-ui/file-explorer/src/lib/reducers/fileSystem.ts
+0
-348
remix-ui-helper.ts
libs/remix-ui/helper/src/lib/remix-ui-helper.ts
+22
-0
workspace.ts
libs/remix-ui/workspace/src/lib/actions/workspace.ts
+20
-2
index.ts
libs/remix-ui/workspace/src/lib/contexts/index.ts
+2
-1
FileSystemProvider.tsx
...mix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
+7
-2
workspace.ts
libs/remix-ui/workspace/src/lib/reducers/workspace.ts
+8
-4
No files found.
apps/remix-ide/src/app/panels/file-panel.js
View file @
39d47a4a
...
@@ -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
)
{
...
...
libs/remix-ui/file-explorer/src/lib/actions/fileSystem.ts
deleted
100644 → 0
View file @
825db4a6
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
}
}
libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
View file @
39d47a4a
...
@@ -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
()
}
}
},
15
0
)
},
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
}
}
})
}
}
},
[
displayInpu
t
])
},
[
global
.
fs
.
focusEdi
t
])
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
()
...
...
libs/remix-ui/file-explorer/src/lib/reducers/fileSystem.ts
deleted
100644 → 0
View file @
825db4a6
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
}
libs/remix-ui/helper/src/lib/remix-ui-helper.ts
View file @
39d47a4a
...
@@ -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
}
libs/remix-ui/workspace/src/lib/actions/workspace.ts
View file @
39d47a4a
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'
))
{
...
...
libs/remix-ui/workspace/src/lib/contexts/index.ts
View file @
39d47a4a
...
@@ -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
)
libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
View file @
39d47a4a
...
@@ -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
}
>
...
...
libs/remix-ui/workspace/src/lib/reducers/workspace.ts
View file @
39d47a4a
...
@@ -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
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment