diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index 633b387365..7cda455c74 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -410,12 +410,12 @@ module.exports = { browser .useXpath() .waitForElementPresent({ - selector: '//i[@data-icon="workspaceDropdownMenuIcon"]', + selector: '//i[@data-id="workspaceDropdownMenuIcon"]', locateStrategy: 'xpath', }) .click('//*[@id="workspacesMenuDropdown"]/span/i') .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[3]') // rename workspace_name + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[4]') // rename workspace_name .useCss() .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextRename"]') diff --git a/apps/remix-ide-e2e/src/tests/workspace_git.test.ts b/apps/remix-ide-e2e/src/tests/workspace_git.test.ts index e2293f4153..fd2c261493 100644 --- a/apps/remix-ide-e2e/src/tests/workspace_git.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace_git.test.ts @@ -58,19 +58,15 @@ module.exports = { .waitForElementContainsText('[data-id="workspaceGitBranchesDropdown"]', 'main') }, - - - // CLONE REPOSITORY E2E START 'Should clone a repository #group2': function (browser: NightwatchBrowser) { browser .clickLaunchIcon('filePanel') - .useXpath() - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() + .waitForElementVisible('[data-id="workspaceMenuDropdown"]') + .click('[data-id="workspaceMenuDropdown"]') + .waitForElementVisible('[data-id="workspaceclone"]') + .click('[data-id="workspaceclone"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -93,11 +89,9 @@ module.exports = { 'Should display non-clashing names for duplicate clone #group2': '' + function (browser: NightwatchBrowser) { browser - .useXpath() - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() + .click('[data-id="workspaceMenuDropdown"]') + .waitForElementVisible('[data-id="workspaceclone"]') + .click('[data-id="workspaceclone"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -105,11 +99,9 @@ module.exports = { .click('[data-id="fileSystem-modal-footer-ok-react"]') .pause(5000) .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix1') - .useXpath() - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() + .click('[data-id="workspaceMenuDropdown"]') + .waitForElementVisible('[data-id="workspaceclone"]') + .click('[data-id="workspaceclone"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -117,10 +109,8 @@ module.exports = { .click('[data-id="fileSystem-modal-footer-ok-react"]') .pause(5000) .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix2') - .useXpath() - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() + .click('[data-id="workspaceMenuDropdown"]') + .waitForElementVisible('[data-id="workspaceDropdownMenuIcon]"') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -136,15 +126,10 @@ module.exports = { 'Should display error message in modal for failed clone #group2': function (browser: NightwatchBrowser) { browser - .useXpath() - .waitForElementPresent({ - selector: '//i[@data-icon="workspaceDropdownMenuIcon"]', - locateStrategy: 'xpath', - }) - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() + .waitForElementVisible('[data-id="workspaceDropdownMenuIcon"]') + .click('[data-id="workspaceMenuDropdown"]') + .waitForElementVisible('[data-id="workspaceclone"]') + .click('[data-id="workspaceclone"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') @@ -163,11 +148,9 @@ module.exports = { browser .clickLaunchIcon('filePanel') .waitForElementNotVisible('[data-id="workspaceGitPanel"]') - .useXpath() - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() + .click('[data-id="workspaceMenuDropdown"]') + .waitForElementVisible('[data-id="workspaceclone"]') + .click('[data-id="workspaceclone"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') .click('[data-id="fileSystemModalDialogModalBody-react"]') .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') diff --git a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json index a063529231..fa9e3500c1 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json @@ -3,16 +3,18 @@ "filePanel.workspace": "WORKSPACES", "filePanel.create": "Create", "filePanel.clone": "Clone", - "filePanel.download": "Download", + "filePanel.download": "Backup", "filePanel.restore": "Restore", "filePanel.workspace.create": "Create Workspace", "filePanel.workspace.rename": "Rename Workspace", "filePanel.workspace.delete": "Delete Workspace", "filePanel.workspace.deleteConfirm": "Are you sure to delete the current workspace?", + "filePanel.workspace.deleteAll": "Delete All Workspaces", + "filePanel.workspace.deleteAllConfirm": "Are you absolutely sure you want to delete all your workspaces? Deleted workspaces can not be restored in any manner.", "filePanel.workspace.name": "Workspace name", "filePanel.workspace.chooseTemplate": "Choose a template", - "filePanel.workspace.download": "Download Workspace", - "filePanel.workspace.restore": "Restore Workspace Backup", + "filePanel.workspace.download": "Backup 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.", "filePanel.workspace.enterGitUrl": "Enter git repository url", diff --git a/apps/remix-ide/src/app/tabs/locales/zh/filePanel.json b/apps/remix-ide/src/app/tabs/locales/zh/filePanel.json index a0a1e1b0c7..94129f0d31 100644 --- a/apps/remix-ide/src/app/tabs/locales/zh/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/zh/filePanel.json @@ -9,6 +9,8 @@ "filePanel.workspace.rename": "重命名工作空间", "filePanel.workspace.delete": "删除工作空间", "filePanel.workspace.deleteConfirm": "确定要删除当前工作空间?", + "filePanel.workspace.deleteAll": "Delete All Workspaces", + "filePanel.workspace.deleteAllConfirm": "Are you absolutely sure you want to delete all your workspaces? Deleted workspaces can not be restored in any manner.", "filePanel.workspace.name": "工作空间名称", "filePanel.workspace.chooseTemplate": "选择一个工作空间模板", "filePanel.workspace.download": "下载工作空间", diff --git a/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx b/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx index 04d31c4839..5322b4c61a 100644 --- a/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx +++ b/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx @@ -30,7 +30,7 @@ export const CustomIconsToggle = React.forwardRef(({ onClick, icon, className = className={`${className.replace('dropdown-toggle', '')} mb-0 pb-0 d-flex justify-content-end align-items-end remixuimenuicon_shadow fs-3`} data-id="workspaceMenuDropdown" > - { icon && } + { icon && } )) @@ -48,7 +48,7 @@ export const CustomMenu = React.forwardRef( > diff --git a/libs/remix-ui/workspace/src/lib/actions/workspace.ts b/libs/remix-ui/workspace/src/lib/actions/workspace.ts index 8b3b1f14e6..a37759044e 100644 --- a/libs/remix-ui/workspace/src/lib/actions/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/actions/workspace.ts @@ -311,6 +311,14 @@ export const deleteWorkspace = async (workspaceName: string, cb?: (err: Error, r cb && cb(null, workspaceName) } +export const deleteAllWorkspaces = async () => { + await (await getWorkspaces()).map(async workspace => { + await deleteWorkspaceFromProvider(workspace.name) + await dispatch(setDeleteWorkspace(workspace.name)) + plugin.workspaceDeleted(workspace.name) + }) +} + const deleteWorkspaceFromProvider = async (workspaceName: string) => { const workspacesPath = plugin.fileProviders.workspace.workspacesPath @@ -450,7 +458,6 @@ export const cloneRepository = async (url: string) => { if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit') await fetchWorkspaceDirectory(ROOT_PATH) const workspacesPath = plugin.fileProviders.workspace.workspacesPath - console.log('go in to promise') const branches = await getGitRepoBranches(workspacesPath + '/' + repoName) dispatch(setCurrentWorkspaceBranches(branches)) @@ -481,7 +488,6 @@ export const cloneRepository = async (url: string) => { } } - export const checkGit = async () => { const isGitRepo = await plugin.fileManager.isGitRepo() dispatch(setCurrentWorkspaceIsGitRepo(isGitRepo)) diff --git a/libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx new file mode 100644 index 0000000000..dc80908b9a --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx @@ -0,0 +1,51 @@ +import React from 'react' +import { CustomTooltip } from '@remix-ui/helper' +import { Dropdown } from 'react-bootstrap' +import { FormattedMessage } from 'react-intl' + +const _paq = window._paq = window._paq || [] + +export interface HamburgerMenuItemProps { + hideOption: boolean + kind: string + actionOnClick: () => void + fa: string +} + +export function HamburgerMenuItem (props: HamburgerMenuItemProps) { + const { hideOption } = props + const uid = 'workspace' + props.kind + return ( + <> + + } + > +
{ + props.actionOnClick() + _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', {uid}]) + }} + > + + + + +
+
+
+ + ) + } diff --git a/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx index 02e9989aed..0dc1e37fc6 100644 --- a/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx +++ b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx @@ -1,13 +1,11 @@ import React from 'react' -import { CustomTooltip } from '@remix-ui/helper' import { Dropdown } from 'react-bootstrap' -import { FormattedMessage } from 'react-intl' - -const _paq = window._paq = window._paq || [] +import { HamburgerMenuItem } from './workspace-hamburger-item' export interface HamburgerMenuProps { createWorkspace: () => void, deleteCurrentWorkspace: () => void, + deleteAllWorkspaces: () => void, renameCurrentWorkspace: () => void, cloneGitRepository: () => void, downloadWorkspaces: () => void, @@ -23,294 +21,51 @@ export interface HamburgerMenuProps { export function HamburgerMenu (props: HamburgerMenuProps) { const { showIconsMenu, hideWorkspaceOptions, hideLocalhostOptions } = props - return ( - <> - - } - > -
{ - props.createWorkspace() - _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceCreate']) - props.hideIconsMenu(!showIconsMenu) - }} - key={`workspacesCreate-fe-ws`} - > - - -
-
-
- - } - > -
{ - props.deleteCurrentWorkspace() - _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceDelete']) - props.hideIconsMenu(!showIconsMenu) - }} - key={`workspacesDelete-fe-ws`} - > - - -
-
-
- - } - > -
{ - props.renameCurrentWorkspace() - _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceRename']) - props.hideIconsMenu(!showIconsMenu) - }} - data-id='workspaceRename' - key={`workspacesRename-fe-ws`} - > - - -
-
-
- - - } - > -
{ - props.cloneGitRepository() - _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'cloneGitRepository']) - props.hideIconsMenu(!showIconsMenu) - }} - key={`cloneGitRepository-fe-ws`} - > - - -
-
-
- - - } - > -
{ - props.downloadWorkspaces() - _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesDownload']) - props.hideIconsMenu(!showIconsMenu) - }} - key={`workspacesDownload-fe-ws`} - > - - -
-
-
- - } - > -
{ - props.restoreBackup() - _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesRestore']) - props.hideIconsMenu(!showIconsMenu) - }} - key={`workspacesRestore-fe-ws`} - > - - -
-
-
- - - } - > -
{ - props.addGithubAction() - _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'addSolidityTesting']) - props.hideIconsMenu(!showIconsMenu) - }} - > - - {} -
-
-
- - } - > -
{ - props.addTsSolTestGithubAction() - _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'addTsSolTestingAction']) - props.hideIconsMenu(!showIconsMenu) - }} - > - - {} -
-
-
- - } - > -
{ - props.addSlitherGithubAction() - _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'addSlitherAction']) - props.hideIconsMenu(!showIconsMenu) - }} - > - - {} -
-
-
- - ) - } + return ( + <> + { + props.createWorkspace() + props.hideIconsMenu(!showIconsMenu) + }}> + { + props.deleteCurrentWorkspace() + props.hideIconsMenu(!showIconsMenu) + }}> + { + props.deleteAllWorkspaces() + props.hideIconsMenu(!showIconsMenu) + }}> + { + props.renameCurrentWorkspace() + props.hideIconsMenu(!showIconsMenu) + }}> + + { + props.cloneGitRepository() + props.hideIconsMenu(!showIconsMenu) + }}> + + { + props.downloadWorkspaces() + props.hideIconsMenu(!showIconsMenu) + }}> + { + props.restoreBackup() + props.hideIconsMenu(!showIconsMenu) + }}> + + { + props.addGithubAction() + props.hideIconsMenu(!showIconsMenu) + }}> + { + props.addTsSolTestGithubAction() + props.hideIconsMenu(!showIconsMenu) + }}> + { + props.addSlitherGithubAction() + props.hideIconsMenu(!showIconsMenu) + }}> + + ) + } \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/contexts/index.ts b/libs/remix-ui/workspace/src/lib/contexts/index.ts index 76ed084896..2afd2b706a 100644 --- a/libs/remix-ui/workspace/src/lib/contexts/index.ts +++ b/libs/remix-ui/workspace/src/lib/contexts/index.ts @@ -15,6 +15,7 @@ export const FileSystemContext = createContext<{ dispatchSwitchToWorkspace: (name: string) => Promise, dispatchRenameWorkspace: (oldName: string, workspaceName: string) => Promise, dispatchDeleteWorkspace: (workspaceName: string) => Promise, + dispatchDeleteAllWorkspaces: () => Promise, dispatchPublishToGist: (path?: string, type?: string) => Promise, dispatchUploadFile: (target?: SyntheticEvent, targetFolder?: string) => Promise, dispatchCreateNewFile: (path: string, rootDir: string) => Promise, diff --git a/libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css b/libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css index b52432b403..020c7807cc 100644 --- a/libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css +++ b/libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css @@ -115,21 +115,8 @@ min-width: 8rem; } - .remixui_menuhr{ - - } - - #workspacesMenuDropdown>div>ul>a:nth-child(6):hover { - background-color: rgba(0,0,0,0) - } - - #workspacesMenuDropdown>div>ul>a:nth-child(4):hover { - background-color: rgba(0, 0, 0, 0) - } - #workspacesMenuDropdown > div > ul > a:hover { background-color: var(--secondary); - /* border: 1px solid var(--secondary); */ border-radius: 2px; color: var(--text) } diff --git a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx index 2921ebe308..887a0f1afd 100644 --- a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx +++ b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx @@ -5,7 +5,7 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line // eslint-disable-next-line @typescript-eslint/no-unused-vars import { FileSystemContext } from '../contexts' import { browserReducer, browserInitialState } from '../reducers/workspace' -import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder, +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, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile, moveFolder, showAllBranches, switchBranch, createNewBranch, checkoutRemoteBranch, createSolidityGithubAction, createTsSolGithubAction, createSlitherGithubAction @@ -67,6 +67,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => { await deleteWorkspace(workspaceName) } + const dispatchDeleteAllWorkspaces = async () => { + await deleteAllWorkspaces() + } + const dispatchPublishToGist = async (path?: string, type?: string) => { await publishToGist(path, type) } @@ -258,6 +262,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => { dispatchSwitchToWorkspace, dispatchRenameWorkspace, dispatchDeleteWorkspace, + dispatchDeleteAllWorkspaces, dispatchPublishToGist, dispatchUploadFile, dispatchCreateNewFile, diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 07c1b8ca85..4a1f8fdd83 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -90,6 +90,16 @@ export function Workspace () { ) } + const deleteAllWorkspaces = () => { + global.modal( + intl.formatMessage({ id: 'filePanel.workspace.deleteAll' }), + intl.formatMessage({ id: 'filePanel.workspace.deleteAllConfirm' }), + intl.formatMessage({ id: 'filePanel.ok' }), + onFinishDeleteAllWorkspaces, + '' + ) + } + const cloneGitRepository = () => { global.modal( intl.formatMessage({ id: 'filePanel.workspace.clone' }), @@ -175,7 +185,15 @@ export function Workspace () { console.error(e) } } - /** ** ****/ + + const onFinishDeleteAllWorkspaces = async () => { + try { + await global.dispatchDeleteAllWorkspaces() + } catch (e) { + global.modal(intl.formatMessage({ id: 'filePanel.workspace.deleteAll' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, '') + console.error(e) + } + } const resetFocus = () => { global.dispatchSetFocusElement([{ key: '', type: 'folder' }]) @@ -433,6 +451,7 @@ export function Workspace () { websocket: WS currentSharedFolder: string watcher: chokidar.FSWatcher + trackDownStreamUpdate: Record = {} constructor (private readOnly = false) { super() @@ -105,6 +107,7 @@ export class RemixdClient extends PluginClient { this.createDir({ path: args.path.substr(0, args.path.lastIndexOf('/')) }) } try { + this.trackDownStreamUpdate[path] = args.content fs.writeFile(path, args.content, 'utf8', (error: Error) => { if (error) { console.log(error) @@ -263,8 +266,9 @@ export class RemixdClient extends PluginClient { }) */ this.watcher.on('change', async (f: string) => { - const currentContent = await this.call('editor', 'getText' as any, f) - const newContent = fs.readFileSync(f) + const path = pathModule.resolve(f) + const currentContent = this.trackDownStreamUpdate[path] + const newContent = fs.readFileSync(f, 'utf-8') if (currentContent !== newContent && this.isLoaded) { this.emit('changed', utils.relativePath(f, this.currentSharedFolder)) }