Merge branch 'master' into desktope2e-remixai

pull/5100/head
Aniket 2 months ago committed by GitHub
commit 9ebd5f7d54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 492
      apps/remix-ide-e2e/src/tests/matomo.test.ts
  2. 22
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  3. 19
      apps/remix-ide/src/app.js
  4. 2
      apps/remix-ide/src/app/components/preload.tsx
  5. 2
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  6. 4
      apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css
  7. 3
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  8. 2
      apps/remix-ide/src/assets/css/themes/bootstrap-flatly.min.css
  9. 3
      apps/remix-ide/src/assets/css/themes/bootstrap-spacelab.min.css
  10. 1
      apps/remix-ide/src/assets/css/themes/remix-black_undtds.css
  11. 1
      apps/remix-ide/src/assets/css/themes/remix-candy_ikhg4m.css
  12. 1
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  13. 1
      apps/remix-ide/src/assets/css/themes/remix-hacker_owl.css
  14. 1
      apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css
  15. 1
      apps/remix-ide/src/assets/css/themes/remix-midcentury_hrzph3.css
  16. 1
      apps/remix-ide/src/assets/css/themes/remix-unicorn.css
  17. 1
      apps/remix-ide/src/assets/css/themes/remix-violet.css
  18. 20
      apps/remix-ide/src/assets/js/loader.js
  19. 2
      libs/remix-ui/app/src/index.ts
  20. 19
      libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx
  21. 8
      libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx
  22. 2
      libs/remix-ui/app/src/lib/remix-app/context/context.tsx
  23. 5
      libs/remix-ui/app/src/lib/remix-app/context/provider.tsx
  24. 7
      libs/remix-ui/app/src/lib/remix-app/interface/index.ts
  25. 3
      libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts
  26. 4
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  27. 9
      libs/remix-ui/app/src/lib/remix-app/types/index.ts
  28. 24
      libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx
  29. 6
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css
  30. 23
      libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx
  31. 7
      libs/remix-ui/modal-dialog/src/lib/types/index.ts
  32. 2
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  33. 2
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  34. 1
      libs/remix-ui/settings/src/lib/settingsAction.ts
  35. 4
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.css

