EIP-1102: add user privacy option

feature/default_network_editable
bitpshr 6 years ago committed by Dan Finlay
parent bfcb73ad53
commit 226601a956
  1. 6
      app/_locales/cs/messages.json
  2. 6
      app/_locales/de/messages.json
  3. 12
      app/_locales/en/messages.json
  4. 6
      app/_locales/es/messages.json
  5. 12
      app/_locales/fr/messages.json
  6. 6
      app/_locales/hn/messages.json
  7. 39
      app/_locales/ht/messages.json
  8. 6
      app/_locales/it/messages.json
  9. 6
      app/_locales/ja/messages.json
  10. 6
      app/_locales/ko/messages.json
  11. 6
      app/_locales/nl/messages.json
  12. 6
      app/_locales/ph/messages.json
  13. 6
      app/_locales/pt/messages.json
  14. 6
      app/_locales/ru/messages.json
  15. 6
      app/_locales/th/messages.json
  16. 6
      app/_locales/tml/messages.json
  17. 6
      app/_locales/tr/messages.json
  18. 6
      app/_locales/vi/messages.json
  19. 6
      app/_locales/zh_CN/messages.json
  20. 6
      app/_locales/zh_TW/messages.json
  21. 27
      app/scripts/contentscript.js
  22. 41
      app/scripts/controllers/provider-approval.js
  23. 4
      app/scripts/inpage.js
  24. 4
      app/scripts/metamask-controller.js
  25. 16
      old-ui/app/config.js
  26. 16
      test/e2e/beta/metamask-beta-ui.spec.js
  27. 31
      test/e2e/metamask.spec.js
  28. 7
      ui/app/actions.js
  29. 39
      ui/app/components/modals/force-injection/force-injection.component.js
  30. 16
      ui/app/components/modals/force-injection/force-injection.container.js
  31. 1
      ui/app/components/modals/force-injection/index.js
  32. 14
      ui/app/components/modals/modal.js
  33. 65
      ui/app/components/pages/settings/settings-tab/settings-tab.component.js
  34. 8
      ui/app/components/pages/settings/settings-tab/settings-tab.container.js

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Režim súkromia"
},
"privacyModeDescription": {
"message": "Webové stránky musia požiadať o prístup k zobrazeniu informácií o vašom účte."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Vystavte účty" "message": "Vystavte účty"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Datenschutzmodus"
},
"privacyModeDescription": {
"message": "Websites müssen Zugriff anfordern, um Ihre Kontoinformationen anzuzeigen."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Expose Konten" "message": "Expose Konten"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Privacy Mode"
},
"privacyModeDescription": {
"message": "Websites must request access to view your account information."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Expose Accounts" "message": "Expose Accounts"
}, },
@ -15,13 +21,13 @@
"message": "Approved website data cleared successfully." "message": "Approved website data cleared successfully."
}, },
"approvalData": { "approvalData": {
"message": "Approval Data" "message": "Privacy Data"
}, },
"approvalDataDescription": { "approvalDataDescription": {
"message": "Clear approved website data so all sites must request approval again." "message": "Clear privacy data so all websites must request access to view account information again."
}, },
"clearApprovalData": { "clearApprovalData": {
"message": "Clear Approval Data" "message": "Clear Privacy Data"
}, },
"reject": { "reject": {
"message": "Reject" "message": "Reject"

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Modo privado"
},
"privacyModeDescription": {
"message": "Los sitios web deben solicitar acceso para ver la información de su cuenta."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Exponer cuentas" "message": "Exponer cuentas"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Les sites Web doivent demander un accès pour afficher les informations de votre compte."
},
"privacyModeDescription": {
"message": "Les sites Web doivent demander un accès pour afficher les informations de votre compte."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Exposer les comptes" "message": "Exposer les comptes"
}, },
@ -23,12 +29,6 @@
"clearApprovalData": { "clearApprovalData": {
"message": "Effacer les données d'approbation" "message": "Effacer les données d'approbation"
}, },
"approve": {
"message": "Approuver"
},
"reject": {
"message": "Rejeter"
},
"providerAPIRequest": { "providerAPIRequest": {
"message": "Demande d'API Web3" "message": "Demande d'API Web3"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "गपनयतड"
},
"privacyModeDescription": {
"message": "वबसइट आपकनकखनिए पहच क अनध करन।"
},
"exposeAccounts": { "exposeAccounts": {
"message": "ख परश कर" "message": "ख परश कर"
}, },

@ -1,4 +1,43 @@
{ {
"privacyMode": {
"message": "Mòd Privacy"
},
"privacyModeDescription": {
"message": "Sou sit entènèt yo dwe mande aksè pou wè enfòmasyon kont ou."
},
"exposeAccounts": {
"message": "Ekspoze Kont"
},
"exposeDescription": {
"message": "Ekspoze kont sou sitwèb aktyèl la. Itil pou dapps eritaj."
},
"confirmExpose": {
"message": "Èske ou sèten ou vle ekspoze kont ou sou sit entènèt aktyèl la?"
},
"confirmClear": {
"message": "Èske ou sèten ou vle klè sitwèb apwouve?"
},
"clearApprovalDataSuccess": {
"message": "Done sou sit wèb apwouve yo te klarifye avèk siksè."
},
"approvalData": {
"message": "Done sou vi prive"
},
"approvalDataDescription": {
"message": "Done sou vi prive klè pou tout sit entènèt yo dwe mande aksè pou wè enfòmasyon kont ankò."
},
"clearApprovalData": {
"message": "Klè Done sou vi prive"
},
"providerAPIRequest": {
"message": "Ethereum API Mande"
},
"reviewProviderRequest": {
"message": "Tanpri revize sa API demann Ethereum."
},
"providerRequestInfo": {
"message": "Domèn ki nan lis anba a ap mande pou jwenn aksè a blòkchou Ethereum ak pou wè kont ou ye kounye a. Toujou double tcheke ke ou sou sit ki kòrèk la anvan apwouve aksè."
},
"accept": { "accept": {
"message": "Aksepte" "message": "Aksepte"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Modalità di privacy"
},
"privacyModeDescription": {
"message": "I siti Web devono richiedere l'accesso per visualizzare le informazioni del tuo account."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Expose Accounts" "message": "Expose Accounts"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "プライバシーモード"
},
"privacyModeDescription": {
"message": "ウェブサイトはあなたのアカウント情報を閲覧するためのアクセスを要求する必要があります。"
},
"exposeAccounts": { "exposeAccounts": {
"message": "アカウントを公開する" "message": "アカウントを公開する"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "개인 정보 보호 모드"
},
"privacyModeDescription": {
"message": "웹 사이트는 계정 정보를 볼 수있는 액세스 권한을 요청해야합니다."
},
"exposeAccounts": { "exposeAccounts": {
"message": "계정 노출" "message": "계정 노출"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Privacy-modus"
},
"privacyModeDescription": {
"message": "Websites moeten toegang vragen om uw accountgegevens te bekijken."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Expose Accounts" "message": "Expose Accounts"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Mode ng Privacy"
},
"privacyModeDescription": {
"message": "Dapat humiling ng access ang mga website upang tingnan ang impormasyon ng iyong account."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Ilantad ang Mga Account" "message": "Ilantad ang Mga Account"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Modo de privacidade"
},
"privacyModeDescription": {
"message": "Os sites devem solicitar acesso para visualizar as informações da sua conta."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Expor contas" "message": "Expor contas"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Режим конфиденциальности"
},
"privacyModeDescription": {
"message": "Веб-сайты должны запрашивать доступ для просмотра информации об учетной записи."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Открыть счета" "message": "Открыть счета"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "โหมดความเปนสวนตว"
},
"privacyModeDescription": {
"message": "เวบไซตองขอเขาถงเพอดอมลบญชของคณ"
},
"exposeAccounts": { "exposeAccounts": {
"message": "เปดเผยบญช" "message": "เปดเผยบญช"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "தனிி"
},
"privacyModeDescription": {
"message": "உஙகள கணக தகவலிட வலதளஙகள அணகலர வ."
},
"exposeAccounts": { "exposeAccounts": {
"message": "கணககள அமபலபபடகள" "message": "கணககள அமபலபபடகள"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Gizlilik modu"
},
"privacyModeDescription": {
"message": "Web siteleri, hesap bilgilerinizi görmek için erişim istemek zorundadır."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Hesapları Açığa Çıkar" "message": "Hesapları Açığa Çıkar"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "Chế độ riêng tư"
},
"privacyModeDescription": {
"message": "Trang web phải yêu cầu quyền truy cập để xem thông tin tài khoản của bạn."
},
"exposeAccounts": { "exposeAccounts": {
"message": "Hiển thị tài khoản" "message": "Hiển thị tài khoản"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "隐私模式"
},
"privacyModeDescription": {
"message": "网站必须请求访问权限才能查看您的帐户信息。"
},
"exposeAccounts": { "exposeAccounts": {
"message": "公开账户" "message": "公开账户"
}, },

@ -1,4 +1,10 @@
{ {
"privacyMode": {
"message": "隱私模式"
},
"privacyModeDescription": {
"message": "網站必須請求訪問權限才能查看您的帳戶信息。"
},
"exposeAccounts": { "exposeAccounts": {
"message": "公開賬戶" "message": "公開賬戶"
}, },

@ -24,7 +24,7 @@ if (shouldInjectWeb3()) {
injectScript(inpageBundle) injectScript(inpageBundle)
setupStreams() setupStreams()
listenForProviderRequest() listenForProviderRequest()
checkForcedInjection() checkPrivacyMode()
} }
/** /**
@ -125,9 +125,9 @@ function listenForProviderRequest () {
origin: source.location.hostname, origin: source.location.hostname,
}) })
break break
case 'ETHEREUM_PROVIDER_STATUS': case 'ETHEREUM_QUERY_STATUS':
extension.runtime.sendMessage({ extension.runtime.sendMessage({
action: 'provider-status-request', action: 'init-status-request',
origin: source.location.hostname, origin: source.location.hostname,
}) })
break break
@ -144,14 +144,7 @@ function listenForProviderRequest () {
case 'reject-provider-request': case 'reject-provider-request':
injectScript(`window.dispatchEvent(new CustomEvent('ethereumprovider', { detail: { error: 'User rejected provider access' }}))`) injectScript(`window.dispatchEvent(new CustomEvent('ethereumprovider', { detail: { error: 'User rejected provider access' }}))`)
break break
case 'force-injection': case 'answer-status-request':
extension.storage.local.get(['forcedOrigins'], ({ forcedOrigins = [] }) => {
extension.storage.local.set({ forcedOrigins: [ ...forcedOrigins, window.location.hostname ] }, () => {
injectScript(`window.location.reload()`)
})
})
break
case 'provider-status':
injectScript(`window.dispatchEvent(new CustomEvent('ethereumproviderstatus', { detail: { isEnabled: ${isEnabled}}}))`) injectScript(`window.dispatchEvent(new CustomEvent('ethereumproviderstatus', { detail: { isEnabled: ${isEnabled}}}))`)
break break
} }
@ -159,15 +152,11 @@ function listenForProviderRequest () {
} }
/** /**
* Checks the current origin to see if it exists in the extension's locally-stored list * Checks if MetaMask is currently operating in "privacy mode", meaning
* off user-whitelisted dapp origins. If it is, this origin will be marked as approved, * dapps must call ethereum.enable in order to access user accounts
* meaning the publicConfig stream will be enabled. This is only meant to ease the transition
* to 1102 and will be removed in the future.
*/ */
function checkForcedInjection () { function checkPrivacyMode () {
extension.storage.local.get(['forcedOrigins'], ({ forcedOrigins = [] }) => { extension.runtime.sendMessage({ action: 'init-privacy-request' })
originApproved = forcedOrigins.indexOf(window.location.hostname) > -1
})
} }
/** /**

@ -1,5 +1,4 @@
const ObservableStore = require('obs-store') const ObservableStore = require('obs-store')
const extension = require('extensionizer')
/** /**
* A controller that services user-approved requests for a full Ethereum provider API * A controller that services user-approved requests for a full Ethereum provider API
@ -10,22 +9,25 @@ class ProviderApprovalController {
* *
* @param {Object} [config] - Options to configure controller * @param {Object} [config] - Options to configure controller
*/ */
constructor ({ closePopup, openPopup, platform, publicConfigStore } = {}) { constructor ({ closePopup, openPopup, platform, preferencesController, publicConfigStore } = {}) {
this.store = new ObservableStore() this.store = new ObservableStore()
this.closePopup = closePopup this.closePopup = closePopup
this.openPopup = openPopup this.openPopup = openPopup
this.platform = platform this.platform = platform
this.publicConfigStore = publicConfigStore this.publicConfigStore = publicConfigStore
this.approvedOrigins = {} this.approvedOrigins = {}
this.preferencesController = preferencesController
platform && platform.addMessageListener && platform.addMessageListener(({ action, origin }) => { platform && platform.addMessageListener && platform.addMessageListener(({ action, origin }) => {
if (!action) { return } if (!action) { return }
switch (action) { switch (action) {
case 'init-provider-request': case 'init-provider-request':
this.handleProviderRequest(origin) this.handleProviderRequest(origin)
break break
case 'provider-status-request': case 'init-status-request':
this.handleProviderStatusRequest(origin) this.handleProviderStatusRequest(origin)
break break
case 'init-privacy-request':
this.handlePrivacyStatusRequest()
} }
}) })
} }
@ -35,9 +37,9 @@ class ProviderApprovalController {
* *
* @param {string} origin - Origin of the window requesting full provider access * @param {string} origin - Origin of the window requesting full provider access
*/ */
async handleProviderRequest (origin) { handleProviderRequest (origin) {
this.store.updateState({ providerRequests: [{ origin }] }) this.store.updateState({ providerRequests: [{ origin }] })
if (await this.isApproved(origin)) { if (this.isApproved(origin)) {
this.approveProviderRequest(origin) this.approveProviderRequest(origin)
return return
} }
@ -45,13 +47,21 @@ class ProviderApprovalController {
} }
/** /**
* Called by a tab to detemrine if a full Ethereum provider API is exposed * Called by a tab to determine if a full Ethereum provider API is exposed
* *
* @param {string} origin - Origin of the window requesting provider status * @param {string} origin - Origin of the window requesting provider status
*/ */
async handleProviderStatusRequest (origin) { async handleProviderStatusRequest (origin) {
const isEnabled = await this.isApproved(origin) const isEnabled = this.isApproved(origin)
this.platform && this.platform.sendMessage({ action: 'provider-status', isEnabled }, { active: true }) this.platform && this.platform.sendMessage({ action: 'answer-status-request', isEnabled }, { active: true })
}
handlePrivacyStatusRequest () {
const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
if (!privacyMode) {
this.platform && this.platform.sendMessage({ action: 'approve-provider-request' }, { active: true })
this.publicConfigStore.emit('update', this.publicConfigStore.getState())
}
} }
/** /**
@ -87,7 +97,6 @@ class ProviderApprovalController {
*/ */
clearApprovedOrigins () { clearApprovedOrigins () {
this.approvedOrigins = {} this.approvedOrigins = {}
extension.storage.local.set({ forcedOrigins: [] })
} }
/** /**
@ -97,18 +106,8 @@ class ProviderApprovalController {
* @returns {boolean} - True if the origin has been approved * @returns {boolean} - True if the origin has been approved
*/ */
isApproved (origin) { isApproved (origin) {
return new Promise(resolve => { const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
extension.storage.local.get(['forcedOrigins'], ({ forcedOrigins = [] }) => { return !privacyMode || this.approvedOrigins[origin]
resolve(this.approvedOrigins[origin] || forcedOrigins.indexOf(origin) > -1)
})
})
}
/**
* Called when a user forces the exposure of a full Ethereum provider API
*/
forceInjection () {
this.platform.sendMessage({ action: 'force-injection' }, { active: true })
} }
} }

@ -56,10 +56,10 @@ inpageProvider.isEnabled = function () {
if (typeof detail.error !== 'undefined') { if (typeof detail.error !== 'undefined') {
reject(detail.error) reject(detail.error)
} else { } else {
resolve(detail.isEnabled) resolve(!!detail.isEnabled)
} }
}) })
window.postMessage({ type: 'ETHEREUM_PROVIDER_STATUS' }, '*') window.postMessage({ type: 'ETHEREUM_QUERY_STATUS' }, '*')
}) })
} }

@ -224,6 +224,7 @@ module.exports = class MetamaskController extends EventEmitter {
closePopup: opts.closePopup, closePopup: opts.closePopup,
openPopup: opts.openPopup, openPopup: opts.openPopup,
platform: opts.platform, platform: opts.platform,
preferencesController: this.preferencesController,
publicConfigStore: this.publicConfigStore, publicConfigStore: this.publicConfigStore,
}) })
@ -275,7 +276,7 @@ module.exports = class MetamaskController extends EventEmitter {
getAccounts: async ({ origin }) => { getAccounts: async ({ origin }) => {
// Expose no accounts if this origin has not been approved, preventing // Expose no accounts if this origin has not been approved, preventing
// account-requring RPC methods from completing successfully // account-requring RPC methods from completing successfully
const isApproved = await this.providerApprovalController.isApproved(origin) const isApproved = this.providerApprovalController.isApproved(origin)
if (origin !== 'MetaMask' && !isApproved) { return [] } if (origin !== 'MetaMask' && !isApproved) { return [] }
const isUnlocked = this.keyringController.memStore.getState().isUnlocked const isUnlocked = this.keyringController.memStore.getState().isUnlocked
const selectedAddress = this.preferencesController.getSelectedAddress() const selectedAddress = this.preferencesController.getSelectedAddress()
@ -455,7 +456,6 @@ module.exports = class MetamaskController extends EventEmitter {
approveProviderRequest: providerApprovalController.approveProviderRequest.bind(providerApprovalController), approveProviderRequest: providerApprovalController.approveProviderRequest.bind(providerApprovalController),
clearApprovedOrigins: providerApprovalController.clearApprovedOrigins.bind(providerApprovalController), clearApprovedOrigins: providerApprovalController.clearApprovedOrigins.bind(providerApprovalController),
rejectProviderRequest: providerApprovalController.rejectProviderRequest.bind(providerApprovalController), rejectProviderRequest: providerApprovalController.rejectProviderRequest.bind(providerApprovalController),
forceInjection: providerApprovalController.forceInjection.bind(providerApprovalController),
} }
} }

