diff --git a/README.md b/README.md index 57cf2b34b4..b8f700f796 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ git clone https://github.com/ethereum/remix-project.git ```bash cd remix-project npm install +npm run build:libs // Build remix libs nx build nx serve ``` diff --git a/apps/remix-ide-e2e/src/tests/importFromGithub.test.ts b/apps/remix-ide-e2e/src/tests/importFromGithub.test.ts new file mode 100644 index 0000000000..036d9ceae9 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/importFromGithub.test.ts @@ -0,0 +1,66 @@ +'use strict' +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +const testData = { + validURL: 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol', + invalidURL: 'https://github.com/Oppelin/Roles.sol' +} + +module.exports = { + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done) + }, + + 'Import from GitHub Modal': function (browser: NightwatchBrowser) { + browser.clickLaunchIcon('home') + .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) + .clickLaunchIcon('filePanel') + .click('div[title="home"]') + .waitForElementVisible('button[data-id="landingPageImportFromGitHubButton"]') + .pause(1000) + .scrollAndClick('button[data-id="landingPageImportFromGitHubButton"]') + .waitForElementVisible('*[data-id="homeTabModalDialogModalTitle-react"]') + .assert.containsText('*[data-id="homeTabModalDialogModalTitle-react"]', 'Import from Github') + .waitForElementVisible('*[data-id="homeTabModalDialogModalBody-react"]') + .assert.containsText('*[data-id="homeTabModalDialogModalBody-react"]', 'Enter the github URL you would like to load.') + .waitForElementVisible('*[data-id="homeTabModalDialogCustomPromptText"]') + .refresh() + }, + + 'Display Error Message For Invalid GitHub URL Modal': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) + .clickLaunchIcon('settings') + .clickLaunchIcon('filePanel') + .scrollAndClick('*[data-id="landingPageImportFromGitHubButton"]') + .waitForElementVisible('input[data-id="homeTabModalDialogCustomPromptText"]') + .execute(() => { + (document.querySelector('input[data-id="homeTabModalDialogCustomPromptText"]') as any).focus() + }, [], () => {}) + .setValue('input[data-id="homeTabModalDialogCustomPromptText"]', testData.invalidURL) + .waitForElementVisible('*[data-id="homeTab-modal-footer-ok-react"]') + .scrollAndClick('[data-id="homeTab-modal-footer-ok-react"]') // submitted + .waitForElementVisible('*[data-shared="tooltipPopup"]') + .assert.containsText('*[data-shared="tooltipPopup"] span', 'not found ' + testData.invalidURL) + }, + + 'Import From Github For Valid URL': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) + .clickLaunchIcon('settings') + .clickLaunchIcon('filePanel') + .scrollAndClick('*[data-id="landingPageImportFromGitHubButton"]') + .waitForElementVisible('*[data-id="homeTabModalDialogCustomPromptText"]') + .clearValue('*[data-id="homeTabModalDialogCustomPromptText"]') + .execute(() => { + (document.querySelector('input[data-id="homeTabModalDialogCustomPromptText"]') as any).focus() + }, [], () => {}) + .setValue('input[data-id="homeTabModalDialogCustomPromptText"]', testData.validURL) + .waitForElementVisible('*[data-id="homeTab-modal-footer-ok-react"]') + .scrollAndClick('[data-id="homeTab-modal-footer-ok-react"]') + .openFile('github/OpenZeppelin/openzeppelin-solidity/contracts/access/Roles.sol') + .waitForElementVisible("div[title='default_workspace/github/OpenZeppelin/openzeppelin-solidity/contracts/access/Roles.sol'") + .end() + } +} diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 1fd8172e6e..e569cc5e15 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -1,13 +1,7 @@ 'use strict' -import { basicLogo } from './app/ui/svgLogo' - import { RunTab, makeUdapp } from './app/udapp' - -import PanelsResize from './lib/panels-resize' import { RemixEngine } from './remixEngine' import { RemixAppManager } from './remixAppManager' -import { FramingService } from './framingService' -import { WalkthroughService } from './walkthroughService' import { MainView } from './app/panels/main-view' import { ThemeModule } from './app/tabs/theme-module' import { NetworkModule } from './app/tabs/network-module' @@ -17,14 +11,16 @@ import { HiddenPanel } from './app/components/hidden-panel' import { VerticalIcons } from './app/components/vertical-icons' import { LandingPage } from './app/ui/landing-page/landing-page' import { MainPanel } from './app/components/main-panel' +import { FramingService } from './framingService' + +import { WalkthroughService } from './walkthroughService' import { OffsetToLineColumnConverter, CompilerMetadata, CompilerArtefacts, FetchAndCompile, CompilerImports } from '@remix-project/core-plugin' import migrateFileSystem from './migrateFileSystem' const isElectron = require('is-electron') -const csjs = require('csjs-inject') -const yo = require('yo-yo') + const remixLib = require('@remix-project/remix-lib') const registry = require('./global/registry') @@ -33,8 +29,7 @@ const Storage = remixLib.Storage const RemixDProvider = require('./app/files/remixDProvider') const HardhatProvider = require('./app/tabs/hardhat-provider') const Config = require('./config') -const modalDialogCustom = require('./app/ui/modal-dialog-custom') -const modalDialog = require('./app/ui/modaldialog') + const FileManager = require('./app/files/fileManager') const FileProvider = require('./app/files/fileProvider') const DGitProvider = require('./app/files/dgitProvider') @@ -54,93 +49,13 @@ const FilePanel = require('./app/panels/file-panel') const Editor = require('./app/editor/editor') const Terminal = require('./app/panels/terminal') const ContextualListener = require('./app/editor/contextualListener') -const _paq = window._paq = window._paq || [] - -const css = csjs` - html { box-sizing: border-box; } - *, *:before, *:after { box-sizing: inherit; } - body { - /* font: 14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif; */ - font-size : .8rem; - } - pre { - overflow-x: auto; - } - .remixIDE { - width : 100vw; - height : 100vh; - overflow : hidden; - flex-direction : row; - display : flex; - } - .mainpanel { - display : flex; - flex-direction : column; - overflow : hidden; - flex : 1; - } - .iconpanel { - display : flex; - flex-direction : column; - overflow : hidden; - width : 50px; - user-select : none; - } - .sidepanel { - display : flex; - flex-direction : row-reverse; - width : 320px; - overflow : auto; - } - .highlightcode { - position : absolute; - z-index : 20; - background-color : var(--info); - } - .highlightcode_fullLine { - position : absolute; - z-index : 20; - background-color : var(--info); - opacity : 0.5; - } - .centered { - position : fixed; - top : 20%; - left : 45%; - width : 200px; - height : 200px; - } - .centered svg path { - fill: var(--secondary); - } - .centered svg polygon { - fill : var(--secondary); - } - .onboarding { - color : var(--text-info); - background-color : var(--info); - } - .matomoBtn { - width : 100px; - } -` -class App { +class AppComponent { constructor (api = {}, events = {}, opts = {}) { - var self = this + const self = this self.appManager = new RemixAppManager({}) self._components = {} - self._view = {} - self._view.splashScreen = yo` -
- ${basicLogo()} -
- REMIX IDE -
-
- ` - document.body.appendChild(self._view.splashScreen) - + self.registry = registry // setup storage const configStorage = new Storage('config-v0.8:') @@ -162,67 +77,22 @@ class App { migrateFileSystem(self._components.filesProviders.browser) } - init () { - this.run().catch(console.error) - } - - render () { - var self = this - if (self._view.el) return self._view.el - // not resizable - self._view.iconpanel = yo` -
- ${''} -
- ` - - // center panel, resizable - self._view.sidepanel = yo` -
- ${''} -
- ` - - // handle the editor + terminal - self._view.mainpanel = yo` -
- ${''} -
- ` - - self._components.resizeFeature = new PanelsResize(self._view.sidepanel) - - self._view.el = yo` - - ` - return self._view.el - } - async run () { - var self = this - - // check the origin and warn message - if (window.location.hostname === 'yann300.github.io') { - modalDialogCustom.alert('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.') - } else if (window.location.hostname === 'remix-alpha.ethereum.org' || - (window.location.hostname === 'ethereum.github.io' && window.location.pathname.indexOf('/remix-live-alpha') === 0)) { - modalDialogCustom.alert('Welcome to the Remix alpha instance. Please use it to try out latest features. But use preferably https://remix.ethereum.org for any production work.') - } else if (window.location.protocol.indexOf('http') === 0 && - window.location.hostname !== 'remix.ethereum.org' && - window.location.hostname !== 'localhost' && - window.location.hostname !== '127.0.0.1') { - modalDialogCustom.alert(`The Remix IDE has moved to http://remix.ethereum.org.\n - This instance of Remix you are visiting WILL NOT BE UPDATED.\n - Please make a backup of your contracts and start using http://remix.ethereum.org`) - } - if (window.location.protocol.indexOf('https') === 0) { - toolTip('You are using an `https` connection. Please switch to `http` if you are using Remix against an `http Web3 provider` or allow Mixed Content in your browser.') + const self = this + // APP_MANAGER + const appManager = self.appManager + const pluginLoader = self.appManager.pluginLoader + self.workspace = pluginLoader.get() + self.engine = new RemixEngine() + self.engine.register(appManager) + + const matomoDomains = { + 'remix-alpha.ethereum.org': 27, + 'remix-beta.ethereum.org': 25, + 'remix.ethereum.org': 23 } + self.showMatamo = (matomoDomains[window.location.hostname] && !registry.get('config').api.exists('settings/matomo-analytics')) + self.walkthroughService = new WalkthroughService(appManager, self.showMatamo) const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080'] // workaround for Electron support @@ -233,23 +103,11 @@ class App { } } - // APP_MANAGER - const appManager = self.appManager - const pluginLoader = appManager.pluginLoader - const workspace = pluginLoader.get() - const engine = new RemixEngine() - engine.register(appManager) - // SERVICES // ----------------- theme service --------------------------------- - const themeModule = new ThemeModule(registry) - registry.put({ api: themeModule, name: 'themeModule' }) - themeModule.initTheme(() => { - setTimeout(() => { - document.body.removeChild(self._view.splashScreen) - self._view.el.style.visibility = 'visible' - }, 1500) - }) + self.themeModule = new ThemeModule(registry) + registry.put({ api: self.themeModule, name: 'themeModule' }) + // ----------------- editor service ---------------------------- const editor = new Editor() // wrapper around ace editor registry.put({ api: editor, name: 'editor' }) @@ -289,10 +147,10 @@ class App { { appManager, blockchain }, { getPosition: (event) => { - var limitUp = 36 - var limitDown = 20 - var height = window.innerHeight - var newpos = (event.pageY < limitUp) ? limitUp : event.pageY + const limitUp = 36 + const limitDown = 20 + const height = window.innerHeight + let newpos = (event.pageY < limitUp) ? limitUp : event.pageY newpos = (newpos < height - limitDown) ? newpos : height - limitDown return height - newpos } @@ -300,10 +158,10 @@ class App { ) const contextualListener = new ContextualListener({ editor }) - engine.register([ + self.engine.register([ blockchain, contentImport, - themeModule, + self.themeModule, editor, fileManager, compilerMetadataGenerator, @@ -315,110 +173,44 @@ class App { web3Provider, fetchAndCompile, dGitProvider, - hardhatProvider + hardhatProvider, + self.walkthroughService ]) // LAYOUT & SYSTEM VIEWS - const appPanel = new MainPanel(registry.get('config').api) - const mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal) - registry.put({ api: mainview, name: 'mainview' }) + const appPanel = new MainPanel() + self.mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal) + registry.put({ api: self.mainview, name: 'mainview' }) - engine.register([ + self.engine.register([ appPanel, - mainview.tabProxy + self.mainview.tabProxy ]) // those views depend on app_manager - const menuicons = new VerticalIcons(appManager) - const sidePanel = new SidePanel(appManager, menuicons) - const hiddenPanel = new HiddenPanel() - const pluginManagerComponent = new PluginManagerComponent(appManager, engine) + self.menuicons = new VerticalIcons(appManager) + self.sidePanel = new SidePanel(appManager, self.menuicons) + self.hiddenPanel = new HiddenPanel() + + const pluginManagerComponent = new PluginManagerComponent(appManager, self.engine) const filePanel = new FilePanel(appManager) - const landingPage = new LandingPage(appManager, menuicons, fileManager, filePanel, contentImport) - const settings = new SettingsTab( + const landingPage = new LandingPage(appManager, self.menuicons, fileManager, filePanel, contentImport) + self.settings = new SettingsTab( registry.get('config').api, editor, appManager ) - // adding Views to the DOM - self._view.mainpanel.appendChild(mainview.render()) - self._view.iconpanel.appendChild(menuicons.render()) - self._view.sidepanel.appendChild(sidePanel.render()) - document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body - - engine.register([ - menuicons, + self.engine.register([ + self.menuicons, landingPage, - hiddenPanel, - sidePanel, + self.hiddenPanel, + self.sidePanel, filePanel, pluginManagerComponent, - settings + self.settings ]) - const queryParams = new QueryParams() - const params = queryParams.get() - - const onAcceptMatomo = () => { - _paq.push(['forgetUserOptOut']) - // @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used - document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;' - settings.updateMatomoAnalyticsChoice(true) - const el = document.getElementById('modal-dialog') - el.parentElement.removeChild(el) - startWalkthroughService() - } - const onDeclineMatomo = () => { - settings.updateMatomoAnalyticsChoice(false) - _paq.push(['optUserOut']) - const el = document.getElementById('modal-dialog') - el.parentElement.removeChild(el) - startWalkthroughService() - } - - const startWalkthroughService = () => { - const walkthroughService = new WalkthroughService(localStorage) - if (!params.code && !params.url && !params.minimizeterminal && !params.gist && !params.minimizesidepanel) { - walkthroughService.start() - } - } - - // Ask to opt in to Matomo for remix, remix-alpha and remix-beta - const matomoDomains = { - 'remix-alpha.ethereum.org': 27, - 'remix-beta.ethereum.org': 25, - 'remix.ethereum.org': 23 - } - if (matomoDomains[window.location.hostname] && !registry.get('config').api.exists('settings/matomo-analytics')) { - modalDialog( - 'Help us to improve Remix IDE', - yo` -
-

