Merge pull request #3504 from ethereum/DownloadWs

Download Workspace
pull/5370/head
yann300 2 years ago committed by GitHub
commit 2c5f997b26
  1. 29
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  2. 4
      apps/remix-ide/src/app/tabs/locales/en/filePanel.json
  3. 3
      libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx
  4. 15
      libs/remix-ui/workspace/src/lib/actions/index.ts
  5. 24
      libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx
  6. 1
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  7. 7
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  8. 15
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx

@ -408,14 +408,10 @@ module.exports = {
'Should rename a workspace #group1': function (browser: NightwatchBrowser) {
browser
.useXpath()
.waitForElementPresent({
selector: '//i[@data-id="workspaceDropdownMenuIcon"]',
locateStrategy: 'xpath',
})
.click('//*[@id="workspacesMenuDropdown"]/span/i')
.waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul')
.click('//*[@id="workspacesMenuDropdown"]/div/ul/a[4]') // rename workspace_name
.waitForElementPresent('*[data-id="workspaceDropdownMenuIcon"]')
.click('*[data-id="workspaceDropdownMenuIcon"]')
.waitForElementVisible('*[data-id="wsdropdownMenu"]')
.click('*[data-id="workspacerename"]') // rename workspace_name
.useCss()
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextRename"]')
@ -437,15 +433,14 @@ module.exports = {
'Should delete a workspace #group1': function (browser: NightwatchBrowser) {
browser
.switchWorkspace('workspace_name_1')//*[@id="workspacesMenuDropdown"]/span
.useXpath()
.click('//*[@id="workspacesMenuDropdown"]/span/i')
.click('//*[@id="workspacesMenuDropdown"]/div/ul/a[2]') // delete workspace_name_1
.waitForElementVisible('//*[@id="fileExplorerView"]/div[2]/div/div/div[2]')
.click('//*[@id="fileExplorerView"]/div[2]/div/div/div[3]/button')
.waitForElementVisible('//*[@id="workspacesSelect"]')
.click('//*[@id="workspacesSelect"]')
.useCss()
.switchWorkspace('workspace_name_1')
.click('*[data-id="workspaceDropdownMenuIcon"]')
.waitForElementVisible('*[data-id="wsdropdownMenu"]')
.click('*[data-id="workspacedelete"]') // delete workspace_name_1
.waitForElementVisible('*[data-id="fileSystemModalDialogModalFooter-react"]')
.click('*[data-id="fileSystem-modal-footer-ok-react"]')
.waitForElementVisible('*[data-id="workspacesSelect"]')
.click('*[data-id="workspacesSelect"]')
.waitForElementNotPresent(`[data-id="dropdown-item-workspace_name_1"]`)
.end()
},

@ -10,12 +10,14 @@
"filePanel.workspace.rename": "Rename Workspace",
"filePanel.workspace.delete": "Delete Workspace",
"filePanel.workspace.deleteConfirm": "Are you sure to delete the current workspace?",
"filePanel.workspace.download": "Download Workspace",
"filePanel.workspace.downloadConfirm": "This will download current workspace in a zip file. Do you want to continue?",
"filePanel.workspace.deleteAll": "Delete All Workspaces",
"filePanel.workspace.deleteAllConfirm1": "Are you absolutely sure you want to delete all your workspaces?",
"filePanel.workspace.deleteAllConfirm2": "Deleted workspaces can not be restored in any manner.",
"filePanel.workspace.name": "Workspace name",
"filePanel.workspace.chooseTemplate": "Choose a template",
"filePanel.workspace.backup": "Backup Workspaces",
"filePanel.workspace.backup": "Backup All Workspaces",
"filePanel.workspace.restore": "Restore Workspaces from the Backup",
"filePanel.workspace.clone": "Clone Git Repository",
"filePanel.workspace.cloneMessage": "Please provide a valid git repository url.",

@ -37,7 +37,7 @@ export const CustomIconsToggle = React.forwardRef(({ onClick, icon, className =
// forwardRef again here!
// Dropdown needs access to the DOM of the Menu to measure it
export const CustomMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, className: string, 'aria-labelledby'?: string }, ref: Ref<HTMLDivElement>) => {
({ children, style, 'data-id': dataId, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, 'data-id'?: string, className: string, 'aria-labelledby'?: string }, ref: Ref<HTMLDivElement>) => {
const height = window.innerHeight * 0.6
return (
<div
@ -45,6 +45,7 @@ export const CustomMenu = React.forwardRef(
style={style}
className={className}
aria-labelledby={labeledBy}
data-id={dataId}
>
<ul className="overflow-auto list-unstyled mb-0" style={{ maxHeight: height+'px' }}>
{

@ -376,6 +376,21 @@ export const handleDownloadFiles = async () => {
}
}
export const handleDownloadWorkspace = async () => {
try {
const zip = new JSZip()
const workspaceProvider = plugin.fileProviders.workspace
await workspaceProvider.copyFolderToJson('/', ({ path, content }) => {
zip.file(path, content)
})
const blob = await zip.generateAsync({ type: 'blob' })
saveAs(blob, `${workspaceProvider.workspace}.zip`)
} catch (e) {
console.error(e)
plugin.call('notification', 'toast', e.message)
}
}
export const restoreBackupZip = async () => {
await plugin.appManager.activatePlugin(['restorebackupzip'])
await plugin.call('mainPanel', 'showContent', 'restorebackupzip')

@ -4,9 +4,10 @@ import { HamburgerMenuItem } from './workspace-hamburger-item'
export interface HamburgerMenuProps {
createWorkspace: () => void,
renameCurrentWorkspace: () => void,
downloadCurrentWorkspace: () => void,
deleteCurrentWorkspace: () => void,
deleteAllWorkspaces: () => void,
renameCurrentWorkspace: () => void,
cloneGitRepository: () => void,
downloadWorkspaces: () => void,
restoreBackup: () => void,
@ -27,24 +28,27 @@ export function HamburgerMenu (props: HamburgerMenuProps) {
props.createWorkspace()
props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem>
<HamburgerMenuItem kind='delete' fa='far fa-trash' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
props.deleteCurrentWorkspace()
props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem>
<HamburgerMenuItem kind='deleteAll' fa='far fa-trash-alt' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
props.deleteAllWorkspaces()
<HamburgerMenuItem kind='clone' fa='fab fa-github' hideOption={hideWorkspaceOptions} actionOnClick={() => {
props.cloneGitRepository()
props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem>
<HamburgerMenuItem kind='rename' fa='far fa-edit' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
props.renameCurrentWorkspace()
props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem>
<HamburgerMenuItem kind='download' fa='far fa-arrow-alt-down' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
props.downloadCurrentWorkspace()
props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem>
<HamburgerMenuItem kind='delete' fa='far fa-trash' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
props.deleteCurrentWorkspace()
props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem>
<Dropdown.Divider className="border mb-0 mt-0 remixui_menuhr" style={{ pointerEvents: 'none' }} />
<HamburgerMenuItem kind='clone' fa='fab fa-github' hideOption={hideWorkspaceOptions} actionOnClick={() => {
props.cloneGitRepository()
<HamburgerMenuItem kind='deleteAll' fa='far fa-trash-alt' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
props.deleteAllWorkspaces()
props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem>
<Dropdown.Divider className="border mt-0 mb-0 remixui_menuhr" style={{ pointerEvents: 'none' }}/>
<HamburgerMenuItem kind='backup' fa='far fa-download' hideOption={hideWorkspaceOptions || hideLocalhostOptions} actionOnClick={() => {
props.downloadWorkspaces()
props.hideIconsMenu(!showIconsMenu)

@ -33,6 +33,7 @@ export const FileSystemContext = createContext<{
dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void>
dispatchHandleExpandPath: (paths: string[]) => Promise<void>,
dispatchHandleDownloadFiles: () => Promise<void>,
dispatchHandleDownloadWorkspace: () => Promise<void>,
dispatchHandleRestoreBackup: () => Promise<void>
dispatchCloneRepository: (url: string) => Promise<void>,
dispatchMoveFile: (src: string, dest: string) => Promise<void>,

@ -7,7 +7,7 @@ import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, deleteAllWorkspaces, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder,
deletePath, renamePath, downloadPath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace,
fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, uploadFolder, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile, moveFolder,
fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, uploadFolder, handleDownloadWorkspace, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile, moveFolder,
showAllBranches, switchBranch, createNewBranch, checkoutRemoteBranch, createSolidityGithubAction, createTsSolGithubAction, createSlitherGithubAction
} from '../actions'
import { Modal, WorkspaceProps, WorkspaceTemplate } from '../types'
@ -135,6 +135,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await handleDownloadFiles()
}
const dispatchHandleDownloadWorkspace = async () => {
await handleDownloadWorkspace()
}
const dispatchHandleRestoreBackup = async () => {
await restoreBackupZip()
}
@ -284,6 +288,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchHandleClickFile,
dispatchHandleExpandPath,
dispatchHandleDownloadFiles,
dispatchHandleDownloadWorkspace,
dispatchHandleRestoreBackup,
dispatchCloneRepository,
dispatchMoveFile,

@ -76,6 +76,9 @@ export function Workspace () {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.rename' }), renameModalMessage(), intl.formatMessage({ id: 'filePanel.ok' }), onFinishRenameWorkspace, intl.formatMessage({ id: 'filePanel.cancel' }))
}
const downloadCurrentWorkspace = () => {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.download' }), intl.formatMessage({ id: 'filePanel.workspace.downloadConfirm' }), intl.formatMessage({ id: 'filePanel.ok' }), onFinishDownloadWorkspace, intl.formatMessage({ id: 'filePanel.cancel' }))
}
const createWorkspace = () => {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.create' }), createModalMessage(), intl.formatMessage({ id: 'filePanel.ok' }), onFinishCreateWorkspace, intl.formatMessage({ id: 'filePanel.cancel' }))
}
@ -156,6 +159,15 @@ export function Workspace () {
}
}
const onFinishDownloadWorkspace = async () => {
try {
await global.dispatchHandleDownloadWorkspace()
} catch (e) {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.download' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, intl.formatMessage({ id: 'filePanel.cancel' }))
console.error(e)
}
}
const onFinishCreateWorkspace = async () => {
if (workspaceCreateInput.current === undefined) return
// @ts-ignore: Object is possibly 'null'.
@ -453,9 +465,10 @@ export function Workspace () {
<Dropdown.Menu as={CustomMenu} data-id="wsdropdownMenu" className='custom-dropdown-items remixui_menuwidth' rootCloseEvent="click">
<HamburgerMenu
createWorkspace={createWorkspace}
renameCurrentWorkspace={renameCurrentWorkspace}
downloadCurrentWorkspace={downloadCurrentWorkspace}
deleteCurrentWorkspace={deleteCurrentWorkspace}
deleteAllWorkspaces={deleteAllWorkspaces}
renameCurrentWorkspace={renameCurrentWorkspace}
cloneGitRepository={cloneGitRepository}
downloadWorkspaces={downloadWorkspaces}
restoreBackup={restoreBackup}

Loading…
Cancel
Save