@ -210,7 +210,7 @@ ConfigScreen.prototype.render = function () {
fontFamily: 'Montserrat Light', fontFamily: 'Montserrat Light',
fontSize: '13px', fontSize: '13px',
}, },
}, 'Clear approved website data so all sites must request approval again.'), }, 'Clear privacy data so all websites must request access to view account information again.'),
h('br'), h('br'),
h('button', { h('button', {
style: { style: {
@ -220,7 +220,7 @@ ConfigScreen.prototype.render = function () {
event.preventDefault() event.preventDefault()
state.dispatch(actions.clearApprovedOrigins()) state.dispatch(actions.clearApprovedOrigins())
}, },
}, 'Clear approval data'), }, 'Clear privacy data'),
]), ]),
h('hr.horizontal-line'), h('hr.horizontal-line'),
@ -235,7 +235,10 @@ ConfigScreen.prototype.render = function () {
fontFamily: 'Montserrat Light', fontFamily: 'Montserrat Light',
fontSize: '13px', fontSize: '13px',
}, },
}, 'Expose accounts to the current website. This is useful for legacy dapps.'), }, metamaskState.featureFlags.privacyMode ?
'Websites will be able to view your account information.' :
'Websites must request access to view your account information.'
),
h('br'), h('br'),
h('button', { h('button', {
style: { style: {
@ -243,9 +246,12 @@ ConfigScreen.prototype.render = function () {
}, },
onClick (event) { onClick (event) {
event.preventDefault() event.preventDefault()
state.dispatch(actions.forceInjection()) state.dispatch(actions.setFeatureFlag('privacyMode', !metamaskState.featureFlags.privacyMode))
}, },
}, 'Expose accounts'), }, metamaskState.featureFlags.privacyMode ?
'Disable privacy mode' :
'Enable privacy mode'
),
]), ]),
h('hr.horizontal-line'), h('hr.horizontal-line'),