@ -0,0 +1,492 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import examples from '../examples/example-contracts'
const sources = [
{ 'Untitled.sol': { content: examples.ballot.content } }
]
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
},
'confirm Matomo #group1': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser
.execute(function () {
localStorage.removeItem('config-v0.8:.remix.config')
localStorage.setItem('showMatomo', 'true')
}, [])
.refreshPage()
.perform(done())
})
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.execute(function () {
return (window as any)._paq
}, [], (res) => {
console.log('_paq', res)
})
.waitForElementVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.pause(1000)
.click('[data-id="matomoModal-modal-footer-ok-react"]') // submitted
.execute(function () {
return (window as any)._paq
}, [], (res) => {
console.log('_paq', res)
})
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.waitForElementVisible('*[data-id="beginnerbtn"]', 10000)
.pause(1000)
.click('[data-id="beginnerbtn"]')
.waitForElementNotPresent('*[data-id="beginnerbtn"]')
.waitForElementVisible({
selector: `//*[contains(text(), 'Welcome to Remix IDE')]`,
locateStrategy: 'xpath'
})
.refreshPage()
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.waitForElementNotPresent('*[data-id="matomoModalModalDialogModalBody-react"]')
.clickLaunchIcon('settings')
.verify.elementPresent('[id="settingsMatomoAnalytics"]:checked')
.execute(function () {
return JSON.parse(window.localStorage.getItem('config-v0.8:.remix.config'))['settings/matomo-analytics'] == true
}, [], (res) => {
console.log('res', res)
browser.assert.ok((res as any).value, 'matomo analytics is enabled')
})
},
'decline Matomo #group1': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
localStorage.removeItem('config-v0.8:.remix.config')
localStorage.setItem('showMatomo', 'true')
localStorage.removeItem('matomo-analytics-consent')
}, [])
.refreshPage()
.perform(done())
})
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.execute(function () {
return (window as any)._paq
}, [], (res) => {
console.log('_paq', res)
})
.waitForElementVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.click('[data-id="matomoModal-modal-footer-cancel-react"]') // cancel
.execute(function () {
return (window as any)._paq
}, [], (res) => {
console.log('_paq', res)
})
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.pause(2000)
.waitForElementNotPresent('*[data-id="beginnerbtn"]', 10000)
.clickLaunchIcon('settings')
.waitForElementNotPresent('[id="settingsMatomoAnalytics"]:checked')
.execute(function () {
return JSON.parse(window.localStorage.getItem('config-v0.8:.remix.config'))['settings/matomo-analytics'] == false
}, [], (res) => {
console.log('res', res)
browser.assert.ok((res as any).value, 'matomo analytics is disabled')
})
},
'blur matomo #group2': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
localStorage.removeItem('config-v0.8:.remix.config')
localStorage.setItem('showMatomo', 'true')
localStorage.removeItem('matomo-analytics-consent')
}, [])
.refreshPage()
.perform(done())
})
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.waitForElementVisible({
selector: '*[data-id="matomoModalModalDialogModalBody-react"]',
abortOnFailure: true
})
.waitForElementVisible('*[data-id="matomoModal-modal-close"]')
.click('[data-id="matomoModal-modal-close"]')
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.pause(2000)
.waitForElementNotPresent('*[data-id="beginnerbtn"]', 10000)
.clickLaunchIcon('settings')
.waitForElementNotPresent('[id="settingsMatomoAnalytics"]:checked')
.execute(function () {
return JSON.parse(window.localStorage.getItem('config-v0.8:.remix.config'))['settings/matomo-analytics'] == undefined
}, [], (res) => {
console.log('res', res)
browser.assert.ok((res as any).value, 'matomo analytics is undefined')
})
},
'matomo should reappear #group2': function (browser: NightwatchBrowser) {
browser
.refreshPage()
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.waitForElementVisible({
selector: '*[data-id="matomoModalModalDialogModalBody-react"]',
abortOnFailure: true
})
.waitForElementVisible('*[data-id="matomoModal-modal-close"]')
.click('[data-id="matomoModal-modal-close"]')
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
},
'change settings #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('settings')
.waitForElementVisible('*[data-id="label-matomo-settings"]')
.pause(1000)
.click('*[data-id="label-matomo-settings"]')
.refreshPage()
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.waitForElementNotPresent('*[data-id="matomoModalModalDialogModalBody-react"]')
},
'should get enter dialog again #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="beginnerbtn"]', 10000)
.pause(1000)
.click('[data-id="beginnerbtn"]')
.waitForElementNotPresent('*[data-id="beginnerbtn"]')
.waitForElementVisible({
selector: `//*[contains(text(), 'Welcome to Remix IDE')]`,
locateStrategy: 'xpath'
})
.waitForElementVisible('*[id="remixTourSkipbtn"]')
.click('*[id="remixTourSkipbtn"]')
.clickLaunchIcon('settings')
.waitForElementPresent('[id="settingsMatomoAnalytics"]:checked')
.execute(function () {
return JSON.parse(window.localStorage.getItem('config-v0.8:.remix.config'))['settings/matomo-analytics'] == true
}, [], (res) => {
console.log('res', res)
browser.assert.ok((res as any).value, 'matomo analytics is enabled')
})
},
'decline Matomo and check timestamp #group3': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
localStorage.removeItem('config-v0.8:.remix.config')
localStorage.setItem('showMatomo', 'true')
localStorage.removeItem('matomo-analytics-consent')
}, [])
.refreshPage()
.perform(done())
})
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
// output the contents of the storage
.execute(function () {
return {
consent: window.localStorage.getItem('matomo-analytics-consent'),
config: window.localStorage.getItem('config-v0.8:.remix.config'),
showMatomo: window.localStorage.getItem('showMatomo')
}
}, [], (res) => {
console.log('res', res)
})
.waitForElementVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.click('[data-id="matomoModal-modal-footer-cancel-react"]') // cancel
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.pause(2000)
.execute(function () {
const timestamp = window.localStorage.getItem('matomo-analytics-consent');
if (timestamp) {
const consentDate = new Date(Number(timestamp));
// validate it is actually a date
if (isNaN(consentDate.getTime())) {
return false;
}
const now = new Date();
console.log('timestamp', timestamp, consentDate, now.getTime())
const diffInMinutes = (now.getTime() - consentDate.getTime()) / (1000 * 60);
console.log('diffInMinutes', diffInMinutes)
return diffInMinutes < 2;
}
return false;
}, [], (res) => {
console.log('res', res)
browser.assert.ok((res as any).value, 'matomo analytics consent timestamp is set')
})
},
'check old timestamp and reappear Matomo #group3': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
const oldTimestamp = new Date()
oldTimestamp.setMonth(oldTimestamp.getMonth() - 7)
localStorage.setItem('matomo-analytics-consent', oldTimestamp.getTime().toString())
}, [])
.refreshPage()
.perform(done())
})
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.execute(function () {
const timestamp = window.localStorage.getItem('matomo-analytics-consent');
if (timestamp) {
const consentDate = new Date(Number(timestamp));
// validate it is actually a date
if (isNaN(consentDate.getTime())) {
return false;
}
// validate it's older than 6 months
const now = new Date();
const diffInMonths = (now.getFullYear() - consentDate.getFullYear()) * 12 + now.getMonth() - consentDate.getMonth();
console.log('timestamp', timestamp, consentDate, now.getTime())
console.log('diffInMonths', diffInMonths)
return diffInMonths > 6;
}
return false;
}, [], (res) => {
console.log('res', res)
browser.assert.ok((res as any).value, 'matomo analytics consent timestamp is set')
})
.waitForElementVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.click('[data-id="matomoModal-modal-footer-cancel-react"]') // cancel
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
},
'check recent timestamp and do not reappear Matomo #group3': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
const recentTimestamp = new Date()
recentTimestamp.setMonth(recentTimestamp.getMonth() - 1)
localStorage.setItem('matomo-analytics-consent', recentTimestamp.getTime().toString())
}, [])
.refreshPage()
.perform(done())
})
// check if timestamp is younger than 6 months
.execute(function () {
const timestamp = window.localStorage.getItem('matomo-analytics-consent');
if (timestamp) {
const consentDate = new Date(Number(timestamp));
// validate it is actually a date
if (isNaN(consentDate.getTime())) {
return false;
}
// validate it's younger than 2 months
const now = new Date();
const diffInMonths = (now.getFullYear() - consentDate.getFullYear()) * 12 + now.getMonth() - consentDate.getMonth();
console.log('timestamp', timestamp, consentDate, now.getTime())
console.log('diffInMonths', diffInMonths)
return diffInMonths < 2;
}
return false;
}, [], (res) => {
console.log('res', res)
browser.assert.ok((res as any).value, 'matomo analytics consent timestamp is set')
})
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.pause(2000)
.waitForElementNotPresent('*[data-id="matomoModalModalDialogModalBody-react"]')
},
'accept Matomo and check timestamp #group3': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
localStorage.removeItem('config-v0.8:.remix.config')
localStorage.setItem('showMatomo', 'true')
localStorage.removeItem('matomo-analytics-consent')
}, [])
.refreshPage()
.perform(done())
})
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.waitForElementVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.click('[data-id="matomoModal-modal-footer-ok-react"]') // accept
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.pause(2000)
.execute(function () {
const timestamp = window.localStorage.getItem('matomo-analytics-consent');
if (timestamp) {
const consentDate = new Date(Number(timestamp));
// validate it is actually a date
if (isNaN(consentDate.getTime())) {
return false;
}
const now = new Date();
console.log('timestamp', timestamp, consentDate, now.getTime())
const diffInMinutes = (now.getTime() - consentDate.getTime()) / (1000 * 60);
console.log('diffInMinutes', diffInMinutes)
return diffInMinutes < 1;
}
return false;
}, [], (res) => {
console.log('res', res)
browser.assert.ok((res as any).value, 'matomo analytics consent timestamp is to a recent date')
})
},
'check old timestamp and do not reappear Matomo after accept #group3': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
const oldTimestamp = new Date()
oldTimestamp.setMonth(oldTimestamp.getMonth() - 7)
localStorage.setItem('matomo-analytics-consent', oldTimestamp.getTime().toString())
}, [])
.refreshPage()
.perform(done())
})
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.pause(2000)
.waitForElementNotPresent('*[data-id="matomoModalModalDialogModalBody-react"]')
},
'check recent timestamp and do not reappear Matomo after accept #group3': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
const recentTimestamp = new Date()
recentTimestamp.setMonth(recentTimestamp.getMonth() - 1)
localStorage.setItem('matomo-analytics-consent', recentTimestamp.getTime().toString())
}, [])
.refreshPage()
.perform(done())
})
.waitForElementPresent({
selector: `//*[@data-id='compilerloaded']`,
locateStrategy: 'xpath',
timeout: 120000
})
.pause(2000)
.waitForElementNotPresent('*[data-id="matomoModalModalDialogModalBody-react"]')
},
'when there is a recent timestamp but no config the dialog should reappear #group3': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
localStorage.removeItem('config-v0.8:.remix.config')
const recentTimestamp = new Date()
recentTimestamp.setMonth(recentTimestamp.getMonth() - 1)
localStorage.setItem('matomo-analytics-consent', recentTimestamp.getTime().toString())
}, [])
.refreshPage()
.perform(done())
})
.waitForElementVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.click('[data-id="matomoModal-modal-footer-cancel-react"]') // cancel
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
},
'when there is a old timestamp but no config the dialog should reappear #group3': function (browser: NightwatchBrowser) {
browser.perform((done) => {
browser.execute(function () {
localStorage.removeItem('config-v0.8:.remix.config')
const oldTimestamp = new Date()
oldTimestamp.setMonth(oldTimestamp.getMonth() - 7)
localStorage.setItem('matomo-analytics-consent', oldTimestamp.getTime().toString())
}, [])
.refreshPage()
.perform(done())
})
.waitForElementVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
.click('[data-id="matomoModal-modal-footer-cancel-react"]') // cancel
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
},
'verify Matomo events are tracked on app start #group4 #lfaky': function (browser: NightwatchBrowser) {
browser
.execute(function () {
return (window as any)._paq
}, [], (res) => {
const expectedEvents = [
["trackEvent", "Preload", "start"],
["trackEvent", "Storage", "activate", "indexedDB"],
["trackEvent", "App", "load"],
];
const actualEvents = (res as any).value;
const areEventsPresent = expectedEvents.every(expectedEvent =>
actualEvents.some(actualEvent =>
JSON.stringify(actualEvent) === JSON.stringify(expectedEvent)
)
);
browser.assert.ok(areEventsPresent, 'Matomo events are tracked correctly');
})
},
'@sources': function () {
return sources
},
'Add Ballot #group4': function (browser: NightwatchBrowser) {
browser
.addFile('Untitled.sol', sources[0]['Untitled.sol'])
},
'Deploy Ballot #group4': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('solidity')
.waitForElementVisible('*[data-id="compilerContainerCompileBtn"]')
.click('*[data-id="compilerContainerCompileBtn"]')
.testContracts('Untitled.sol', sources[0]['Untitled.sol'], ['Ballot'])
},
'verify Matomo compiler events are tracked #group4': function (browser: NightwatchBrowser) {
browser
.execute(function () {
return (window as any)._paq
}, [], (res) => {
const expectedEvent = ["trackEvent", "compiler", "compiled"];
const actualEvents = (res as any).value;
const isEventPresent = actualEvents.some(actualEvent =>
actualEvent[0] === expectedEvent[0] &&
actualEvent[1] === expectedEvent[1] &&
actualEvent[2] === expectedEvent[2] &&
actualEvent[3].startsWith("with_version_")
);
browser.assert.ok(isEventPresent, 'Matomo compiler events are tracked correctly');
})
},
}

