Merge pull request #4273 from ethereum/userEnterModal

Added Enter Dialog.
pull/4300/head
yann300 1 year ago committed by GitHub
commit 715d5f9c3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      apps/remix-ide-e2e/src/helpers/init.ts
  2. 26
      apps/remix-ide-e2e/src/tests/migrateFileSystem.test.ts
  3. 4
      apps/remix-ide/src/app.js
  4. 28
      apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css
  5. 14
      apps/remix-ide/src/walkthroughService.js
  6. 60
      libs/remix-ui/app/src/lib/remix-app/components/modals/enter.tsx
  7. 8
      libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx
  8. 7
      libs/remix-ui/app/src/lib/remix-app/interface/index.ts
  9. 60
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  10. 20
      libs/remix-ui/app/src/lib/remix-app/types/index.ts
  11. 1
      libs/remix-ui/home-tab/src/lib/components/workspaceTemplate.tsx

@ -11,9 +11,7 @@ export default function (browser: NightwatchBrowser, callback: VoidFunction, url
browser
.url(url || 'http://127.0.0.1:8080')
//.switchBrowserTab(0)
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]')
.perform((done) => {
if (!loadPlugin) return done()
browser
@ -54,7 +52,6 @@ export default function (browser: NightwatchBrowser, callback: VoidFunction, url
if (preloadPlugins) {
initModules(browser, () => {
browser
.clickLaunchIcon('solidity')
.waitForElementVisible('[for="autoCompile"]')
.click('[for="autoCompile"]')

@ -11,8 +11,8 @@ module.exports = {
.maximizeWindow()
.waitForElementVisible('*[data-id="skipbackup-btn"]', 5000)
.click('*[data-id="skipbackup-btn"]')
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]')
.pause(5000)
.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 10000)
},
'Should load the testmigration url and refresh and still have test data #group7': function (browser: NightwatchBrowser) {
browser.url('http://127.0.0.1:8080?e2e_testmigration=true')
@ -21,8 +21,9 @@ module.exports = {
.maximizeWindow()
.waitForElementVisible('*[data-id="skipbackup-btn"]', 5000)
.click('*[data-id="skipbackup-btn"]')
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]').refreshPage()
.pause(5000)
.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 10000)
.refreshPage()
},
'should have indexedDB storage in terminal #group1 #group7': function (browser: NightwatchBrowser) {
browser.assert.containsText('*[data-id="terminalJournal"]', 'indexedDB')
@ -32,16 +33,17 @@ module.exports = {
.pause(6000)
.switchBrowserTab(0)
.maximizeWindow()
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]')
.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.pause(5000)
.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 10000)
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.openFile('README.txt')
.waitForElementVisible('*[id="editorView"]', 10000)
.getEditorValue((content) => {
browser.assert.ok(content.includes('Output from script will appear in remix terminal.'))
})
.click('*[data-id="treeViewLitreeViewItemcontracts"]')
.openFile('contracts/1_Storage.sol')
.waitForElementVisible('*[id="editorView"]', 10000)
.getEditorValue((content) => {
browser.assert.ok(content.includes('function retrieve() public view returns (uint256){'))
})
@ -53,8 +55,8 @@ module.exports = {
.maximizeWindow()
.waitForElementVisible('*[data-id="skipbackup-btn"]', 5000)
.click('*[data-id="skipbackup-btn"]')
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]')
.pause(5000)
.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 10000)
},
'Should generate error in migration by deleting indexedDB and falling back to local storage with test #group5': function (browser: NightwatchBrowser) {
browser.url('http://127.0.0.1:8080?e2e_testmigration=true')
@ -63,8 +65,8 @@ module.exports = {
.maximizeWindow().execute(('delete window.indexedDB'))
.waitForElementVisible('*[data-id="skipbackup-btn"]', 5000)
.click('*[data-id="skipbackup-btn"]')
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]')
.pause(5000)
.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 10000)
},
'should have localstorage storage in terminal #group2 #group3 #group5': function (browser: NightwatchBrowser) {
browser.assert.containsText('*[data-id="terminalJournal"]', 'localstorage')
@ -74,6 +76,7 @@ module.exports = {
.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.openFile('TEST_README.txt')
.waitForElementVisible('*[id="editorView"]', 10000)
.getEditorValue((content) => {
browser.assert.equal(content, 'TEST README')
})
@ -96,6 +99,7 @@ module.exports = {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.click('*[data-id="treeViewLitreeViewItemtest_contracts/artifacts"]')
.openFile('test_contracts/artifacts/Storage_metadata.json')
.waitForElementVisible('*[id="editorView"]', 10000)
.getEditorValue((content) => {
const metadata = JSON.parse(content)
browser.assert.equal(metadata.test, 'data')

@ -131,7 +131,9 @@ class AppComponent {
'6fd22d6fe5549ad4c4d8fd3ca0b7816b.mod': 35 // remix desktop
}
this.showMatamo = matomoDomains[window.location.hostname] && !Registry.getInstance().get('config').api.exists('settings/matomo-analytics')
this.walkthroughService = new WalkthroughService(appManager, this.showMatamo)
this.showEnter = matomoDomains[window.location.hostname] && !localStorage.getItem('hadUsageTypeAsked')
this.walkthroughService = new WalkthroughService(appManager, !this.showMatamo || !this.showEnter)
const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080']
// workaround for Electron support

@ -2386,8 +2386,8 @@ fieldset:disabled a.btn {
.btn-secondary {
color: #fff;
background-color: #a8b3bc;
border-color: #a8b3bc;
background-color: #8b98a3;
border-color: #8b98a3;
}
.btn-secondary:hover {
color: #fff;
@ -2401,8 +2401,8 @@ fieldset:disabled a.btn {
.btn-secondary.disabled,
.btn-secondary:disabled {
color: #fff;
background-color: #a8b3bc;
border-color: #a8b3bc;
background-color: #8b98a3;
border-color: #8b98a3;
}
.btn-secondary:not(:disabled):not(.disabled):active,
.btn-secondary:not(:disabled):not(.disabled).active,
@ -2656,13 +2656,13 @@ fieldset:disabled a.btn {
}
.btn-outline-secondary {
color: #a8b3bc;
border-color: #a8b3bc;
color: #8b98a3;
border-color: #8b98a3;
}
.btn-outline-secondary:hover {
color: #fff;
background-color: #a8b3bc;
border-color: #a8b3bc;
background-color: #8b98a3;
border-color: #8b98a3;
}
.btn-outline-secondary:focus,
.btn-outline-secondary.focus {
@ -2670,15 +2670,15 @@ fieldset:disabled a.btn {
}
.btn-outline-secondary.disabled,
.btn-outline-secondary:disabled {
color: #a8b3bc;
color: #8b98a3;
background-color: transparent;
}
.btn-outline-secondary:not(:disabled):not(.disabled):active,
.btn-outline-secondary:not(:disabled):not(.disabled).active,
.show > .btn-outline-secondary.dropdown-toggle {
color: #fff;
background-color: #a8b3bc;
border-color: #a8b3bc;
background-color: #8b98a3;
border-color: #8b98a3;
}
.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,
.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,
@ -4731,7 +4731,7 @@ a.badge-primary.focus {
.badge-secondary {
color: #fff;
background-color: #a8b3bc;
background-color: #8b98a3;
}
a.badge-secondary:hover,
a.badge-secondary:focus {
@ -6271,7 +6271,7 @@ button.bg-dark:focus {
}
.border-secondary {
border-color: #a8b3bc !important;
border-color: #8b98a3 !important;
}
.border-success {
@ -9308,7 +9308,7 @@ a.text-primary:focus {
}
.text-secondary {
color: #a8b3bc !important;
color: #8b98a3 !important;
}
a.text-secondary:hover,

@ -11,13 +11,21 @@ const profile = {
}
export class WalkthroughService extends Plugin {
constructor (appManager, showMatamo) {
constructor (appManager, showWalkthrough) {
super(profile)
appManager.event.on('activate', (plugin) => {
if (plugin.name === 'udapp' && !showMatamo) {
let readyToStart = 0;
/*appManager.event.on('activate', (plugin) => {
if (plugin.name === 'udapp') readyToStart++
if (readyToStart == 2 && showWalkthrough) {
this.start()
}
})
appManager.event.on('activate', (plugin) => {
if (plugin.name === 'solidity') readyToStart++
if (readyToStart == 2 && showWalkthrough) {
this.start()
}
})*/
}
start () {

@ -0,0 +1,60 @@
import React, {useContext, useEffect, useState} from 'react'
import {AppContext} from '../../context/context'
import {UsageTypes} from '../../types'
import { type } from 'os'
interface EnterDialogProps {
hide: boolean,
handleUserChoice: (userChoice: UsageTypes) => void,
}
const EnterDialog = (props: EnterDialogProps) => {
const [visibility, setVisibility] = useState<boolean>(false)
const {showEnter} = useContext(AppContext)
useEffect(() => {
setVisibility(!props.hide)
}, [props.hide])
const enterAs = async (uType) => {
props.handleUserChoice(uType)
}
const modalClass = (visibility && showEnter) ? "d-flex" : "d-none"
return (
<div
data-id={`EnterModalDialogContainer-react`}
data-backdrop="static"
data-keyboard="false"
className={"modal " + modalClass}
role="dialog"
>
<div className="modal-dialog align-self-center pb-4" role="document">
<div
tabIndex={-1}
className={'modal-content remixModalContent mb-4'}
onKeyDown={({keyCode}) => {
}}
>
<div className="modal-header d-flex flex-column">
<h3 className='text-dark'>Welcome to Remix IDE</h3>
<div className='d-flex flex-row pt-2'>
<h6 className="modal-title" data-id={`EnterModalDialogModalTitle-react`}>
To load the project with the most efficient setup we would like to know your experience type.
</h6>
<i className="text-dark fal fa-door-open text-center" style={{minWidth: "100px", fontSize: "xxx-large"}}></i>
</div>
</div>
<div className="modal-body text-break remixModalBody d-flex flex-row p-3 justify-content-between" data-id={`EnterModalDialogModalBody-react`}>
<button className="btn-secondary" data-id="beginnerbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Beginner)}}>Beginner</button>
<button className="btn-secondary" data-id="tutorbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Tutor)}}>Teacher</button>
<button className="btn-secondary" data-id="prototyperbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Prototyper)}}>Prototyper</button>
<button className="btn-secondary" data-id="productionbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Production)}}>Production User</button>
</div>
</div>
</div>
</div>
)
}
export default EnterDialog

@ -25,11 +25,7 @@ const MatomoDialog = (props) => {
</p>
<p>We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.</p>
<p>
All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public:{' '}
<a href="https://matomo.ethereum.org/index.php?module=MultiSites&action=index&idSite=23&period=day&date=yesterday" target="_blank" rel="noreferrer">
take a look
</a>
.
All data collected through Matomo is stored on our own server - no data is ever given to third parties.
</p>
<p>We do not collect nor store any personally identifiable information (PII).</p>
<p>
@ -61,14 +57,12 @@ const MatomoDialog = (props) => {
const declineModal = async () => {
settings.updateMatomoAnalyticsChoice(false)
_paq.push(['optUserOut'])
appManager.call('walkthrough', 'start')
setVisible(false)
}
const handleModalOkClick = async () => {
_paq.push(['forgetUserOptOut'])
settings.updateMatomoAnalyticsChoice(true)
appManager.call('walkthrough', 'start')
setVisible(false)
}

@ -37,3 +37,10 @@ export interface ModalState {
focusModal: AppModal,
focusToaster: {message: (string | JSX.Element), timestamp: number }
}
export interface forceChoiceModal {
id: string
title?: string,
message: string | JSX.Element,
}

@ -2,6 +2,7 @@ import React, {useEffect, useRef, useState} from 'react'
import './style/remix-app.css'
import {RemixUIMainPanel} from '@remix-ui/panel'
import MatomoDialog from './components/modals/matomo'
import EnterDialog from './components/modals/enter'
import OriginWarning from './components/modals/origin-warning'
import DragBar from './components/dragbar/dragbar'
import {AppProvider} from './context/provider'
@ -10,12 +11,21 @@ import DialogViewPlugin from './components/modals/dialogViewPlugin'
import {AppContext} from './context/context'
import {IntlProvider, FormattedMessage} from 'react-intl'
import {CustomTooltip} from '@remix-ui/helper'
import {UsageTypes} from './types'
declare global {
interface Window {
_paq: any
}
}
const _paq = (window._paq = window._paq || [])
interface IRemixAppUi {
app: any
}
const RemixApp = (props: IRemixAppUi) => {
const [appReady, setAppReady] = useState<boolean>(false)
const [showEnterDialog, setShowEnterDialog] = useState<boolean>(true)
const [hideSidePanel, setHideSidePanel] = useState<boolean>(false)
const [maximiseTrigger, setMaximiseTrigger] = useState<number>(0)
const [resetTrigger, setResetTrigger] = useState<number>(0)
@ -37,6 +47,8 @@ const RemixApp = (props: IRemixAppUi) => {
if (props.app) {
activateApp()
}
const hadUsageTypeAsked = localStorage.getItem('hadUsageTypeAsked')
setShowEnterDialog(!hadUsageTypeAsked)
}, [])
function setListeners() {
@ -75,22 +87,66 @@ const RemixApp = (props: IRemixAppUi) => {
const value = {
settings: props.app.settings,
showMatamo: props.app.showMatamo,
showEnter: props.app.showEnter,
appManager: props.app.appManager,
modal: props.app.notification,
layout: props.app.layout
}
const handleUserChosenType = async (type) => {
setShowEnterDialog(false)
localStorage.setItem('hadUsageTypeAsked', type)
await props.app.appManager.call('walkthrough', 'start')
// Use the type to setup the UI accordingly
switch (type) {
case UsageTypes.Beginner: {
await props.app.appManager.call('manager', 'activatePlugin', 'LearnEth')
// const wName = 'Playground'
// const workspaces = await props.app.appManager.call('filePanel', 'getWorkspaces')
// if (!workspaces.find((workspace) => workspace.name === wName)) {
// await props.app.appManager.call('filePanel', 'createWorkspace', wName, 'playground')
// }
// await props.app.appManager.call('filePanel', 'switchToWorkspace', { name: wName, isLocalHost: false })
_paq.push(['trackEvent', 'enterDialog', 'usageType', 'beginner'])
break
}
case UsageTypes.Tutor: {
_paq.push(['trackEvent', 'enterDialog', 'usageType', 'tutor'])
break
}
case UsageTypes.Prototyper: {
_paq.push(['trackEvent', 'enterDialog', 'usageType', 'prototyper'])
break
}
case UsageTypes.Production: {
_paq.push(['trackEvent', 'enterDialog', 'usageType', 'production'])
break
}
default: throw new Error()
}
}
return (
//@ts-ignore
<IntlProvider locale={locale.code} messages={locale.messages}>
<AppProvider value={value}>
<OriginWarning></OriginWarning>
<MatomoDialog hide={!appReady}></MatomoDialog>
<MatomoDialog hide={!appReady} okFn={() => {setShowEnterDialog(true)}}></MatomoDialog>
<EnterDialog hide={!showEnterDialog} handleUserChoice={(type) => handleUserChosenType(type)}></EnterDialog>
<div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE">
<div id="icon-panel" data-id="remixIdeIconPanel" className="custom_icon_panel iconpanel bg-light">
{props.app.menuicons.render()}
</div>
<div ref={sidePanelRef} id="side-panel" data-id="remixIdeSidePanel" className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}>
<div
ref={sidePanelRef}
id="side-panel"
data-id="remixIdeSidePanel"
className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}
>
{props.app.sidePanel.render()}
</div>
<DragBar

@ -1,8 +1,16 @@
export const enum ModalTypes {
alert = 'alert',
confirm = 'confirm',
prompt = 'prompt',
password = 'password',
default = 'default',
form = 'form',
alert = 'alert',
confirm = 'confirm',
prompt = 'prompt',
password = 'password',
default = 'default',
form = 'form',
forceChoice = 'forceChoice'
}
export const enum UsageTypes {
Beginner = 1,
Tutor,
Prototyper,
Production,
}

@ -14,7 +14,6 @@ interface WorkspaceTemplateProps {
function WorkspaceTemplate({gsID, workspaceTitle, description, projectLogo, callback}: WorkspaceTemplateProps) {
const themeFilter = useContext(ThemeContext)
console.log("theme ", themeFilter)
return (
<div className="d-flex remixui_home_workspaceTemplate">
<button

Loading…
Cancel
Save