@ -284,6 +284,22 @@ describe('MetaMask', function () {
}) })
}) })
describe('Enable privacy mode', () => {
it('enables privacy mode', async () => {
const networkDropdown = await findElement(driver, By.css('.network-name'))
await networkDropdown.click()
await delay(regularDelayMs)
const customRpcButton = await findElement(driver, By.xpath(`//span[contains(text(), 'Custom RPC')]`))
await customRpcButton.click()
await delay(regularDelayMs)
const privacyToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(9) .settings-page__content-item-col > div'))
await privacyToggle.click()
await delay(largeDelayMs * 2)
})
})
describe('Log out an log back in', () => { describe('Log out an log back in', () => {
it('logs out of the account', async () => { it('logs out of the account', async () => {
await driver.findElement(By.css('.account-menu__icon')).click() await driver.findElement(By.css('.account-menu__icon')).click()

@ -2,11 +2,6 @@ const path = require('path')
const assert = require('assert') const assert = require('assert')
const { By, Key, until } = require('selenium-webdriver') const { By, Key, until } = require('selenium-webdriver')
const { delay, createModifiedTestBuild, setupBrowserAndExtension, verboseReportOnFailure } = require('./func') const { delay, createModifiedTestBuild, setupBrowserAndExtension, verboseReportOnFailure } = require('./func')
const {
closeAllWindowHandlesExcept,
switchToWindowWithTitle,
switchToWindowWithUrlThatMatches,
} = require('./beta/helpers')
describe('Metamask popup page', function () { describe('Metamask popup page', function () {
const browser = process.env.SELENIUM_BROWSER const browser = process.env.SELENIUM_BROWSER
@ -188,7 +183,6 @@ describe('Metamask popup page', function () {
}) })
it('restores from seed phrase', async function () { it('restores from seed phrase', async function () {
await delay(1000)
const restoreSeedLink = await driver.findElement(By.css('#app-content > div > div.app-primary.from-left > div > div.flex-row.flex-center.flex-grow > p')) const restoreSeedLink = await driver.findElement(By.css('#app-content > div > div.app-primary.from-left > div > div.flex-row.flex-center.flex-grow > p'))
assert.equal(await restoreSeedLink.getText(), 'Restore from seed phrase') assert.equal(await restoreSeedLink.getText(), 'Restore from seed phrase')
await restoreSeedLink.click() await restoreSeedLink.click()
@ -207,10 +201,10 @@ describe('Metamask popup page', function () {
}) })
it('balance renders', async function () { it('balance renders', async function () {
await delay(1000) await delay(500)
const balance = await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > div.ether-balance.ether-balance-amount > div > div > div:nth-child(1) > div:nth-child(1)')) const balance = await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > div.ether-balance.ether-balance-amount > div > div > div:nth-child(1) > div:nth-child(1)'))
assert.equal(await balance.getText(), '100.000') assert.equal(await balance.getText(), '100.000')
await delay(1000) await delay(200)
}) })
it('sends transaction', async function () { it('sends transaction', async function () {
@ -248,31 +242,12 @@ describe('Metamask popup page', function () {
}) })
describe('Token Factory', function () { describe('Token Factory', function () {
let windowHandles
let extension
let dapp
it('navigates to token factory', async function () { it('navigates to token factory', async function () {
await driver.get('http://token-factory-1102.now.sh') await driver.get('http://tokenfactory.surge.sh/')
await delay(7000)
windowHandles = await driver.getAllWindowHandles()
dapp = await switchToWindowWithTitle(driver, 'Token Factory', windowHandles)
await delay(400)
extension = await switchToWindowWithUrlThatMatches(driver, /notification.html/, windowHandles)
await delay(400)
await closeAllWindowHandlesExcept(driver, [extension, dapp])
await switchToWindowWithUrlThatMatches(driver, /notification.html/, [extension, dapp])
const approveButton = await driver.wait(until.elementLocated(By.xpath(`//button[contains(text(), 'APPROVE')]`)), 10000)
await approveButton.click()
}) })
it('navigates to create token contract link', async function () { it('navigates to create token contract link', async function () {
await delay(400)
await switchToWindowWithTitle(driver, 'Token Factory', windowHandles)
await delay(400)
const createToken = await driver.findElement(By.css('#bs-example-navbar-collapse-1 > ul > li:nth-child(3) > a')) const createToken = await driver.findElement(By.css('#bs-example-navbar-collapse-1 > ul > li:nth-child(3) > a'))
await createToken.click() await createToken.click()
}) })

@ -328,7 +328,6 @@ var actions = {
approveProviderRequest, approveProviderRequest,
rejectProviderRequest, rejectProviderRequest,
clearApprovedOrigins, clearApprovedOrigins,
forceInjection,
} }
module.exports = actions module.exports = actions
@ -2506,9 +2505,3 @@ function clearApprovedOrigins () {
background.clearApprovedOrigins() background.clearApprovedOrigins()
} }
} }
function forceInjection () {
return (dispatch) => {
background.forceInjection()
}
}

@ -1,39 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Modal, { ModalContent } from '../../modal'
export default class ForceInjection extends PureComponent {
static propTypes = {
hideModal: PropTypes.func.isRequired,
forceInjection: PropTypes.func.isRequired,
}
static contextTypes = {
t: PropTypes.func,
}
handleForce = () => {
const { forceInjection, hideModal } = this.props
forceInjection()
hideModal()
}
render () {
const { t } = this.context
return (
<Modal
onSubmit={this.handleForce}
onCancel={() => this.props.hideModal()}
submitText={t('ok')}
cancelText={t('nevermind')}
submitType="secondary"
>
<ModalContent
title={t('exposeAccounts')}
description={t('confirmExpose')}
/>
</Modal>
)
}
}

@ -1,16 +0,0 @@
import { connect } from 'react-redux'
import { compose } from 'recompose'
import withModalProps from '../../../higher-order-components/with-modal-props'
import ForceInjectionComponent from './force-injection.component'
import { forceInjection } from '../../../actions'
const mapDispatchToProps = dispatch => {
return {
forceInjection: () => dispatch(forceInjection()),
}
}
export default compose(
withModalProps,
connect(null, mapDispatchToProps)
)(ForceInjectionComponent)

@ -1 +0,0 @@
export { default } from './force-injection.container'

@ -29,7 +29,6 @@ import CancelTransaction from './cancel-transaction'
import WelcomeBeta from './welcome-beta' import WelcomeBeta from './welcome-beta'
import RejectTransactions from './reject-transactions' import RejectTransactions from './reject-transactions'
import ClearApprovedOrigins from './clear-approved-origins' import ClearApprovedOrigins from './clear-approved-origins'
import ForceInjection from './force-injection'
const modalContainerBaseStyle = { const modalContainerBaseStyle = {
transform: 'translate3d(-50%, 0, 0px)', transform: 'translate3d(-50%, 0, 0px)',
@ -227,19 +226,6 @@ const MODALS = {
}, },
}, },
FORCE_INJECTION: {
contents: h(ForceInjection),
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
OLD_UI_NOTIFICATION_MODAL: { OLD_UI_NOTIFICATION_MODAL: {
contents: [ contents: [
h(NotifcationModal, { h(NotifcationModal, {

@ -39,6 +39,8 @@ export default class SettingsTab extends PureComponent {
metamask: PropTypes.object, metamask: PropTypes.object,
setUseBlockie: PropTypes.func, setUseBlockie: PropTypes.func,
setHexDataFeatureFlag: PropTypes.func, setHexDataFeatureFlag: PropTypes.func,
setPrivacyMode: PropTypes.func,
privacyMode: PropTypes.bool,
setCurrentCurrency: PropTypes.func, setCurrentCurrency: PropTypes.func,
setRpcTarget: PropTypes.func, setRpcTarget: PropTypes.func,
delRpcTarget: PropTypes.func, delRpcTarget: PropTypes.func,
@ -46,7 +48,6 @@ export default class SettingsTab extends PureComponent {
revealSeedConfirmation: PropTypes.func, revealSeedConfirmation: PropTypes.func,
setFeatureFlagToBeta: PropTypes.func, setFeatureFlagToBeta: PropTypes.func,
showClearApprovalModal: PropTypes.func, showClearApprovalModal: PropTypes.func,
showForceInjectionModal: PropTypes.func,
showResetAccountConfirmationModal: PropTypes.func, showResetAccountConfirmationModal: PropTypes.func,
warning: PropTypes.string, warning: PropTypes.string,
history: PropTypes.object, history: PropTypes.object,
@ -308,36 +309,6 @@ export default class SettingsTab extends PureComponent {
) )
} }
renderForceInjection () {
const { t } = this.context
const { showForceInjectionModal } = this.props
return (
<div className="settings-page__content-row">
<div className="settings-page__content-item">
<span>{ t('exposeAccounts') }</span>
<span className="settings-page__content-description">
{ t('exposeDescription') }
</span>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<Button
type="secondary"
large
className="settings-tab__button--orange"
onClick={event => {
event.preventDefault()
showForceInjectionModal()
}}
>
{ t('exposeAccounts') }
</Button>
</div>
</div>
</div>
)
}
renderSeedWords () { renderSeedWords () {
const { t } = this.context const { t } = this.context
const { history } = this.props const { history } = this.props
@ -523,6 +494,32 @@ export default class SettingsTab extends PureComponent {
) )
} }
renderPrivacyOptIn () {
const { t } = this.context
const { privacyMode, setPrivacyMode } = this.props
return (
<div className="settings-page__content-row">
<div className="settings-page__content-item">
<span>{ t('privacyMode') }</span>
<div className="settings-page__content-description">
{ t('privacyModeDescription') }
</div>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<ToggleButton
value={privacyMode}
onToggle={value => setPrivacyMode(!value)}
activeLabel=""
inactiveLabel=""
/>
</div>
</div>
</div>
)
}
render () { render () {
const { warning, isMascara } = this.props const { warning, isMascara } = this.props
@ -535,12 +532,12 @@ export default class SettingsTab extends PureComponent {
{ this.renderNewRpcUrl() } { this.renderNewRpcUrl() }
{ this.renderStateLogs() } { this.renderStateLogs() }
{ this.renderSeedWords() } { this.renderSeedWords() }
{ this.renderClearApproval() }
{ this.renderForceInjection() }
{ !isMascara && this.renderOldUI() } { !isMascara && this.renderOldUI() }
{ this.renderResetAccount() } { this.renderResetAccount() }
{ this.renderBlockieOptIn() } { this.renderClearApproval() }
{ this.renderPrivacyOptIn() }
{ this.renderHexDataOptIn() } { this.renderHexDataOptIn() }
{ this.renderBlockieOptIn() }
</div> </div>
) )
} }

@ -22,7 +22,10 @@ const mapStateToProps = state => {
conversionDate, conversionDate,
nativeCurrency, nativeCurrency,
useBlockie, useBlockie,
featureFlags: { sendHexData } = {}, featureFlags: {
sendHexData,
privacyMode,
} = {},
provider = {}, provider = {},
isMascara, isMascara,
currentLocale, currentLocale,
@ -38,6 +41,7 @@ const mapStateToProps = state => {
nativeCurrency, nativeCurrency,
useBlockie, useBlockie,
sendHexData, sendHexData,
privacyMode,
provider, provider,
useNativeCurrencyAsPrimaryCurrency, useNativeCurrencyAsPrimaryCurrency,
} }
@ -55,12 +59,12 @@ const mapDispatchToProps = dispatch => {
return dispatch(setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) return dispatch(setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
}, },
setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)), setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)),
setPrivacyMode: enabled => dispatch(setFeatureFlag('privacyMode', enabled)),
showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })), showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })),
setUseNativeCurrencyAsPrimaryCurrencyPreference: value => { setUseNativeCurrencyAsPrimaryCurrencyPreference: value => {
return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value)) return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value))
}, },
showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })), showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })),
showForceInjectionModal: () => dispatch(showModal({ name: 'FORCE_INJECTION' })),
} }
} }

Loading…
Cancel
Save