@ -361,18 +361,16 @@ module.exports = {
Resolver resolver = ens.resolver(node);
console.log(resolver.addr(node));
}
`
`
if (runMasterTests) {
const path = "//*[@class='view-line' and contains(.,'resolveENS') and contains(.,'view')]//span//span[contains(.,'(')]"
browser
// .clickLaunchIcon('udapp')
.switchEnvironment('vm-mainnet-fork')
.clickLaunchIcon('filePanel')
.addFile('test_mainnet.sol', { content: script })
const path = "//*[@class='view-line' and contains(.,'resolveENS') and contains(.,'view')]//span//span[contains(.,'(')]"
const pathRunFunction = `//li//*[@aria-label='Run the free function "resolveENS"']`
browser.waitForElementVisible('#editorView')
//.waitForElementPresent(pathRunFunction)
.waitForElementVisible('#editorView')
.pause(10000) // the parser need to parse the code
.useXpath()
.scrollToLine(16)
@ -396,15 +394,15 @@ module.exports = {
console.log("test running free function");
}
`
const path = "//*[@class='view-line' and contains(.,'runSomething') and contains(.,'view')]//span//span[contains(.,'(')]"
browser
.addFile('test.sol', { content: script })
.scrollToLine(3)
const path = "//*[@class='view-line' and contains(.,'runSomething') and contains(.,'view')]//span//span[contains(.,'(')]"
const pathRunFunction = `//li//*[@aria-label='Run the free function "runSomething"']`
browser.waitForElementVisible('#editorView')
.waitForElementVisible('#editorView')
.pause(10000) // the parser need to parse the code
.useXpath()
.click(path)
.pause(3000) // the parser need to parse the code
.scrollToLine(3)
.click(path)
.perform(function () {
const actions = this.actions({ async: true });
return actions

@ -102,6 +102,7 @@ const Editor = require('./app/editor/editor')
const Terminal = require('./app/panels/terminal')
const { TabProxy } = require('./app/panels/tab-proxy.js')
const _paq = (window._paq = window._paq || [])
export class platformApi {
get name() {
@ -177,16 +178,24 @@ class AppComponent {
'6fd22d6fe5549ad4c4d8fd3ca0b7816b.mod': 35 // remix desktop
}
_paq.push(['trackEvent', 'App', 'load']);
this.matomoConfAlreadySet = Registry.getInstance().get('config').api.exists('settings/matomo-analytics')
this.matomoCurrentSetting = Registry.getInstance().get('config').api.get('settings/matomo-analytics')
let electronTracking = false
let electronTracking = window.electronAPI ? await window.electronAPI.canTrackMatomo() : false
if (window.electronAPI) {
electronTracking = await window.electronAPI.canTrackMatomo()
}
const lastMatomoCheck = window.localStorage.getItem('matomo-analytics-consent')
const sixMonthsAgo = new Date();
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
const e2eforceMatomoToShow = window.localStorage.getItem('showMatomo') && window.localStorage.getItem('showMatomo') === 'true'
const contextShouldShowMatomo = matomoDomains[window.location.hostname] || e2eforceMatomoToShow || electronTracking
const shouldRenewConsent = this.matomoCurrentSetting === false && (!lastMatomoCheck || new Date(Number(lastMatomoCheck)) < sixMonthsAgo) // it is set to false for more than 6 months.
this.showMatomo = contextShouldShowMatomo && (!this.matomoConfAlreadySet || shouldRenewConsent)
this.showMatamo = (matomoDomains[window.location.hostname] || electronTracking) && !this.matomoConfAlreadySet
if (this.showMatomo && shouldRenewConsent) {
_paq.push(['trackEvent', 'Matomo', 'refreshMatomoPermissions']);
}
this.walkthroughService = new WalkthroughService(appManager)

@ -10,6 +10,8 @@ import './styles/preload.css'
import isElectron from 'is-electron'
const _paq = (window._paq = window._paq || [])
_paq.push(['trackEvent', 'Preload', 'start'])
export const Preload = (props: any) => {
const [tip, setTip] = useState<string>('')
const [supported, setSupported] = useState<boolean>(true)

@ -109,6 +109,8 @@ module.exports = class SettingsTab extends ViewPlugin {
updateMatomoAnalyticsChoice(isChecked) {
this.config.set('settings/matomo-analytics', isChecked)
// set timestamp to local storage to track when the user has given consent
localStorage.setItem('matomo-analytics-consent', Date.now().toString())
this.useMatomoAnalytics = isChecked
if (!isChecked) {
// revoke tracking consent

@ -5045,8 +5045,10 @@ a.close.disabled {
}
.modal-header .close {
padding:1rem 1rem;
margin:-1rem -1rem -1rem auto
margin:-1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {
margin-bottom:0;
line-height:1.5

@ -5047,7 +5047,8 @@ a.close.disabled {
}
.modal-header .close {
padding:1rem 1rem;
margin:-1rem -1rem -1rem auto
margin:-1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {
margin-bottom:0;

@ -4153,7 +4153,7 @@ a.close.disabled {
display:-ms-flexbox; display:flex; -ms-flex-align:start; align-items:flex-start; -ms-flex-pack:justify; justify-content:space-between; padding:1rem 1rem; border-bottom:1px solid #dee2e6; border-top-left-radius:calc(.3rem - 1px); border-top-right-radius:calc(.3rem - 1px)
}
.modal-header .close {
padding:1rem 1rem; margin:-1rem -1rem -1rem auto
padding:1rem 1rem; margin:-1rem -1rem -1rem auto; cursor: pointer;
}
.modal-title {
margin-bottom:0; line-height:1.5

@ -5048,7 +5048,8 @@ a.close.disabled {
}
.modal-header .close {
padding:1rem 1rem;
margin:-1rem -1rem -1rem auto
margin:-1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {
margin-bottom:0;

@ -5080,6 +5080,7 @@ a.close.disabled {
.modal-header .close {
padding: 15px;
margin: -1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {
margin-bottom: 0;

@ -5540,6 +5540,7 @@ a.close.disabled {
.modal-header .close {
padding: 1rem 1rem;
margin: -1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {

@ -5082,6 +5082,7 @@ a.close.disabled {
.modal-header .close {
padding: 15px;
margin: -1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {
margin-bottom: 0;

@ -5092,6 +5092,7 @@ a.close.disabled {
.modal-header .close {
padding: 15px;
margin: -1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {
margin-bottom: 0;

@ -5536,6 +5536,7 @@ a.close.disabled {
.modal-header .close {
padding: 1rem 1rem;
margin: -1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {

@ -5542,6 +5542,7 @@ a.close.disabled {
.modal-header .close {
padding: 1rem 1rem;
margin: -1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {

@ -5540,6 +5540,7 @@ a.close.disabled {
.modal-header .close {
padding: 1rem 1rem;
margin: -1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {

@ -5536,6 +5536,7 @@ a.close.disabled {
.modal-header .close {
padding: 1rem 1rem;
margin: -1rem -1rem -1rem auto;
cursor: pointer;
}
.modal-title {

@ -18,14 +18,26 @@ function trackDomain(domainToTrack) {
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
_paq.push(['enableHeartBeatTimer']);
if (!window.localStorage.getItem('config-v0.8:.remix.config') ||
(window.localStorage.getItem('config-v0.8:.remix.config') && !window.localStorage.getItem('config-v0.8:.remix.config').includes('settings/matomo-analytics'))) {
const remixConfig = window.localStorage.getItem('config-v0.8:.remix.config');
if (!remixConfig || (remixConfig && !remixConfig.includes('settings/matomo-analytics'))) {
// require user tracking consent before processing data
_paq.push(['requireConsent']);
} else {
// user has given consent to process their data
_paq.push(['setConsentGiven'])
try {
const config = JSON.parse(remixConfig);
if (config['settings/matomo-analytics'] === true) {
// user has given consent to process their data
_paq.push(['setConsentGiven']);
} else {
// user has not given consent to process their data
_paq.push(['requireConsent']);
}
} catch (e) {
console.error('Error parsing remix config:', e);
_paq.push(['requireConsent']);
}
}
_paq.push(['trackEvent', 'loader', 'load']);
(function () {
var u = "https://ethereumfoundation.matomo.cloud/";
_paq.push(['setTrackerUrl', u + 'matomo.php?debug=1']);

@ -3,5 +3,5 @@ export { dispatchModalContext, dispatchModalInterface, AppContext, appProviderCo
export { ModalProvider, useDialogDispatchers } from './lib/remix-app/context/provider'
export { AppModal } from './lib/remix-app/interface/index'
export { AlertModal } from './lib/remix-app/interface/index'
export { ModalTypes } from './lib/remix-app/types/index'
export { ModalTypes, AppModalCancelTypes } from './lib/remix-app/types/index'
export { AppAction, appActionTypes } from './lib/remix-app/actions/app'

@ -2,6 +2,7 @@ import React, { useContext, useEffect, useState } from 'react'
import { FormattedMessage } from 'react-intl'
import { AppContext } from '../../context/context'
import { useDialogDispatchers } from '../../context/provider'
import { AppModalCancelTypes } from '../../types'
declare global {
interface Window {
_paq: any
@ -15,7 +16,7 @@ interface MatomoDialogProps {
}
const MatomoDialog = (props: MatomoDialogProps) => {
const { settings, showMatamo, appManager } = useContext(AppContext)
const { settings, showMatomo, appManager } = useContext(AppContext)
const { modal } = useDialogDispatchers()
const [visible, setVisible] = useState<boolean>(props.hide)
@ -63,7 +64,7 @@ const MatomoDialog = (props: MatomoDialogProps) => {
}
useEffect(() => {
if (visible && showMatamo) {
if (visible && showMatomo) {
modal({
id: 'matomoModal',
title: <FormattedMessage id="remixApp.matomoTitle" />,
@ -72,18 +73,22 @@ const MatomoDialog = (props: MatomoDialogProps) => {
okFn: handleModalOkClick,
cancelLabel: <FormattedMessage id="remixApp.decline" />,
cancelFn: declineModal,
preventBlur: true
})
}
}, [visible])
const declineModal = async () => {
settings.updateMatomoAnalyticsChoice(false)
// revoke tracking consent
_paq.push(['forgetConsentGiven'])
setVisible(false)
const declineModal = async (reason: AppModalCancelTypes) => {
if (reason === AppModalCancelTypes.click || reason === AppModalCancelTypes.enter) {
settings.updateMatomoAnalyticsChoice(false)
// revoke tracking consent
_paq.push(['forgetConsentGiven'])
setVisible(false)
}
}
const handleModalOkClick = async () => {
// user has given consent to process their data
_paq.push(['setConsentGiven'])
settings.updateMatomoAnalyticsChoice(true)

@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from 'react'
import { ModalDialog, ModalDialogProps, ValidationResult } from '@remix-ui/modal-dialog'
import { ModalTypes } from '../../types'
import { AppModalCancelTypes, ModalTypes } from '../../types'
interface ModalWrapperProps extends ModalDialogProps {
modalType?: ModalTypes
@ -42,8 +42,8 @@ const ModalWrapper = (props: ModalWrapperProps) => {
props.okFn ? props.okFn(data.current) : props.resolve(data.current || true)
}
const onCancelFn = async () => {
props.cancelFn ? props.cancelFn() : props.resolve(false)
const onCancelFn = async (reason?: AppModalCancelTypes) => {
props.cancelFn ? props.cancelFn(reason) : props.resolve(false)
}
const onInputChanged = (event) => {
@ -142,6 +142,8 @@ const ModalWrapper = (props: ModalWrapperProps) => {
props.handleHide()
}
if (!props.id || props.id === '') return null
return <ModalDialog id={props.id} {...state} handleHide={handleHide} />
}
export default ModalWrapper

@ -5,7 +5,7 @@ import { AppAction } from '../actions/app'
export type appProviderContextType = {
settings: any,
showMatamo: boolean,
showMatomo: boolean,
showEnter: boolean,
appManager: any
modal: any

@ -23,7 +23,7 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
}
const modal = (modalData: AppModal) => {
const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, modalParentClass, defaultValue, hideFn, data } = modalData
const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, modalParentClass, defaultValue, hideFn, data, preventBlur } = modalData
return new Promise((resolve, reject) => {
dispatch({
type: modalActionTypes.setModal,
@ -42,7 +42,8 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
hideFn,
resolve,
next: onNextFn,
data
data,
preventBlur
}
})
})

@ -1,5 +1,5 @@
import { GitHubUser } from '@remix-api'
import { ModalTypes } from '../types'
import { AppModalCancelTypes, ModalTypes } from '../types'
export type ValidationResult = {
valid: boolean,
@ -17,14 +17,15 @@ export interface AppModal {
okLabel: string | JSX.Element
okFn?: (value?:any) => void
cancelLabel?: string | JSX.Element
cancelFn?: () => void,
cancelFn?: (reason?: AppModalCancelTypes) => void,
modalType?: ModalTypes,
modalParentClass?: string
defaultValue?: string
hideFn?: () => void,
resolve?: (value?:any) => void,
next?: () => void,
data?: any
data?: any,
preventBlur?: boolean
}
export interface AlertModal {

@ -23,7 +23,8 @@ export const modalReducer = (state: ModalState = ModalInitialState, action: Moda
hideFn: action.payload.hideFn,
resolve: action.payload.resolve,
next: action.payload.next,
data: action.payload.data
data: action.payload.data,
preventBlur: action.payload.preventBlur
}
const modalList: AppModal[] = state.modals.slice()

@ -60,7 +60,7 @@ const RemixApp = (props: IRemixAppUi) => {
activateApp()
}
const hadUsageTypeAsked = localStorage.getItem('hadUsageTypeAsked')
if (props.app.showMatamo) {
if (props.app.showMatomo) {
// if matomo dialog is displayed, it will take care of calling "setShowEnterDialog",
// if the user approves matomo tracking.
// so "showEnterDialog" stays false
@ -149,7 +149,7 @@ const RemixApp = (props: IRemixAppUi) => {
const value: appProviderContextType = {
settings: props.app.settings,
showMatamo: props.app.showMatamo,
showMatomo: props.app.showMatomo,
appManager: props.app.appManager,
showEnter: props.app.showEnter,
modal: props.app.notification,

@ -8,6 +8,15 @@ export const enum ModalTypes {
forceChoice = 'forceChoice'
}
export const enum AppModalCancelTypes {
close = 'close',
cancel = 'cancel',
blur = 'blur',
escape = 'escape',
enter = 'enter',
click = 'click'
}
export const enum UsageTypes {
Beginner = 1,
Prototyper,

@ -113,30 +113,6 @@ function HomeTabFeatured(props:HomeTabFeaturedProps) {
</a>
</div>
</div>
<div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href="https://docs.google.com/forms/d/e/1FAIpQLSd0WsJnKbeJo-BGrnf7WijxAdmE4PnC_Z4M0IApbBfHLHZdsQ/viewform" target="__blank">
<img src={'assets/img/remixRewardBetaTester_small.webp'} className="remixui_carouselImage_remixbeta" alt=""></img>
</a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5>
<FormattedMessage id="home.betaTesting" />
</h5>
<p style={{ fontStyle: 'italic', fontSize: '1rem' }}>
<FormattedMessage id="home.betaTestingText1" />
</p>
<div style={{ fontSize: '0.8rem' }} className="mb-3">
<FormattedMessage id="home.betaTestingText2" />
</div>
<a
className="remixui_home_text btn-sm btn-secondary mt-2 text-decoration-none mb-3"
onClick={() => _paq.push(['trackEvent', 'hometab', 'featuredSection', 'betatesting'])}
target="__blank"
href="https://docs.google.com/forms/d/e/1FAIpQLSd0WsJnKbeJo-BGrnf7WijxAdmE4PnC_Z4M0IApbBfHLHZdsQ/viewform"
>
<FormattedMessage id="home.betaTestingMore" />
</a>
</div>
</div>
</Carousel>
</ThemeContext.Provider>
</div>

@ -125,12 +125,6 @@
width: 20rem;
}
.remixui_carouselImage_remixbeta {
flex: 1;
height: 20rem;
width: 18rem;
}
.remixui_carouselbox {
min-height: 25.12rem;
}

@ -2,6 +2,7 @@ import React, {useRef, useState, useEffect} from 'react' // eslint-disable-line
import {ModalDialogProps} from './types' // eslint-disable-line
import './remix-ui-modal-dialog.css'
import { AppModalCancelTypes } from '@remix-ui/app'
declare global {
// eslint-disable-next-line no-unused-vars
@ -24,12 +25,14 @@ export const ModalDialog = (props: ModalDialogProps) => {
}
useEffect(() => {
if (!props.id) return
calledHideFunctionOnce.current = props.hide
modal.current.focus()
if (modal.current) {
if (!props.hide) {
modal.current.focus()
modal.current.removeEventListener('blur', handleBlur)
modal.current.addEventListener('blur', handleBlur)
if (modal.current && !props.preventBlur) {
modal.current.addEventListener('blur', handleBlur)
}
}
return () => {
modal.current && modal.current.removeEventListener('blur', handleBlur)
@ -37,11 +40,11 @@ export const ModalDialog = (props: ModalDialogProps) => {
}, [props.hide])
function handleBlur(e) {
if (!e.currentTarget.contains(e.relatedTarget)) {
if (e.currentTarget && !e.currentTarget.contains(e.relatedTarget)) {
e.stopPropagation()
if (document.activeElement !== this) {
!window.testmode && handleHide()
!window.testmode && props.cancelFn && props.cancelFn()
!window.testmode && props.cancelFn && props.cancelFn(AppModalCancelTypes.blur)
}
}
}
@ -49,7 +52,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
const modalKeyEvent = (keyCode) => {
if (keyCode === 27) {
// Esc
if (props.cancelFn) props.cancelFn()
if (props.cancelFn) props.cancelFn(AppModalCancelTypes.escape)
handleHide()
} else if (keyCode === 13) {
// Enter
@ -71,7 +74,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
if (state.toggleBtn) {
if (props.okFn) props.okFn()
} else {
if (props.cancelFn) props.cancelFn()
if (props.cancelFn) props.cancelFn(AppModalCancelTypes.enter)
}
handleHide()
}
@ -99,7 +102,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
{props.title && props.title}
</h6>
{!props.showCancelIcon && (
<span className="modal-close" onClick={() => handleHide()}>
<span data-id={`${props.id}-modal-close`} className="modal-close" onClick={() => handleHide()}>
<i className="fas fa-times" aria-hidden="true"></i>
</span>
)}
@ -130,7 +133,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
className={'modal-cancel btn btn-sm ' + (props.cancelBtnClass ? props.cancelBtnClass : state.toggleBtn ? 'border-secondary' : 'border-primary')}
data-dismiss="modal"
onClick={() => {
if (props.cancelFn) props.cancelFn()
if (props.cancelFn) props.cancelFn(AppModalCancelTypes.click)
handleHide()
}}
>

@ -1,3 +1,5 @@
import { AppModalCancelTypes } from "@remix-ui/app"
export type ValidationResult = {
valid: boolean,
message?: string
@ -15,7 +17,7 @@ export interface ModalDialogProps {
okFn?: (value?:any) => void,
donotHideOnOkClick?: boolean,
cancelLabel?: string | JSX.Element,
cancelFn?: () => void,
cancelFn?: (reason?: AppModalCancelTypes) => void,
modalClass?: string,
modalParentClass?: string
showCancelIcon?: boolean,
@ -26,5 +28,6 @@ export interface ModalDialogProps {
next?: () => void,
data?: any,
okBtnClass?: string,
cancelBtnClass?: string
cancelBtnClass?: string,
preventBlur?: boolean
}

@ -6,7 +6,6 @@ import { ContractData, FuncABI, OverSizeLimit } from '@remix-project/core-plugin
import * as ethJSUtil from '@ethereumjs/util'
import { ContractGUI } from './contractGUI'
import { CustomTooltip, deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper'
import { title } from 'process'
const _paq = (window._paq = window._paq || [])
export function ContractDropdownUI(props: ContractDropdownProps) {
@ -80,6 +79,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
content: '',
})
if (!currentContract) enableAtAddress(false)
if (currentContract && loadedAddress) enableAtAddress(true)
} else {
setAbiLabel({
display: 'none',

@ -315,7 +315,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
</div>
<div className="custom-control custom-checkbox mb-1">
<input onChange={onchangeMatomoAnalytics} id="settingsMatomoAnalytics" type="checkbox" className="custom-control-input" checked={isMatomoChecked} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/matomo-analytics')}`} htmlFor="settingsMatomoAnalytics">
<label data-id="label-matomo-settings" className={`form-check-label custom-control-label align-middle ${getTextClass('settings/matomo-analytics')}`} htmlFor="settingsMatomoAnalytics">
<span>
<FormattedMessage id="settings.matomoAnalytics" />
</span>

@ -41,6 +41,7 @@ export const copilotTemperature = (config, checked, dispatch) => {
export const useMatomoAnalytics = (config, checked, dispatch) => {
config.set('settings/matomo-analytics', checked)
localStorage.setItem('matomo-analytics-consent', Date.now().toString())
dispatch({ type: 'useMatomoAnalytics', payload: { isChecked: checked, textClass: checked ? textDark : textSecondary } })
if (checked) {
// user has given consent to process their data

@ -21,7 +21,8 @@
vertical-align: middle;
}
.tab:hover .close-tabs{
visibility: visible
visibility: visible;
cursor: pointer;
}
.active .close-tabs {
visibility: visible
@ -29,6 +30,7 @@
.close-tabs {
visibility: hidden;
font-size: medium;
cursor: pointer;
}
.iconImage {
width: 1rem;

Loading…
Cancel
Save