An Opt-in version of Matomo, an open source data analytics platform is being used to improve Remix IDE.

-

We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.

-

All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: take a look.

-

We do not collect nor store any personally identifiable information (PII).

-

For more info, see: Matomo Analyitcs on Remix iDE.

-

You can change your choice in the Settings panel anytime.

-
- - -
-
`, - { - label: '', - fn: null - }, - { - label: '', - fn: null - } - ) - } else { - startWalkthroughService() - } - // CONTENT VIEWS & DEFAULT PLUGINS const compileTab = new CompileTab(registry.get('config').api, registry.get('filemanager').api) const run = new RunTab( @@ -429,7 +221,7 @@ class App { filePanel, registry.get('compilersartefacts').api, networkModule, - mainview, + self.mainview, registry.get('fileproviders/browser').api ) const analysis = new AnalysisTab(registry) @@ -443,7 +235,7 @@ class App { contentImport ) - engine.register([ + self.engine.register([ compileTab, run, debug, @@ -454,47 +246,55 @@ class App { filePanel.hardhatHandle, filePanel.slitherHandle ]) + } + + async activate () { + const queryParams = new QueryParams() + const params = queryParams.get() + const self = this if (isElectron()) { - appManager.activatePlugin('remixd') + self.appManager.activatePlugin('remixd') } try { - engine.register(await appManager.registeredPlugins()) + self.engine.register(await self.appManager.registeredPlugins()) } catch (e) { console.log('couldn\'t register iframe plugins', e.message) } - await appManager.activatePlugin(['editor']) - await appManager.activatePlugin(['theme', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) - await appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) - await appManager.activatePlugin(['sidePanel']) // activating host plugin separately - await appManager.activatePlugin(['home']) - await appManager.activatePlugin(['settings']) - await appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport']) - - appManager.on('filePanel', 'workspaceInitializationCompleted', async () => { - await appManager.registerContextMenuItems() + await self.appManager.activatePlugin(['editor']) + await self.appManager.activatePlugin(['theme', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) + await self.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) + await self.appManager.activatePlugin(['sidePanel']) // activating host plugin separately + await self.appManager.activatePlugin(['home']) + await self.appManager.activatePlugin(['settings']) + await self.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport']) + await self.appManager.activatePlugin(['settings']) + await self.appManager.activatePlugin(['walkthrough']) + + self.appManager.on('filePanel', 'workspaceInitializationCompleted', async () => { + await self.appManager.registerContextMenuItems() }) - await appManager.activatePlugin(['filePanel']) + + await self.appManager.activatePlugin(['filePanel']) // Set workspace after initial activation - appManager.on('editor', 'editorMounted', () => { - if (Array.isArray(workspace)) { - appManager.activatePlugin(workspace).then(async () => { + self.appManager.on('editor', 'editorMounted', () => { + if (Array.isArray(self.workspace)) { + self.appManager.activatePlugin(self.workspace).then(async () => { try { if (params.deactivate) { - await appManager.deactivatePlugin(params.deactivate.split(',')) + await self.appManager.deactivatePlugin(params.deactivate.split(',')) } } catch (e) { console.log(e) } - if (params.code) { // if code is given in url we focus on solidity plugin - menuicons.select('solidity') + self.menuicons.select('solidity') } else { // If plugins are loaded from the URL params, we focus on the last one. - if (pluginLoader.current === 'queryParams' && workspace.length > 0) menuicons.select(workspace[workspace.length - 1]) + if (self.appManager.pluginLoader.current === 'queryParams' && self.workspace.length > 0) self.menuicons.select(self.workspace[self.workspace.length - 1]) } if (params.call) { @@ -502,21 +302,20 @@ class App { if (callDetails.length > 1) { toolTip(`initiating ${callDetails[0]} ...`) // @todo(remove the timeout when activatePlugin is on 0.3.0) - appManager.call(...callDetails).catch(console.error) + self.appManager.call(...callDetails).catch(console.error) } } }).catch(console.error) } }) // activate solidity plugin - appManager.activatePlugin(['solidity', 'udapp']) - + self.appManager.activatePlugin(['solidity', 'udapp']) // Load and start the service who manager layout and frame - const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature) + const framingService = new FramingService(self.sidePanel, self.menuicons, self.mainview, null) if (params.embed) framingService.embed() framingService.start(params) } } -module.exports = App +export default AppComponent diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index f25b04d21f..295d83fd59 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -46,7 +46,8 @@ class Editor extends Plugin { txt: 'text', json: 'json', abi: 'json', - rs: 'rust' + rs: 'rust', + cairo: 'cairo' } this.activated = false diff --git a/apps/remix-ide/src/app/tabs/theme-module.js b/apps/remix-ide/src/app/tabs/theme-module.js index 8558ad4da3..d3f5e9a98c 100644 --- a/apps/remix-ide/src/app/tabs/theme-module.js +++ b/apps/remix-ide/src/app/tabs/theme-module.js @@ -2,7 +2,6 @@ import { Plugin } from '@remixproject/engine' import { EventEmitter } from 'events' import QueryParams from '../../lib/query-params' import * as packageJson from '../../../../../package.json' -import yo from 'yo-yo' const _paq = window._paq = window._paq || [] const themes = [ @@ -37,10 +36,12 @@ export class ThemeModule extends Plugin { theme.url = window.location.origin + window.location.pathname + theme.url return { ...acc, [theme.name]: theme } }, {}) + this._paq = _paq let queryTheme = (new QueryParams()).get().theme queryTheme = this.themes[queryTheme] ? queryTheme : null let currentTheme = this._deps.config.get('settings/theme') currentTheme = this.themes[currentTheme] ? currentTheme : null + this.currentThemeState = { queryTheme, currentTheme } this.active = queryTheme || currentTheme || 'Dark' this.forced = !!queryTheme } @@ -58,11 +59,16 @@ export class ThemeModule extends Plugin { /** * Init the theme */ - initTheme (callback) { + initTheme (callback) { // callback is setTimeOut in app.js which is always passed + if (callback) this.initCallback = callback if (this.active) { const nextTheme = this.themes[this.active] // Theme document.documentElement.style.setProperty('--theme', nextTheme.quality) - const theme = yo`` + + const theme = document.createElement('link') + theme.setAttribute('rel', 'stylesheet') + theme.setAttribute('href', nextTheme.url) + theme.setAttribute('id', 'theme-link') theme.addEventListener('load', () => { if (callback) callback() }) @@ -79,12 +85,16 @@ export class ThemeModule extends Plugin { throw new Error(`Theme ${themeName} doesn't exist`) } const next = themeName || this.active // Name - if (next === this.active) return + if (next === this.active) return // --> exit out of this method _paq.push(['trackEvent', 'themeModule', 'switchTo', next]) const nextTheme = this.themes[next] // Theme if (!this.forced) this._deps.config.set('settings/theme', next) document.getElementById('theme-link').remove() - const theme = yo`` + const theme = document.createElement('link') + + theme.setAttribute('rel', 'stylesheet') + theme.setAttribute('href', nextTheme.url) + theme.setAttribute('id', 'theme-link') theme.addEventListener('load', () => { this.emit('themeLoaded', nextTheme) this.events.emit('themeLoaded', nextTheme) 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 744b5c988b..a100c4ea4e 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,110 +1,11 @@ +/* global */ +import React from 'react' // eslint-disable-line +import ReactDOM from 'react-dom' import * as packageJson from '../../../../../../package.json' import { ViewPlugin } from '@remixproject/engine-web' -import { migrateToWorkspace } from '../../../migrateFileSystem' -import JSZip from 'jszip' +import { RemixUiHomeTab } from '@remix-ui/home-tab' // eslint-disable-line -const yo = require('yo-yo') -const csjs = require('csjs-inject') -const globalRegistry = require('../../../global/registry') -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') -const _paq = window._paq = window._paq || [] - -const css = csjs` - .text { - cursor: pointer; - font-weight: normal; - max-width: 300px; - user-select: none; - } - .text:hover { - cursor: pointer; - text-decoration: underline; - } - .homeContainer { - user-select: none; - overflow-y: hidden; - } - .mainContent { - overflow-y: auto; - flex-grow: 3; - } - .hpLogoContainer { - margin: 30px; - padding-right: 90px; - } - .mediaBadge { - font-size: 2em; - height: 2em; - width: 2em; - } - .mediaBadge:focus { - outline: none; - } - .image { - height: 1em; - width: 1em; - text-align: center; - } - .logoImg { - height: 10em; - } - .hpSections { - } - .rightPanel { - right: 0; - position: absolute; - z-index: 3; - } - .remixHomeMedia { - overflow-y: auto; - overflow-x: hidden; - max-height: 720px; - } - .panels { - box-shadow: 0px 0px 13px -7px; - } - .labelIt { - margin-bottom: 0; - } - .bigLabelSize { - font-size: 13px; - } - .seeAll { - margin-top: 7px; - white-space: nowrap; - } - .importFrom p { - margin-right: 10px; - } - .logoContainer img{ - height: 150px; - opacity: 0.7; - } - .envLogo { - height: 16px; - } - .cursorStyle { - cursor: pointer; - } - .envButton { - width: 120px; - height: 70px; - } - .media { - overflow: hidden; - width: 400px; - transition: .5s ease-out; - z-index: 1000; - } - .migrationBtn { - width: 100px; - } -} -` const profile = { name: 'home', @@ -116,7 +17,6 @@ const profile = { location: 'mainPanel', version: packageJson.version } - export class LandingPage extends ViewPlugin { constructor (appManager, verticalIcons, fileManager, filePanel, contentImport) { super(profile) @@ -127,459 +27,22 @@ export class LandingPage extends ViewPlugin { this.appManager = appManager this.verticalIcons = verticalIcons this.gistHandler = new GistHandler() - const themeQuality = globalRegistry.get('themeModule').api.currentTheme().quality - window.addEventListener('resize', () => this.adjustMediaPanel()) - window.addEventListener('click', (e) => this.hideMediaPanel(e)) - this.twitterFrame = yo` -
- - - -
- ` - this.badgeTwitter = yo`` - this.badgeMedium = yo`` - this.twitterPanel = yo` -
- ${this.twitterFrame} -
- ` - this.mediumPanel = yo` -
-
-
--
-
-
- ` - this.adjustMediaPanel() - globalRegistry.get('themeModule').api.events.on('themeChanged', (theme) => { - this.onThemeChanged(theme.quality) - }) - } - - adjustMediaPanel () { - this.twitterPanel.style.maxHeight = Math.max(window.innerHeight - 150, 200) + 'px' - this.mediumPanel.style.maxHeight = Math.max(window.innerHeight - 150, 200) + 'px' - } - - hideMediaPanel (e) { - const mediaPanelsTitle = document.getElementById('remixIDEMediaPanelsTitle') - const mediaPanels = document.getElementById('remixIDEMediaPanels') - if (!mediaPanelsTitle || !mediaPanels) return - if (!mediaPanelsTitle.contains(e.target) && !mediaPanels.contains(e.target)) { - this.mediumPanel.classList.remove('d-block') - this.mediumPanel.classList.add('d-none') - this.twitterPanel.classList.remove('d-block') - this.twitterPanel.classList.add('d-none') - } - } - - onThemeChanged (themeQuality) { - const twitterFrame = yo` -
- - - -
- ` - yo.update(this.twitterFrame, twitterFrame) - - const invertNum = (themeQuality === 'dark') ? 1 : 0 - if (this.solEnv.getElementsByTagName('img')[0]) this.solEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - if (this.optimismEnv.getElementsByTagName('img')[0]) this.optimismEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - if (this.solhintEnv.getElementsByTagName('img')[0]) this.solhintEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - if (this.learnEthEnv.getElementsByTagName('img')[0]) this.learnEthEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - if (this.sourcifyEnv.getElementsByTagName('img')[0]) this.sourcifyEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - if (this.moreEnv.getElementsByTagName('img')[0]) this.moreEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - if (this.websiteIcon) this.websiteIcon.style.filter = `invert(${invertNum})` - } - - showMediaPanel (e) { - if (e.target.id === 'remixIDEHomeTwitterbtn') { - this.mediumPanel.classList.remove('d-block') - this.mediumPanel.classList.add('d-none') - this.twitterPanel.classList.toggle('d-block') - _paq.push(['trackEvent', 'pluginManager', 'media', 'twitter']) - } else { - this.twitterPanel.classList.remove('d-block') - this.twitterPanel.classList.add('d-none') - this.mediumPanel.classList.toggle('d-block') - _paq.push(['trackEvent', 'pluginManager', 'media', 'medium']) - } + this.el = document.createElement('div') + this.el.setAttribute('id', 'landingPageHomeContainer') + this.el.setAttribute('class', 'remixui_homeContainer justify-content-between bg-light d-flex') + this.el.setAttribute('data-id', 'landingPageHomeContainer') } render () { - const load = (service, item, examples, info) => { - const contentImport = this.contentImport - const fileProviders = globalRegistry.get('fileproviders').api - const msg = yo` -
- Enter the ${item} you would like to load. -
${info}
-
e.g ${examples.map((url) => { return yo`` })}
-
` - - const title = `Import from ${service}` - modalDialogCustom.prompt(title, msg, null, (target) => { - if (target !== '') { - contentImport.import( - target, - (loadingMsg) => { tooltip(loadingMsg) }, - (error, content, cleanUrl, type, url) => { - if (error) { - modalDialogCustom.alert(title, error.message || error) - } else { - try { - fileProviders.workspace.addExternal(type + '/' + cleanUrl, content, url) - this.verticalIcons.select('filePanel') - } catch (e) { - modalDialogCustom.alert(title, e.message) - } - } - } - ) - } - }) - } - - const startSolidity = async () => { - await this.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting']) - this.verticalIcons.select('solidity') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidity']) - } - const startOptimism = async () => { - await this.appManager.activatePlugin('optimism-compiler') - this.verticalIcons.select('optimism-compiler') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'optimism-compiler']) - } - const startSolhint = async () => { - await this.appManager.activatePlugin(['solidity', 'solhint']) - this.verticalIcons.select('solhint') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solhint']) - } - const startLearnEth = async () => { - await this.appManager.activatePlugin(['solidity', 'LearnEth', 'solidityUnitTesting']) - this.verticalIcons.select('LearnEth') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'learnEth']) - } - const startSourceVerify = async () => { - await this.appManager.activatePlugin(['solidity', 'source-verification']) - this.verticalIcons.select('source-verification') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'source-verification']) - } - const startPluginManager = async () => { - await this.appManager.activatePlugin('pluginManager') - this.verticalIcons.select('pluginManager') - } - const startRestoreBackupZip = async () => { - await this.appManager.activatePlugin(['restorebackupzip']) - this.verticalIcons.select('restorebackupzip') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'restorebackupzip']) - } - - const createNewFile = () => { - this.call('filePanel', '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 for download, please wait..') - const fileProviders = globalRegistry.get('fileproviders').api - const zip = new JSZip() - await fileProviders.browser.copyFolderToJson('/', ({ path, content }) => { - zip.file(`remixbackup${path}`, content) - }) - zip.generateAsync({ type: 'blob' }).then(function (blob) { - saveAs(blob, 'remixbackup.zip') - }).catch((e) => { - tooltip(e.message) - }) - } catch (e) { - tooltip(e.message) - } - } - - const uploadFile = (target) => { - this.call('filePanel', 'uploadFile', target) - } - - const connectToLocalhost = () => { - this.appManager.activatePlugin('remixd') - } - const importFromGist = () => { - this.gistHandler.loadFromGist({ gist: '' }, globalRegistry.get('filemanager').api) - this.verticalIcons.select('filePanel') - } - - globalRegistry.get('themeModule').api.events.on('themeChanged', (theme) => { - globalRegistry.get('themeModule').api.fixInvert(document.getElementById('remixLogo')) - globalRegistry.get('themeModule').api.fixInvert(document.getElementById('solidityLogo')) - globalRegistry.get('themeModule').api.fixInvert(document.getElementById('debuggerLogo')) - globalRegistry.get('themeModule').api.fixInvert(document.getElementById('learnEthLogo')) - globalRegistry.get('themeModule').api.fixInvert(document.getElementById('workshopLogo')) - globalRegistry.get('themeModule').api.fixInvert(document.getElementById('moreLogo')) - globalRegistry.get('themeModule').api.fixInvert(document.getElementById('solhintLogo')) - }) - - const createLargeButton = (imgPath, envID, envText, callback) => { - return yo` - - ` - } - - // main - this.solEnv = createLargeButton('assets/img/solidityLogo.webp', 'solidityLogo', 'Solidity', startSolidity) - // Featured - this.optimismEnv = createLargeButton('assets/img/optimismLogo.webp', 'optimismLogo', 'Optimism', startOptimism) - this.solhintEnv = createLargeButton('assets/img/solhintLogo.png', 'solhintLogo', 'Solhint linter', startSolhint) - this.learnEthEnv = createLargeButton('assets/img/learnEthLogo.webp', 'learnEthLogo', 'LearnEth', startLearnEth) - this.sourcifyEnv = createLargeButton('assets/img/sourcifyLogo.webp', 'sourcifyLogo', 'Sourcify', startSourceVerify) - this.moreEnv = createLargeButton('assets/img/moreLogo.webp', 'moreLogo', 'More', startPluginManager) - this.websiteIcon = yo`` - - const themeQuality = globalRegistry.get('themeModule').api.currentTheme().quality - const invertNum = (themeQuality === 'dark') ? 1 : 0 - this.solEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - this.optimismEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - this.solhintEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - this.learnEthEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - this.sourcifyEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - this.moreEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})` - this.websiteIcon.style.filter = `invert(${invertNum})` - - const switchToPreviousVersion = () => { - const query = new QueryParams() - query.update({ appVersion: '0.7.7' }) - _paq.push(['trackEvent', 'LoadingType', 'oldExperience_0.7.7']) - document.location.reload() - } - - const migrate = async () => { - try { - setTimeout(() => { - tooltip('migrating workspace...') - }, 500) - const workspaceName = await migrateToWorkspace(this.fileManager, this.filePanel) - tooltip('done. ' + workspaceName + ' created.') - } catch (e) { - setTimeout(() => { - tooltip(e.message) - }, 1000) - } - } - const onAcceptDownloadn = async () => { - await downloadFiles() - const el = document.getElementById('modal-dialog') - el.parentElement.removeChild(el) - migrate() - } - - const onDownload = () => { - const el = document.getElementById('modal-dialog') - el.parentElement.removeChild(el) - migrate() - } - - const onCancel = () => { - const el = document.getElementById('modal-dialog') - el.parentElement.removeChild(el) - } - - const migrateWorkspace = async () => { - modalDialog( - 'File system Migration', - yo` - Do you want to download your files to local device first? -
- - - -
- `, - { - label: '', - fn: null - }, - { - label: '', - fn: null - } - ) - } - - const img = yo`` - const playRemi = async () => { await document.getElementById('remiAudio').play() } - // to retrieve medium posts - document.body.appendChild(yo``) - const container = yo` -
-
-
-
-
-
- ${img} - -
-
Quicklinks
- Guide for migrating the old File System -

Migration tools:

-
  • - - migrateWorkspace()}>Basic migration - -
  • -
  • - downloadFiles()}>Download all Files - as a backup zip -
  • -
  • - startRestoreBackupZip()}>Restore filesfrom backup zip -
  • -

    Help:

    - - Gitter channel - Report on Github - -
    -
    -
    -
    -
    -
    -

    Featured Plugins

    -
    - ${this.solEnv} - ${this.optimismEnv} - ${this.learnEthEnv} - ${this.solhintEnv} - ${this.sourcifyEnv} - ${this.moreEnv} -
    -
    -
    -
    -

    File

    -

    - - createNewFile()}>New File -

    -

    - - -

    -

    - - connectToLocalhost()}>Connect to Localhost -

    -

    -
    - - - - -
    -
    -
    -

    Resources

    -

    - - Documentation -

    -

    - - Gitter channel -

    -

    - ${this.websiteIcon} - Featuring website -

    -

    - - switchToPreviousVersion()}>Old experience -

    -
    -
    -
    -
    -
    -
    -
    - ${this.badgeTwitter} - ${this.badgeMedium} -
    -
    - ${this.mediumPanel} - ${this.twitterPanel} -
    -
    -
    -
    -
    - ` + this.renderComponent() + return this.el + } - return container + renderComponent () { + ReactDOM.render( + + , this.el) } } diff --git a/apps/remix-ide/src/assets/img/solhintLogo.png b/apps/remix-ide/src/assets/img/solhintLogo.png deleted file mode 100644 index eab3cbb63b..0000000000 Binary files a/apps/remix-ide/src/assets/img/solhintLogo.png and /dev/null differ diff --git a/apps/remix-ide/src/assets/img/solhintLogo.webp b/apps/remix-ide/src/assets/img/solhintLogo.webp new file mode 100644 index 0000000000..cc9ecb3bea Binary files /dev/null and b/apps/remix-ide/src/assets/img/solhintLogo.webp differ diff --git a/apps/remix-ide/src/framingService.js b/apps/remix-ide/src/framingService.js index 75c217f18a..ed029b7a65 100644 --- a/apps/remix-ide/src/framingService.js +++ b/apps/remix-ide/src/framingService.js @@ -8,16 +8,6 @@ export class FramingService { } start (params) { - this.sidePanel.events.on('toggle', () => { - this.resizeFeature.panel.clientWidth !== 0 ? this.resizeFeature.hidePanel() : this.resizeFeature.showPanel() - }) - this.sidePanel.events.on('showing', () => { - if (this.resizeFeature.panel.clientWidth === 0) this.resizeFeature.showPanel() - }) - this.mainPanel.events.on('toggle', () => { - this.resizeFeature.showPanel() - }) - this.verticalIcons.select('filePanel') document.addEventListener('keypress', (e) => { diff --git a/apps/remix-ide/src/index.html b/apps/remix-ide/src/index.html index ebc8a47c71..94bb974028 100644 --- a/apps/remix-ide/src/index.html +++ b/apps/remix-ide/src/index.html @@ -56,6 +56,7 @@ +