diff --git a/apps/remix-ide-e2e/src/tests/solidityImport.test.ts b/apps/remix-ide-e2e/src/tests/solidityImport.test.ts index afbf1fe84f..ab4e4331b1 100644 --- a/apps/remix-ide-e2e/src/tests/solidityImport.test.ts +++ b/apps/remix-ide-e2e/src/tests/solidityImport.test.ts @@ -84,12 +84,12 @@ module.exports = { 'Test NPM Import (with unpkg.com)': function (browser: NightwatchBrowser) { browser - // .setSolidityCompilerVersion('soljson-v0.8.0+commit.c7dfd78e.js') + .setSolidityCompilerVersion('soljson-v0.8.1+commit.df193b15.js') .clickLaunchIcon('fileExplorers') .click('li[data-id="treeViewLitreeViewItemREADME.txt"') .addFile('Untitled9.sol', sources[8]['Untitled9.sol']) .clickLaunchIcon('fileExplorers') - .verifyContracts(['test13', 'ERC20', 'SafeMath'], { wait: 30000 }) + .verifyContracts(['test13', 'ERC20'], { wait: 30000 }) .end() }, tearDown: sauce @@ -122,6 +122,6 @@ const sources = [ 'Untitled8.sol': { content: 'import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol"; contract test12 {}' } }, { - 'Untitled9.sol': { content: 'pragma solidity >=0.6.0 <0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract test13 {}' } + 'Untitled9.sol': { content: 'pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract test13 {}' } } ] diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 5e8b7ac863..829a635104 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -18,7 +18,7 @@ import { LandingPage } from './app/ui/landing-page/landing-page' import { MainPanel } from './app/components/main-panel' import FetchAndCompile from './app/compiler/compiler-sourceVerifier-fetchAndCompile' -import migrateFileSystem, { migrateToWorkspace } from './migrateFileSystem' +import migrateFileSystem from './migrateFileSystem' const isElectron = require('is-electron') const csjs = require('csjs-inject') @@ -325,11 +325,11 @@ Please make a backup of your contracts and start using http://remix.ethereum.org // those views depend on app_manager const menuicons = new VerticalIcons(appManager) - const landingPage = new LandingPage(appManager, menuicons) const sidePanel = new SidePanel(appManager, menuicons) const hiddenPanel = new HiddenPanel() const pluginManagerComponent = new PluginManagerComponent(appManager, engine) const filePanel = new FilePanel(appManager) + const landingPage = new LandingPage(appManager, menuicons, fileManager, filePanel) const settings = new SettingsTab( registry.get('config').api, editor, @@ -494,7 +494,5 @@ Please make a backup of your contracts and start using http://remix.ethereum.org // get the file list from the parent iframe loadFileFromParent(fileManager) - migrateToWorkspace(fileManager, filePanel) - if (params.embed) framingService.embed() } diff --git a/apps/remix-ide/src/app/files/fileProvider.js b/apps/remix-ide/src/app/files/fileProvider.js index 20a7135c12..544a1b0bab 100644 --- a/apps/remix-ide/src/app/files/fileProvider.js +++ b/apps/remix-ide/src/app/files/fileProvider.js @@ -199,21 +199,24 @@ class FileProvider { * copy the folder recursively (internal use) * @param {string} path is the folder to be copied over * @param {Function} visitFile is a function called for each visited files + * @param {Function} visitFolder is a function called for each visited folders */ - _copyFolderToJsonInternal (path, visitFile) { + _copyFolderToJsonInternal (path, visitFile, visitFolder) { visitFile = visitFile || (() => {}) + visitFolder = visitFolder || (() => {}) return new Promise((resolve, reject) => { const json = {} path = this.removePrefix(path) if (window.remixFileSystem.existsSync(path)) { try { const items = window.remixFileSystem.readdirSync(path) + visitFolder({ path }) if (items.length !== 0) { items.forEach(async (item, index) => { const file = {} const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` if (window.remixFileSystem.statSync(curPath).isDirectory()) { - file.children = await this._copyFolderToJsonInternal(curPath, visitFile) + file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder) } else { file.content = window.remixFileSystem.readFileSync(curPath, 'utf8') visitFile({ path: curPath, content: file.content }) @@ -234,10 +237,12 @@ class FileProvider { * copy the folder recursively * @param {string} path is the folder to be copied over * @param {Function} visitFile is a function called for each visited files + * @param {Function} visitFolder is a function called for each visited folders */ - copyFolderToJson (path, visitFile) { + copyFolderToJson (path, visitFile, visitFolder) { visitFile = visitFile || (() => {}) - return this._copyFolderToJsonInternal(path, visitFile) + visitFolder = visitFolder || (() => {}) + return this._copyFolderToJsonInternal(path, visitFile, visitFolder) } removeFile (path) { diff --git a/apps/remix-ide/src/app/files/workspaceFileProvider.js b/apps/remix-ide/src/app/files/workspaceFileProvider.js index 2b1a795d0b..fe4e6825cb 100644 --- a/apps/remix-ide/src/app/files/workspaceFileProvider.js +++ b/apps/remix-ide/src/app/files/workspaceFileProvider.js @@ -48,11 +48,14 @@ class WorkspaceFileProvider extends FileProvider { }) } - async copyFolderToJson (directory, visitFile) { + async copyFolderToJson (directory, visitFile, visitFolder) { visitFile = visitFile || (() => {}) + visitFolder = visitFolder || (() => {}) const regex = new RegExp(`.workspaces/${this.workspace}/`, 'g') let json = await super._copyFolderToJsonInternal(directory, ({ path, content }) => { visitFile({ path: path.replace(regex, ''), content }) + }, ({ path }) => { + visitFolder({ path: path.replace(regex, '') }) }) json = JSON.stringify(json).replace(regex, '') return JSON.parse(json) diff --git a/apps/remix-ide/src/app/ui/landing-page/landing-page.js b/apps/remix-ide/src/app/ui/landing-page/landing-page.js index bf89d166d8..844778ac82 100644 --- a/apps/remix-ide/src/app/ui/landing-page/landing-page.js +++ b/apps/remix-ide/src/app/ui/landing-page/landing-page.js @@ -1,11 +1,14 @@ import * as packageJson from '../../../../../../package.json' import { ViewPlugin } from '@remixproject/engine-web' +import { migrateToWorkspace } from '../../../migrateFileSystem' +import JSZip from 'jszip' const yo = require('yo-yo') const csjs = require('csjs-inject') const globalRegistry = require('../../../global/registry') const CompilerImport = require('../../compiler/compiler-imports') const modalDialogCustom = require('../modal-dialog-custom') +const modalDialog = require('../modaldialog') const tooltip = require('../tooltip') const GistHandler = require('../../../lib/gist-handler') const QueryParams = require('../../../lib/query-params.js') @@ -112,9 +115,11 @@ const profile = { } export class LandingPage extends ViewPlugin { - constructor (appManager, verticalIcons) { + constructor (appManager, verticalIcons, fileManager, filePanel) { super(profile) this.profile = profile + this.fileManager = fileManager + this.filePanel = filePanel this.appManager = appManager this.verticalIcons = verticalIcons this.gistHandler = new GistHandler() @@ -297,6 +302,44 @@ export class LandingPage extends ViewPlugin { this.call('fileExplorers', 'createNewFile') } + const saveAs = (blob, name) => { + const node = document.createElement('a') + node.download = name + node.rel = 'noopener' + node.href = URL.createObjectURL(blob) + setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s + setTimeout(function () { + try { + node.dispatchEvent(new MouseEvent('click')) + } catch (e) { + var evt = document.createEvent('MouseEvents') + evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, + 20, false, false, false, false, 0, null) + node.dispatchEvent(evt) + } + }, 0) // 40s + } + + const downloadFiles = async () => { + try { + tooltip('preparing files, please wait..') + const fileProviders = globalRegistry.get('fileproviders').api + const zip = new JSZip() + await fileProviders.browser.copyFolderToJson('/', ({ path, content }) => { + zip.file(path, content) + }, ({ path, content }) => { + zip.folder(path, content) + }) + zip.generateAsync({ type: 'blob' }).then(function (blob) { + saveAs(blob, 'remixdbackup.zip') + }).catch((e) => { + tooltip(e.message) + }) + } catch (e) { + tooltip(e.message) + } + } + const uploadFile = (target) => { this.call('fileExplorers', 'uploadFile', target) } @@ -357,6 +400,37 @@ export class LandingPage extends ViewPlugin { query.update({ appVersion: '0.7.7' }) document.location.reload() } + + const migrate = async () => { + tooltip('migrating workspace...') + try { + const workspaceName = await migrateToWorkspace(this.fileManager, this.filePanel) + tooltip('done. ' + workspaceName + ' created.') + } catch (e) { + return tooltip(e.message) + } + } + + const migrateWorkspace = async () => { + modalDialog( + 'File system Migration', + yo`Do you want to download your files to local device first?`, + { + label: 'Download and Migrate', + fn: async () => { + await downloadFiles() + migrate() + } + }, + { + label: 'Migrate', + fn: () => { + migrate() + } + } + ) + } + const img = yo`` const playRemi = async () => { await document.getElementById('remiAudio').play() } // to retrieve medium posts @@ -408,6 +482,10 @@ export class LandingPage extends ViewPlugin { connectToLocalhost()}>Connect to Localhost
++ + downloadFiles()}>Download all Files +
+
switchToPreviousVersion()}>Old experience
++ + migrateWorkspace()}>Migrate old filesystem to workspace +