aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWhymarrh Whitby <whymarrh.whitby@gmail.com>2019-08-16 05:07:18 +0800
committerGitHub <noreply@github.com>2019-08-16 05:07:18 +0800
commit247659ca651b683baba4eff2fbb49a9cb89d4503 (patch)
tree59df94c5a34a8316d5ddbcd941fa44057f111350
parent4d9b095dd0c280638b7593e87317f075b93f5ec4 (diff)
downloadtangerine-wallet-browser-247659ca651b683baba4eff2fbb49a9cb89d4503.tar.gz
tangerine-wallet-browser-247659ca651b683baba4eff2fbb49a9cb89d4503.tar.zst
tangerine-wallet-browser-247659ca651b683baba4eff2fbb49a9cb89d4503.zip
Connections settings tab (#7013)
* Nix notification for Share Address * Add Connections settings tab in place of privacy mode toggle * Split ProviderApprovalController into two stores * Remove privacyMode feature flag altogether * Add migration to remove privacyMode feature flag
-rw-r--r--app/_locales/cs/messages.json12
-rw-r--r--app/_locales/de/messages.json12
-rw-r--r--app/_locales/en/messages.json47
-rw-r--r--app/_locales/es/messages.json12
-rw-r--r--app/_locales/fr/messages.json12
-rw-r--r--app/_locales/hn/messages.json12
-rw-r--r--app/_locales/ht/messages.json12
-rw-r--r--app/_locales/it/messages.json12
-rw-r--r--app/_locales/ja/messages.json12
-rw-r--r--app/_locales/ko/messages.json12
-rw-r--r--app/_locales/nl/messages.json12
-rw-r--r--app/_locales/ph/messages.json12
-rw-r--r--app/_locales/pt/messages.json12
-rw-r--r--app/_locales/ru/messages.json12
-rw-r--r--app/_locales/sk/messages.json12
-rw-r--r--app/_locales/sl/messages.json12
-rw-r--r--app/_locales/th/messages.json12
-rw-r--r--app/_locales/tml/messages.json12
-rw-r--r--app/_locales/tr/messages.json12
-rw-r--r--app/_locales/vi/messages.json12
-rw-r--r--app/_locales/zh_CN/messages.json12
-rw-r--r--app/_locales/zh_TW/messages.json12
-rw-r--r--app/scripts/background.js4
-rw-r--r--app/scripts/controllers/preferences.js1
-rw-r--r--app/scripts/controllers/provider-approval.js98
-rw-r--r--app/scripts/metamask-controller.js7
-rw-r--r--app/scripts/migrations/036.js30
-rw-r--r--app/scripts/migrations/index.js1
-rw-r--r--test/unit/app/controllers/provider-approval-test.js260
-rw-r--r--test/unit/migrations/036-test.js119
-rw-r--r--ui/app/helpers/constants/routes.js3
-rw-r--r--ui/app/pages/home/home.component.js29
-rw-r--r--ui/app/pages/home/home.container.js22
-rw-r--r--ui/app/pages/settings/connections-tab/connected-site-row/connected-site-row.component.js31
-rw-r--r--ui/app/pages/settings/connections-tab/connected-site-row/index.js1
-rw-r--r--ui/app/pages/settings/connections-tab/connected-site-row/index.scss14
-rw-r--r--ui/app/pages/settings/connections-tab/connections-tab.component.js133
-rw-r--r--ui/app/pages/settings/connections-tab/connections-tab.container.js39
-rw-r--r--ui/app/pages/settings/connections-tab/index.js1
-rw-r--r--ui/app/pages/settings/connections-tab/index.scss1
-rw-r--r--ui/app/pages/settings/index.scss2
-rw-r--r--ui/app/pages/settings/security-tab/security-tab.component.js97
-rw-r--r--ui/app/pages/settings/security-tab/security-tab.container.js8
-rw-r--r--ui/app/pages/settings/settings.component.js8
-rw-r--r--ui/app/pages/settings/settings.container.js2
-rw-r--r--ui/app/store/actions.js7
46 files changed, 711 insertions, 506 deletions
diff --git a/app/_locales/cs/messages.json b/app/_locales/cs/messages.json
index 187ffd95f..bcc294542 100644
--- a/app/_locales/cs/messages.json
+++ b/app/_locales/cs/messages.json
@@ -1,10 +1,4 @@
{
- "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": {
"message": "Vystavte účty"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Schválené údaje webových stránek byly úspěšně zrušeny."
},
- "approvalData": {
- "message": "Údaje o schválení"
- },
- "approvalDataDescription": {
- "message": "Vymažte schválené údaje webových stránek, aby všechny weby znovu požádaly o schválení."
- },
"clearApprovalData": {
"message": "Jasné údaje o schválení"
},
diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json
index 98a8bf972..3e93172ca 100644
--- a/app/_locales/de/messages.json
+++ b/app/_locales/de/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "Datenschutzmodus"
- },
- "privacyModeDescription": {
- "message": "Websites müssen Zugriff anfordern, um Ihre Kontoinformationen anzuzeigen."
- },
"exposeAccounts": {
"message": "Expose Konten"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Genehmigte Website-Daten wurden erfolgreich gelöscht."
},
- "approvalData": {
- "message": "Genehmigungsdaten"
- },
- "approvalDataDescription": {
- "message": "Löschen Sie die genehmigten Website-Daten, damit alle Websites erneut eine Genehmigung anfordern müssen."
- },
"clearApprovalData": {
"message": "Genehmigungsdaten löschen"
},
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index d1a0cd12c..2b957129d 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1,22 +1,4 @@
{
- "shareAddress": {
- "message": "Share Address"
- },
- "shareAddressToConnect": {
- "message": "Share your address to connect to $1?"
- },
- "shareAddressInfo": {
- "message": "Sharing your address with $1 will allow you to interact with this dapp. This permission is to protect your privacy by default. You may need to reload the dapp for the change to take effect."
- },
- "privacyModeDefault": {
- "message": "Privacy Mode is now enabled by default"
- },
- "privacyMode": {
- "message": "Privacy Mode"
- },
- "privacyModeDescription": {
- "message": "Websites must request access to view your account information."
- },
"exposeAccounts": {
"message": "Expose Accounts"
},
@@ -32,20 +14,35 @@
"confirmClear": {
"message": "Are you sure you want to clear approved websites?"
},
+ "connections": {
+ "message": "Connections"
+ },
+ "connectionsSettingsDescription": {
+ "message": "Sites allowed to read your accounts"
+ },
+ "addSite": {
+ "message": "Add Site"
+ },
+ "addSiteDescription": {
+ "message": "Manually add a site to allow it access to your accounts, useful for older dapps"
+ },
+ "connected": {
+ "message": "Connected"
+ },
+ "connectedDescription": {
+ "message": "The list of sites allowed access to your addresses"
+ },
+ "privacyModeDefault": {
+ "message": "Privacy Mode is now enabled by default"
+ },
"contractInteraction": {
"message": "Contract Interaction"
},
"clearApprovalDataSuccess": {
"message": "Approved website data cleared successfully."
},
- "approvalData": {
- "message": "Privacy Data"
- },
- "approvalDataDescription": {
- "message": "Clear privacy data so all websites must request access to view account information again."
- },
"clearApprovalData": {
- "message": "Clear Privacy Data"
+ "message": "Remove all sites"
},
"reject": {
"message": "Reject"
diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json
index bf466fdc2..066dc824c 100644
--- a/app/_locales/es/messages.json
+++ b/app/_locales/es/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "Modo privado"
- },
- "privacyModeDescription": {
- "message": "Los sitios web deben solicitar acceso para ver la información de su cuenta."
- },
"exposeAccounts": {
"message": "Exponer cuentas"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Los datos aprobados del sitio web se borraron con éxito."
},
- "approvalData": {
- "message": "Datos de aprobación"
- },
- "approvalDataDescription": {
- "message": "Borrar la información privada de modo que todos los sitios deban volver a requerir acceso para acceder a los datos de la cuenta."
- },
"clearApprovalData": {
"message": "Borrar datos de aprobación"
},
diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json
index 70effb3ea..2c9308e76 100644
--- a/app/_locales/fr/messages.json
+++ b/app/_locales/fr/messages.json
@@ -1,10 +1,4 @@
{
- "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": {
"message": "Exposer les comptes"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Les données de site Web approuvées ont été supprimées."
},
- "approvalData": {
- "message": "Données d'approbation"
- },
- "approvalDataDescription": {
- "message": "Effacer les données de site Web approuvées afin que tous les sites doivent à nouveau demander l'approbation."
- },
"clearApprovalData": {
"message": "Effacer les données d'approbation"
},
diff --git a/app/_locales/hn/messages.json b/app/_locales/hn/messages.json
index 54ddcc02b..d7e87b005 100644
--- a/app/_locales/hn/messages.json
+++ b/app/_locales/hn/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "गोपनीयता मोड"
- },
- "privacyModeDescription": {
- "message": "वेबसाइटों को आपकी खाता जानकारी देखने के लिए पहुंच का अनुरोध करना होगा।"
- },
"exposeAccounts": {
"message": "खातों का पर्दाफाश करें"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "स्वीकृत वेबसाइट डेटा सफलतापूर्वक मंजूरी दे दी।"
},
- "approvalData": {
- "message": "स्वीकृति डेटा"
- },
- "approvalDataDescription": {
- "message": "अनुमोदित वेबसाइट डेटा साफ़ करें ताकि सभी साइटों को फिर से अनुमोदन का अनुरोध करना होगा।"
- },
"clearApprovalData": {
"message": "अनुमोदन डेटा साफ़ करें"
},
diff --git a/app/_locales/ht/messages.json b/app/_locales/ht/messages.json
index 720c35889..e73d07738 100644
--- a/app/_locales/ht/messages.json
+++ b/app/_locales/ht/messages.json
@@ -1,10 +1,4 @@
{
- "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"
},
@@ -20,12 +14,6 @@
"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"
},
diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json
index c79f22b71..082df85fd 100644
--- a/app/_locales/it/messages.json
+++ b/app/_locales/it/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "Modalità privacy"
- },
- "privacyModeDescription": {
- "message": "I siti Web devono richiedere l'accesso per visualizzare le informazioni del tuo account."
- },
"exposeAccounts": {
"message": "Esponi Accounts"
},
@@ -26,12 +20,6 @@
"clearApprovalDataSuccess": {
"message": "Dati del sito Web approvati cancellati correttamente."
},
- "approvalData": {
- "message": "Dati di approvazione"
- },
- "approvalDataDescription": {
- "message": "Cancella i dati del sito web approvati, quindi tutti i siti devono richiedere nuovamente l'approvazione."
- },
"clearApprovalData": {
"message": "Cancella i dati di approvazione"
},
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index 49b033997..cde9b30be 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "プライバシーモード"
- },
- "privacyModeDescription": {
- "message": "ウェブサイトはあなたのアカウント情報を閲覧するためのアクセスを要求する必要があります。"
- },
"exposeAccounts": {
"message": "アカウントを公開する"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "承認されたウェブサイトデータが正常に消去されました。"
},
- "approvalData": {
- "message": "承認データ"
- },
- "approvalDataDescription": {
- "message": "承認されたウェブサイトのデータをクリアすると、すべてのサイトで承認を再度要求する必要があります"
- },
"clearApprovalData": {
"message": "承認データのクリア"
},
diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json
index 541d9dd5c..5a3ce1c1c 100644
--- a/app/_locales/ko/messages.json
+++ b/app/_locales/ko/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "개인 정보 보호 모드"
- },
- "privacyModeDescription": {
- "message": "웹 사이트는 계정 정보를 볼 수있는 액세스 권한을 요청해야합니다."
- },
"exposeAccounts": {
"message": "계정 노출"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "승인 된 웹 사이트 데이터가 성공적으로 삭제되었습니다."
},
- "approvalData": {
- "message": "승인 데이터"
- },
- "approvalDataDescription": {
- "message": "승인 된 웹 사이트 데이터를 삭제하여 모든 사이트에서 승인을 다시 요청해야합니다."
- },
"clearApprovalData": {
"message": "승인 데이터 삭제"
},
diff --git a/app/_locales/nl/messages.json b/app/_locales/nl/messages.json
index ade7327de..3159592ac 100644
--- a/app/_locales/nl/messages.json
+++ b/app/_locales/nl/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "Privacy-modus"
- },
- "privacyModeDescription": {
- "message": "Websites moeten toegang vragen om uw accountgegevens te bekijken."
- },
"exposeAccounts": {
"message": "Expose Accounts"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Goedgekeurde websitegegevens zijn met succes gewist."
},
- "approvalData": {
- "message": "Goedkeuringsgegevens"
- },
- "approvalDataDescription": {
- "message": "Goedgekeurde websitegegevens wissen zodat alle sites opnieuw goedkeuring moeten aanvragen."
- },
"clearApprovalData": {
"message": "Gegevens over goedkeuring wissen"
},
diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json
index 9763bd273..8179e135d 100644
--- a/app/_locales/ph/messages.json
+++ b/app/_locales/ph/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "Mode ng Privacy"
- },
- "privacyModeDescription": {
- "message": "Dapat humiling ng access ang mga website upang tingnan ang impormasyon ng iyong account."
- },
"exposeAccounts": {
"message": "Ilantad ang Mga Account"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Matagumpay na na-clear ang data ng aprubadong website."
},
- "approvalData": {
- "message": "Data ng Pag-apruba"
- },
- "approvalDataDescription": {
- "message": "I-clear ang naaprubahang data ng website upang ang lahat ng site ay dapat humiling muli ng pag-apruba"
- },
"clearApprovalData": {
"message": "Tanggalin ang data ng pag-apruba"
},
diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json
index 5724bce88..ba406d464 100644
--- a/app/_locales/pt/messages.json
+++ b/app/_locales/pt/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "Modo de privacidade"
- },
- "privacyModeDescription": {
- "message": "Os sites devem solicitar acesso para visualizar as informações da sua conta."
- },
"exposeAccounts": {
"message": "Expor contas"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Dados aprovados do website foram limpos com sucesso."
},
- "approvalData": {
- "message": "Dados de aprovação"
- },
- "approvalDataDescription": {
- "message": "Limpe os dados aprovados do website para que todos os sites solicitem aprovação novamente."
- },
"clearApprovalData": {
"message": "Limpar dados de aprovação"
},
diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json
index e56441186..1437f6444 100644
--- a/app/_locales/ru/messages.json
+++ b/app/_locales/ru/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "Режим конфиденциальности"
- },
- "privacyModeDescription": {
- "message": "Веб-сайты должны запрашивать доступ для просмотра информации об учетной записи."
- },
"exposeAccounts": {
"message": "Открыть счета"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Утвержденные данные веб-сайта успешно удалены."
},
- "approvalData": {
- "message": "Данные об утверждении"
- },
- "approvalDataDescription": {
- "message": "Очистите утвержденные данные веб-сайта, чтобы все сайты снова запросили подтверждение."
- },
"clearApprovalData": {
"message": "Четкие данные об утверждении"
},
diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json
index f98de674b..89b077dae 100644
--- a/app/_locales/sk/messages.json
+++ b/app/_locales/sk/messages.json
@@ -1,10 +1,4 @@
{
- "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": {
"message": "Vystavte účty"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Schválené údaje webových stránek byly úspěšně zrušeny."
},
- "approvalData": {
- "message": "Údaje o schválení"
- },
- "approvalDataDescription": {
- "message": "Vymažte schválené údaje webových stránek, aby všechny weby znovu požádaly o schválení."
- },
"clearApprovalData": {
"message": "Jasné údaje o schválení"
},
diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json
index 563bae1b1..24f059f3d 100644
--- a/app/_locales/sl/messages.json
+++ b/app/_locales/sl/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "Zasebnostni način"
- },
- "privacyModeDescription": {
- "message": "Spletne strani morajo zahtevati dovoljenje za ogled podatkov o vašem računu."
- },
"privacyNotice": {
"message": "Obvestilo o zasebnosti"
},
@@ -26,12 +20,6 @@
"clearApprovalDataSuccess": {
"message": "Odobrene spletne strani uspešno počiščene."
},
- "approvalData": {
- "message": "Podatki o odobritvi"
- },
- "approvalDataDescription": {
- "message": "Počistite seznam odobrenih spletnih strani, tako da bodo morale ponovno zahtevati odobritev."
- },
"clearApprovalData": {
"message": "Počisti podatke o odobritvi"
},
diff --git a/app/_locales/th/messages.json b/app/_locales/th/messages.json
index a49cf31ad..7b1503764 100644
--- a/app/_locales/th/messages.json
+++ b/app/_locales/th/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "โหมดความเป็นส่วนตัว"
- },
- "privacyModeDescription": {
- "message": "เว็บไซต์ต้องขอเข้าถึงเพื่อดูข้อมูลบัญชีของคุณ"
- },
"exposeAccounts": {
"message": "เปิดเผยบัญชี"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "อนุมัติข้อมูลเว็บไซต์ที่ได้รับอนุมัติแล้ว"
},
- "approvalData": {
- "message": "ข้อมูลการอนุมัติ"
- },
- "approvalDataDescription": {
- "message": "ล้างข้อมูลเว็บไซต์ที่ได้รับการอนุมัติเพื่อให้ทุกไซต์ต้องขออนุมัติอีกครั้ง"
- },
"clearApprovalData": {
"message": "ล้างข้อมูลการอนุมัติ"
},
diff --git a/app/_locales/tml/messages.json b/app/_locales/tml/messages.json
index 2f883b26b..394558522 100644
--- a/app/_locales/tml/messages.json
+++ b/app/_locales/tml/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "தனியுரிமை முறை"
- },
- "privacyModeDescription": {
- "message": "உங்கள் கணக்குத் தகவலை பார்வையிட வலைத்தளங்கள் அணுகலைக் கோர வேண்டும்."
- },
"exposeAccounts": {
"message": "கணக்குகளை அம்பலப்படுத்துங்கள்"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "அங்கீகரிக்கப்பட்ட வலைத்தள தரவு வெற்றிகரமாக அழிக்கப்பட்டது."
},
- "approvalData": {
- "message": "ஒப்புதல் தரவு"
- },
- "approvalDataDescription": {
- "message": "அங்கீகரிக்கப்பட்ட வலைத்தள தரவை அழிக்கவும், அனைத்து தளங்களும் ஒப்புதல் மீண்டும் கோர வேண்டும்."
- },
"clearApprovalData": {
"message": "ஒப்புதல் தரவை அழி"
},
diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json
index 726024764..c807084c5 100644
--- a/app/_locales/tr/messages.json
+++ b/app/_locales/tr/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "Gizlilik modu"
- },
- "privacyModeDescription": {
- "message": "Web siteleri, hesap bilgilerinizi görmek için erişim istemek zorundadır."
- },
"exposeAccounts": {
"message": "Hesapları Açığa Çıkar"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Onaylanan web sitesi verileri başarıyla temizlendi."
},
- "approvalData": {
- "message": "Onay Verileri"
- },
- "approvalDataDescription": {
- "message": "Onaylanan web sitesi verilerini temizle, tüm sitelerin tekrar onay isteğinde bulunması gerekir."
- },
"clearApprovalData": {
"message": "Onay verilerini temizle"
},
diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json
index 622e0d818..38a098311 100644
--- a/app/_locales/vi/messages.json
+++ b/app/_locales/vi/messages.json
@@ -1,10 +1,4 @@
{
- "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": {
"message": "Hiển thị tài khoản"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "Đã xóa thành công dữ liệu trang web được phê duyệt."
},
- "approvalData": {
- "message": "Dữ liệu phê duyệt"
- },
- "approvalDataDescription": {
- "message": "Xóa dữ liệu trang web được phê duyệt để tất cả các trang web phải yêu cầu phê duyệt lại."
- },
"clearApprovalData": {
"message": "Xóa dữ liệu phê duyệt"
},
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
index d14b99acb..5cecffba9 100644
--- a/app/_locales/zh_CN/messages.json
+++ b/app/_locales/zh_CN/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "隐私模式"
- },
- "privacyModeDescription": {
- "message": "网站必须请求访问权限才能查看您的帐户信息。"
- },
"exposeAccounts": {
"message": "公开账户"
},
@@ -20,12 +14,6 @@
"clearApprovalDataSuccess": {
"message": "已批准的网站数据已成功清除。"
},
- "approvalData": {
- "message": "审批数据"
- },
- "approvalDataDescription": {
- "message": "清除已批准的网站数据,以便所有网站都必须再次申请"
- },
"clearApprovalData": {
"message": "清除批准数据"
},
diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json
index 2559fc0ad..3f4eecfd3 100644
--- a/app/_locales/zh_TW/messages.json
+++ b/app/_locales/zh_TW/messages.json
@@ -1,10 +1,4 @@
{
- "privacyMode": {
- "message": "隱私模式"
- },
- "privacyModeDescription": {
- "message": "網站必須請求訪問權限才能查看您的帳戶資訊"
- },
"exposeAccounts": {
"message": "公開賬戶"
},
@@ -23,12 +17,6 @@
"clearApprovalDataSuccess": {
"message": "已批准的網站紀錄已成功清除。"
},
- "approvalData": {
- "message": "審核紀錄"
- },
- "approvalDataDescription": {
- "message": "清除之前已批准過的網站審核紀錄,所有網站都必須再次申請"
- },
"clearApprovalData": {
"message": "清除批准數據"
},
diff --git a/app/scripts/background.js b/app/scripts/background.js
index cb87878a9..02497d487 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -409,7 +409,7 @@ function setupController (initState, initLangCode) {
controller.messageManager.on('updateBadge', updateBadge)
controller.personalMessageManager.on('updateBadge', updateBadge)
controller.typedMessageManager.on('updateBadge', updateBadge)
- controller.providerApprovalController.store.on('update', updateBadge)
+ controller.providerApprovalController.memStore.on('update', updateBadge)
/**
* Updates the Web Extension's "badge" number, on the little fox in the toolbar.
@@ -421,7 +421,7 @@ function setupController (initState, initLangCode) {
const unapprovedMsgCount = controller.messageManager.unapprovedMsgCount
const unapprovedPersonalMsgs = controller.personalMessageManager.unapprovedPersonalMsgCount
const unapprovedTypedMsgs = controller.typedMessageManager.unapprovedTypedMessagesCount
- const pendingProviderRequests = controller.providerApprovalController.store.getState().providerRequests.length
+ const pendingProviderRequests = controller.providerApprovalController.memStore.getState().providerRequests.length
const count = unapprovedTxCount + unapprovedMsgCount + unapprovedPersonalMsgs + unapprovedTypedMsgs + pendingProviderRequests
if (count) {
label = String(count)
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index d480834f5..f02708d40 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -41,7 +41,6 @@ class PreferencesController {
// for convenient testing of pre-release features, and should never
// perform sensitive operations.
featureFlags: {
- privacyMode: true,
},
knownMethodData: {},
participateInMetaMetrics: null,
diff --git a/app/scripts/controllers/provider-approval.js b/app/scripts/controllers/provider-approval.js
index 2300bb957..3beda6d53 100644
--- a/app/scripts/controllers/provider-approval.js
+++ b/app/scripts/controllers/provider-approval.js
@@ -7,26 +7,22 @@ const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware
*/
class ProviderApprovalController extends SafeEventEmitter {
/**
- * Determines if caching is enabled
- */
- caching = true
-
- /**
* Creates a ProviderApprovalController
*
* @param {Object} [config] - Options to configure controller
*/
- constructor ({ closePopup, keyringController, openPopup, preferencesController } = {}) {
+ constructor ({ closePopup, initState, keyringController, openPopup, preferencesController } = {}) {
super()
this.closePopup = closePopup
this.keyringController = keyringController
this.openPopup = openPopup
this.preferencesController = preferencesController
- this.store = new ObservableStore({
- approvedOrigins: {},
- dismissedOrigins: {},
+ this.memStore = new ObservableStore({
providerRequests: [],
})
+
+ const defaultState = { approvedOrigins: {} }
+ this.store = new ObservableStore(Object.assign(defaultState, initState))
}
/**
@@ -65,11 +61,17 @@ class ProviderApprovalController extends SafeEventEmitter {
* @param {string} siteImage - The icon of the window requesting full provider access
*/
_handleProviderRequest (origin, siteTitle, siteImage) {
- this.store.updateState({ providerRequests: [{ origin, siteTitle, siteImage }] })
+ const { providerRequests } = this.memStore.getState()
+ this.memStore.updateState({
+ providerRequests: [
+ ...providerRequests,
+ { origin, siteTitle, siteImage },
+ ],
+ })
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
- const { approvedOrigins, dismissedOrigins } = this.store.getState()
- const originAlreadyHandled = approvedOrigins[origin] || dismissedOrigins[origin]
- if (originAlreadyHandled && this.caching && isUnlocked) {
+ const { approvedOrigins } = this.store.getState()
+ const originAlreadyHandled = approvedOrigins[origin]
+ if (originAlreadyHandled && isUnlocked) {
return
}
this.openPopup && this.openPopup()
@@ -85,23 +87,20 @@ class ProviderApprovalController extends SafeEventEmitter {
this.closePopup()
}
- const { approvedOrigins, dismissedOrigins, providerRequests } = this.store.getState()
-
- let _dismissedOrigins = dismissedOrigins
- if (dismissedOrigins[origin]) {
- _dismissedOrigins = Object.assign({}, dismissedOrigins)
- delete _dismissedOrigins[origin]
- }
-
+ const { approvedOrigins } = this.store.getState()
+ const { providerRequests } = this.memStore.getState()
+ const providerRequest = providerRequests.find((request) => request.origin === origin)
const remainingProviderRequests = providerRequests.filter(request => request.origin !== origin)
this.store.updateState({
approvedOrigins: {
...approvedOrigins,
- [origin]: true,
+ [origin]: {
+ siteTitle: providerRequest ? providerRequest.siteTitle : null,
+ siteImage: providerRequest ? providerRequest.siteImage : null,
+ },
},
- dismissedOrigins: _dismissedOrigins,
- providerRequests: remainingProviderRequests,
})
+ this.memStore.updateState({ providerRequests: remainingProviderRequests })
this.emit(`resolvedRequest:${origin}`, { approved: true })
}
@@ -115,51 +114,21 @@ class ProviderApprovalController extends SafeEventEmitter {
this.closePopup()
}
- const { approvedOrigins, providerRequests, dismissedOrigins } = this.store.getState()
+ const { approvedOrigins } = this.store.getState()
+ const { providerRequests } = this.memStore.getState()
const remainingProviderRequests = providerRequests.filter(request => request.origin !== origin)
// We're cloning and deleting keys here because we don't want to keep unneeded keys
const _approvedOrigins = Object.assign({}, approvedOrigins)
delete _approvedOrigins[origin]
- this.store.putState({
- approvedOrigins: _approvedOrigins,
- providerRequests: remainingProviderRequests,
- dismissedOrigins: {
- ...dismissedOrigins,
- [origin]: true,
- },
- })
+ this.store.putState({ approvedOrigins: _approvedOrigins })
+ this.memStore.putState({ providerRequests: remainingProviderRequests })
this.emit(`resolvedRequest:${origin}`, { approved: false })
}
/**
- * Silently approves access to a full Ethereum provider API for the origin
- *
- * @param {string} origin - origin of the domain that had provider access approved
- */
- forceApproveProviderRequestByOrigin (origin) {
- const { approvedOrigins, dismissedOrigins, providerRequests } = this.store.getState()
- const remainingProviderRequests = providerRequests.filter(request => request.origin !== origin)
-
- let _dismissedOrigins = dismissedOrigins
- if (dismissedOrigins[origin]) {
- _dismissedOrigins = Object.assign({}, dismissedOrigins)
- delete _dismissedOrigins[origin]
- }
-
- this.store.updateState({
- approvedOrigins: {
- ...approvedOrigins,
- [origin]: true,
- },
- dismissedOrigins: _dismissedOrigins,
- providerRequests: remainingProviderRequests,
- })
- }
-
- /**
- * Clears any cached approvals for user-approved origins
+ * Clears any approvals for user-approved origins
*/
clearApprovedOrigins () {
this.store.updateState({
@@ -174,10 +143,17 @@ class ProviderApprovalController extends SafeEventEmitter {
* @returns {boolean} - True if the origin has been approved
*/
shouldExposeAccounts (origin) {
- const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
- return !privacyMode || Boolean(this.store.getState().approvedOrigins[origin])
+ return Boolean(this.store.getState().approvedOrigins[origin])
}
+ /**
+ * Returns a merged state representation
+ * @return {object}
+ * @private
+ */
+ _getMergedState () {
+ return Object.assign({}, this.memStore.getState(), this.store.getState())
+ }
}
module.exports = ProviderApprovalController
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index ee148c285..e9299d5f8 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -251,6 +251,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.providerApprovalController = new ProviderApprovalController({
closePopup: opts.closePopup,
+ initState: initState.ProviderApprovalController,
keyringController: this.keyringController,
openPopup: opts.openPopup,
preferencesController: this.preferencesController,
@@ -268,6 +269,7 @@ module.exports = class MetamaskController extends EventEmitter {
InfuraController: this.infuraController.store,
CachedBalancesController: this.cachedBalancesController.store,
OnboardingController: this.onboardingController.store,
+ ProviderApprovalController: this.providerApprovalController.store,
})
this.memStore = new ComposableObservableStore(null, {
@@ -288,8 +290,10 @@ module.exports = class MetamaskController extends EventEmitter {
CurrencyController: this.currencyRateController,
ShapeshiftController: this.shapeshiftController,
InfuraController: this.infuraController.store,
- ProviderApprovalController: this.providerApprovalController.store,
OnboardingController: this.onboardingController.store,
+ // ProviderApprovalController
+ ProviderApprovalController: this.providerApprovalController.store,
+ ProviderApprovalControllerMemStore: this.providerApprovalController.memStore,
})
this.memStore.subscribe(this.sendUpdate.bind(this))
}
@@ -503,7 +507,6 @@ module.exports = class MetamaskController extends EventEmitter {
// provider approval
approveProviderRequestByOrigin: providerApprovalController.approveProviderRequestByOrigin.bind(providerApprovalController),
rejectProviderRequestByOrigin: providerApprovalController.rejectProviderRequestByOrigin.bind(providerApprovalController),
- forceApproveProviderRequestByOrigin: providerApprovalController.forceApproveProviderRequestByOrigin.bind(providerApprovalController),
clearApprovedOrigins: providerApprovalController.clearApprovedOrigins.bind(providerApprovalController),
// onboarding controller
diff --git a/app/scripts/migrations/036.js b/app/scripts/migrations/036.js
new file mode 100644
index 000000000..5aad16a84
--- /dev/null
+++ b/app/scripts/migrations/036.js
@@ -0,0 +1,30 @@
+const version = 36
+const clone = require('clone')
+
+/**
+ * The purpose of this migration is to remove the {@code privacyMode} feature flag.
+ */
+module.exports = {
+ version,
+ migrate: async function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ const state = versionedData.data
+ versionedData.data = transformState(state)
+ return versionedData
+ },
+}
+
+function transformState (state) {
+ const { PreferencesController } = state
+
+ if (PreferencesController) {
+ const featureFlags = PreferencesController.featureFlags || {}
+
+ if (typeof featureFlags.privacyMode !== 'undefined') {
+ delete featureFlags.privacyMode
+ }
+ }
+
+ return state
+}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
index 6b8860e52..93a4f0a0a 100644
--- a/app/scripts/migrations/index.js
+++ b/app/scripts/migrations/index.js
@@ -46,4 +46,5 @@ module.exports = [
require('./033'),
require('./034'),
require('./035'),
+ require('./036'),
]
diff --git a/test/unit/app/controllers/provider-approval-test.js b/test/unit/app/controllers/provider-approval-test.js
new file mode 100644
index 000000000..c12387a33
--- /dev/null
+++ b/test/unit/app/controllers/provider-approval-test.js
@@ -0,0 +1,260 @@
+const assert = require('assert')
+const sinon = require('sinon')
+const ProviderApprovalController = require('../../../../app/scripts/controllers/provider-approval')
+
+const mockLockedKeyringController = {
+ memStore: {
+ getState: () => ({
+ isUnlocked: false,
+ }),
+ },
+}
+
+const mockUnlockedKeyringController = {
+ memStore: {
+ getState: () => ({
+ isUnlocked: true,
+ }),
+ },
+}
+
+describe('ProviderApprovalController', () => {
+ describe('#_handleProviderRequest', () => {
+ it('should add a pending provider request when unlocked', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ assert.deepEqual(controller._getMergedState(), {
+ approvedOrigins: {},
+ providerRequests: [{
+ origin: 'example.com',
+ siteTitle: 'Example',
+ siteImage: 'https://example.com/logo.svg',
+ }],
+ })
+ })
+
+ it('should add a pending provider request when locked', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockLockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ assert.deepEqual(controller._getMergedState(), {
+ approvedOrigins: {},
+ providerRequests: [{
+ origin: 'example.com',
+ siteTitle: 'Example',
+ siteImage: 'https://example.com/logo.svg',
+ }],
+ })
+ })
+
+ it('should add a 2nd pending provider request when unlocked', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example1.com', 'Example 1', 'https://example1.com/logo.svg')
+ controller._handleProviderRequest('example2.com', 'Example 2', 'https://example2.com/logo.svg')
+ assert.deepEqual(controller._getMergedState(), {
+ approvedOrigins: {},
+ providerRequests: [{
+ origin: 'example1.com',
+ siteTitle: 'Example 1',
+ siteImage: 'https://example1.com/logo.svg',
+ }, {
+ origin: 'example2.com',
+ siteTitle: 'Example 2',
+ siteImage: 'https://example2.com/logo.svg',
+ }],
+ })
+ })
+
+ it('should add a 2nd pending provider request when locked', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockLockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example1.com', 'Example 1', 'https://example1.com/logo.svg')
+ controller._handleProviderRequest('example2.com', 'Example 2', 'https://example2.com/logo.svg')
+ assert.deepEqual(controller._getMergedState(), {
+ approvedOrigins: {},
+ providerRequests: [{
+ origin: 'example1.com',
+ siteTitle: 'Example 1',
+ siteImage: 'https://example1.com/logo.svg',
+ }, {
+ origin: 'example2.com',
+ siteTitle: 'Example 2',
+ siteImage: 'https://example2.com/logo.svg',
+ }],
+ })
+ })
+
+ it('should call openPopup when unlocked and when given', () => {
+ const openPopup = sinon.spy()
+ const controller = new ProviderApprovalController({
+ openPopup,
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ assert.ok(openPopup.calledOnce)
+ })
+
+ it('should call openPopup when locked and when given', () => {
+ const openPopup = sinon.spy()
+ const controller = new ProviderApprovalController({
+ openPopup,
+ keyringController: mockLockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ assert.ok(openPopup.calledOnce)
+ })
+
+ it('should NOT call openPopup when unlocked and when the domain has already been approved', () => {
+ const openPopup = sinon.spy()
+ const controller = new ProviderApprovalController({
+ openPopup,
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller.store.updateState({
+ approvedOrigins: {
+ 'example.com': {
+ siteTitle: 'Example',
+ siteImage: 'https://example.com/logo.svg',
+ },
+ },
+ })
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ assert.ok(openPopup.notCalled)
+ })
+ })
+
+ describe('#approveProviderRequestByOrigin', () => {
+ it('should mark the origin as approved and remove the provider request', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ controller.approveProviderRequestByOrigin('example.com')
+ assert.deepEqual(controller._getMergedState(), {
+ providerRequests: [],
+ approvedOrigins: {
+ 'example.com': {
+ siteTitle: 'Example',
+ siteImage: 'https://example.com/logo.svg',
+ },
+ },
+ })
+ })
+
+ it('should mark the origin as approved and multiple requests for the same domain', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ controller.approveProviderRequestByOrigin('example.com')
+ assert.deepEqual(controller._getMergedState(), {
+ providerRequests: [],
+ approvedOrigins: {
+ 'example.com': {
+ siteTitle: 'Example',
+ siteImage: 'https://example.com/logo.svg',
+ },
+ },
+ })
+ })
+
+ it('should mark the origin as approved without a provider request', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller.approveProviderRequestByOrigin('example.com')
+ assert.deepEqual(controller._getMergedState(), {
+ providerRequests: [],
+ approvedOrigins: {
+ 'example.com': {
+ siteTitle: null,
+ siteImage: null,
+ },
+ },
+ })
+ })
+ })
+
+ describe('#rejectProviderRequestByOrigin', () => {
+ it('should remove the origin from approved', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ controller.approveProviderRequestByOrigin('example.com')
+ controller.rejectProviderRequestByOrigin('example.com')
+ assert.deepEqual(controller._getMergedState(), {
+ providerRequests: [],
+ approvedOrigins: {},
+ })
+ })
+
+ it('should reject the origin even without a pending request', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller.rejectProviderRequestByOrigin('example.com')
+ assert.deepEqual(controller._getMergedState(), {
+ providerRequests: [],
+ approvedOrigins: {},
+ })
+ })
+ })
+
+ describe('#clearApprovedOrigins', () => {
+ it('should clear the approved origins', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ controller.approveProviderRequestByOrigin('example.com')
+ controller.clearApprovedOrigins()
+ assert.deepEqual(controller._getMergedState(), {
+ providerRequests: [],
+ approvedOrigins: {},
+ })
+ })
+ })
+
+ describe('#shouldExposeAccounts', () => {
+ it('should return true for an approved origin', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ controller.approveProviderRequestByOrigin('example.com')
+ assert.ok(controller.shouldExposeAccounts('example.com'))
+ })
+
+ it('should return false for an origin not yet approved', () => {
+ const controller = new ProviderApprovalController({
+ keyringController: mockUnlockedKeyringController,
+ })
+
+ controller._handleProviderRequest('example.com', 'Example', 'https://example.com/logo.svg')
+ controller.approveProviderRequestByOrigin('example.com')
+ assert.ok(!controller.shouldExposeAccounts('bad.website'))
+ })
+ })
+})
diff --git a/test/unit/migrations/036-test.js b/test/unit/migrations/036-test.js
new file mode 100644
index 000000000..5c13c1f0c
--- /dev/null
+++ b/test/unit/migrations/036-test.js
@@ -0,0 +1,119 @@
+const assert = require('assert')
+const migration36 = require('../../../app/scripts/migrations/036')
+
+describe('migration #36', () => {
+ it('should update the version metadata', (done) => {
+ const oldStorage = {
+ 'meta': {
+ 'version': 35,
+ },
+ 'data': {},
+ }
+
+ migration36.migrate(oldStorage)
+ .then((newStorage) => {
+ assert.deepEqual(newStorage.meta, {
+ 'version': 36,
+ })
+ done()
+ })
+ .catch(done)
+ })
+
+ it('should remove privacyMode if featureFlags.privacyMode was false', (done) => {
+ const oldStorage = {
+ 'meta': {},
+ 'data': {
+ 'PreferencesController': {
+ 'featureFlags': {
+ 'privacyMode': false,
+ },
+ },
+ },
+ }
+
+ migration36.migrate(oldStorage)
+ .then((newStorage) => {
+ assert.deepEqual(newStorage.data.PreferencesController, {
+ 'featureFlags': {
+ },
+ })
+ done()
+ })
+ .catch(done)
+ })
+
+ it('should remove privacyMode if featureFlags.privacyMode was true', (done) => {
+ const oldStorage = {
+ 'meta': {},
+ 'data': {
+ 'PreferencesController': {
+ 'featureFlags': {
+ 'privacyMode': true,
+ },
+ },
+ },
+ }
+
+ migration36.migrate(oldStorage)
+ .then((newStorage) => {
+ assert.deepEqual(newStorage.data.PreferencesController, {
+ 'featureFlags': {
+ },
+ })
+ done()
+ })
+ .catch(done)
+ })
+
+ it('should NOT change any state if privacyMode does not exist', (done) => {
+ const oldStorage = {
+ 'meta': {},
+ 'data': {
+ 'PreferencesController': {
+ 'migratedPrivacyMode': true,
+ 'featureFlags': {
+ },
+ },
+ },
+ }
+
+ migration36.migrate(oldStorage)
+ .then((newStorage) => {
+ assert.deepEqual(newStorage.data, oldStorage.data)
+ done()
+ })
+ .catch(done)
+ })
+
+ it('should NOT change any state if PreferencesController is missing', (done) => {
+ const oldStorage = {
+ 'meta': {},
+ 'data': {},
+ }
+
+ migration36.migrate(oldStorage)
+ .then((newStorage) => {
+ assert.deepEqual(newStorage.data, oldStorage.data)
+ done()
+ })
+ .catch(done)
+ })
+
+ it('should NOT change any state if featureFlags is missing', (done) => {
+ const oldStorage = {
+ 'meta': {},
+ 'data': {
+ 'PreferencesController': {
+ },
+ },
+ }
+
+ migration36.migrate(oldStorage)
+ .then((newStorage) => {
+ assert.deepEqual(newStorage.data, oldStorage.data)
+ done()
+ })
+ .catch(done)
+ })
+})
diff --git a/ui/app/helpers/constants/routes.js b/ui/app/helpers/constants/routes.js
index cd26b3628..8c2a8b8bc 100644
--- a/ui/app/helpers/constants/routes.js
+++ b/ui/app/helpers/constants/routes.js
@@ -3,6 +3,7 @@ const UNLOCK_ROUTE = '/unlock'
const LOCK_ROUTE = '/lock'
const SETTINGS_ROUTE = '/settings'
const GENERAL_ROUTE = '/settings/general'
+const CONNECTIONS_ROUTE = '/settings/connections'
const ADVANCED_ROUTE = '/settings/advanced'
const SECURITY_ROUTE = '/settings/security'
const ABOUT_US_ROUTE = '/settings/about-us'
@@ -82,6 +83,7 @@ module.exports = {
ADVANCED_ROUTE,
SECURITY_ROUTE,
GENERAL_ROUTE,
+ CONNECTIONS_ROUTE,
ABOUT_US_ROUTE,
CONTACT_LIST_ROUTE,
CONTACT_EDIT_ROUTE,
@@ -93,4 +95,3 @@ module.exports = {
NETWORKS_ROUTE,
INITIALIZE_BACKUP_SEED_PHRASE_ROUTE,
}
-
diff --git a/ui/app/pages/home/home.component.js b/ui/app/pages/home/home.component.js
index 66d962ff1..ff7428eff 100644
--- a/ui/app/pages/home/home.component.js
+++ b/ui/app/pages/home/home.component.js
@@ -21,18 +21,10 @@ export default class Home extends PureComponent {
}
static defaultProps = {
- activeTab: {},
unsetMigratedPrivacyMode: null,
- forceApproveProviderRequestByOrigin: null,
}
static propTypes = {
- activeTab: PropTypes.shape({
- origin: PropTypes.string,
- protocol: PropTypes.string,
- title: PropTypes.string,
- url: PropTypes.string,
- }),
history: PropTypes.object,
forgottenPassword: PropTypes.bool,
suggestedTokens: PropTypes.object,
@@ -40,10 +32,7 @@ export default class Home extends PureComponent {
providerRequests: PropTypes.array,
showPrivacyModeNotification: PropTypes.bool.isRequired,
unsetMigratedPrivacyMode: PropTypes.func,
- viewingUnconnectedDapp: PropTypes.bool.isRequired,
- forceApproveProviderRequestByOrigin: PropTypes.func,
shouldShowSeedPhraseReminder: PropTypes.bool,
- rejectProviderRequestByOrigin: PropTypes.func,
isPopup: PropTypes.bool,
}
@@ -73,16 +62,12 @@ export default class Home extends PureComponent {
render () {
const { t } = this.context
const {
- activeTab,
forgottenPassword,
providerRequests,
history,
showPrivacyModeNotification,
unsetMigratedPrivacyMode,
- viewingUnconnectedDapp,
- forceApproveProviderRequestByOrigin,
shouldShowSeedPhraseReminder,
- rejectProviderRequestByOrigin,
isPopup,
} = this.props
@@ -121,20 +106,6 @@ export default class Home extends PureComponent {
/>,
},
{
- shouldBeRendered: viewingUnconnectedDapp,
- component: <HomeNotification
- descriptionText={t('shareAddressToConnect', [activeTab.origin])}
- acceptText={t('shareAddress')}
- onAccept={() => {
- forceApproveProviderRequestByOrigin(activeTab.origin)
- }}
- ignoreText={t('dismiss')}
- onIgnore={() => rejectProviderRequestByOrigin(activeTab.origin)}
- infoText={t('shareAddressInfo', [activeTab.origin])}
- key="home-shareAddressToConnect"
- />,
- },
- {
shouldBeRendered: shouldShowSeedPhraseReminder,
component: <HomeNotification
descriptionText={t('backupApprovalNotice')}
diff --git a/ui/app/pages/home/home.container.js b/ui/app/pages/home/home.container.js
index fdcb3ded4..4de51ee50 100644
--- a/ui/app/pages/home/home.container.js
+++ b/ui/app/pages/home/home.container.js
@@ -5,39 +5,23 @@ import { withRouter } from 'react-router-dom'
import { unconfirmedTransactionsCountSelector } from '../../selectors/confirm-transaction'
import { getCurrentEthBalance } from '../../selectors/selectors'
import {
- forceApproveProviderRequestByOrigin,
unsetMigratedPrivacyMode,
- rejectProviderRequestByOrigin,
} from '../../store/actions'
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
import { ENVIRONMENT_TYPE_POPUP } from '../../../../app/scripts/lib/enums'
-const activeTabDappProtocols = ['http:', 'https:', 'dweb:', 'ipfs:', 'ipns:', 'ssb:']
-
const mapStateToProps = state => {
- const { activeTab, metamask, appState } = state
+ const { metamask, appState } = state
const {
- approvedOrigins,
- dismissedOrigins,
suggestedTokens,
providerRequests,
migratedPrivacyMode,
- featureFlags: {
- privacyMode,
- } = {},
seedPhraseBackedUp,
tokens,
} = metamask
const accountBalance = getCurrentEthBalance(state)
const { forgottenPassword } = appState
- const isUnconnected = Boolean(
- activeTab &&
- activeTabDappProtocols.includes(activeTab.protocol) &&
- privacyMode &&
- !approvedOrigins[activeTab.origin] &&
- !dismissedOrigins[activeTab.origin]
- )
const isPopup = getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP
return {
@@ -46,8 +30,6 @@ const mapStateToProps = state => {
unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state),
providerRequests,
showPrivacyModeNotification: migratedPrivacyMode,
- activeTab,
- viewingUnconnectedDapp: isUnconnected && isPopup,
shouldShowSeedPhraseReminder: !seedPhraseBackedUp && (parseInt(accountBalance, 16) > 0 || tokens.length > 0),
isPopup,
}
@@ -55,8 +37,6 @@ const mapStateToProps = state => {
const mapDispatchToProps = (dispatch) => ({
unsetMigratedPrivacyMode: () => dispatch(unsetMigratedPrivacyMode()),
- forceApproveProviderRequestByOrigin: (origin) => dispatch(forceApproveProviderRequestByOrigin(origin)),
- rejectProviderRequestByOrigin: origin => dispatch(rejectProviderRequestByOrigin(origin)),
})
export default compose(
diff --git a/ui/app/pages/settings/connections-tab/connected-site-row/connected-site-row.component.js b/ui/app/pages/settings/connections-tab/connected-site-row/connected-site-row.component.js
new file mode 100644
index 000000000..37c068ab5
--- /dev/null
+++ b/ui/app/pages/settings/connections-tab/connected-site-row/connected-site-row.component.js
@@ -0,0 +1,31 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+
+export default class ConnectedSiteRow extends PureComponent {
+ static defaultProps = {
+ siteTitle: null,
+ siteImage: null,
+ onDelete: () => {},
+ }
+
+ static propTypes = {
+ siteTitle: PropTypes.string,
+ siteImage: PropTypes.string,
+ origin: PropTypes.string.isRequired,
+ onDelete: PropTypes.func,
+ }
+
+ render () {
+ const {
+ origin,
+ onDelete,
+ } = this.props
+
+ return (
+ <div className="connected-site-row">
+ <div className="connected-site-row__origin">{origin}</div>
+ <div className="connected-site-row__delete" onClick={onDelete}><i className="fa fa-trash" /></div>
+ </div>
+ )
+ }
+}
diff --git a/ui/app/pages/settings/connections-tab/connected-site-row/index.js b/ui/app/pages/settings/connections-tab/connected-site-row/index.js
new file mode 100644
index 000000000..57023b6da
--- /dev/null
+++ b/ui/app/pages/settings/connections-tab/connected-site-row/index.js
@@ -0,0 +1 @@
+export { default } from './connected-site-row.component'
diff --git a/ui/app/pages/settings/connections-tab/connected-site-row/index.scss b/ui/app/pages/settings/connections-tab/connected-site-row/index.scss
new file mode 100644
index 000000000..f0957a8e9
--- /dev/null
+++ b/ui/app/pages/settings/connections-tab/connected-site-row/index.scss
@@ -0,0 +1,14 @@
+.connected-site-row {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+
+ &__origin {
+ font-family: monospace;
+ }
+
+ &__delete {
+ padding: 8px;
+ }
+}
diff --git a/ui/app/pages/settings/connections-tab/connections-tab.component.js b/ui/app/pages/settings/connections-tab/connections-tab.component.js
new file mode 100644
index 000000000..3309fcc0d
--- /dev/null
+++ b/ui/app/pages/settings/connections-tab/connections-tab.component.js
@@ -0,0 +1,133 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import ConnectedSiteEntry from './connected-site-row'
+import TextField from '../../../components/ui/text-field'
+import Button from '../../../components/ui/button'
+
+export default class ConnectionsTab extends PureComponent {
+ static contextTypes = {
+ t: PropTypes.func,
+ metricsEvent: PropTypes.func,
+ }
+
+ static defaultProps = {
+ activeTab: {},
+ }
+
+ static propTypes = {
+ activeTab: PropTypes.object,
+ approvedOrigins: PropTypes.object.isRequired,
+ approveProviderRequestByOrigin: PropTypes.func.isRequired,
+ rejectProviderRequestByOrigin: PropTypes.func.isRequired,
+ showClearApprovalModal: PropTypes.func.isRequired,
+ }
+
+ state = {
+ input: this.props.activeTab.origin || '',
+ }
+
+ handleAddOrigin = () => {
+ const newOrigin = this.state.input
+ this.setState({
+ input: '',
+ }, () => {
+ if (newOrigin && newOrigin.trim()) {
+ this.props.approveProviderRequestByOrigin(newOrigin)
+ }
+ })
+ }
+
+ renderNewOriginInput () {
+ const { t } = this.context
+
+ return (
+ <div className="settings-page__content-row">
+ <div className="settings-page__content-item">
+ <span>{ t('addSite') }</span>
+ <div className="settings-page__content-description">
+ { t('addSiteDescription') }
+ </div>
+ </div>
+ <div className="settings-page__content-item">
+ <div className="settings-page__content-item-col">
+ <TextField
+ type="text"
+ value={this.state.input}
+ onChange={e => this.setState({ input: e.target.value })}
+ fullWidth
+ margin="dense"
+ min={0}
+ />
+ <button
+ className="button btn-primary settings-tab__rpc-save-button"
+ onClick={this.handleAddOrigin}
+ >
+ { t('connect') }
+ </button>
+ </div>
+ </div>
+ </div>
+ )
+ }
+
+ renderApprovedOriginsList () {
+ const { t } = this.context
+ const { approvedOrigins, rejectProviderRequestByOrigin, showClearApprovalModal } = this.props
+ const approvedEntries = Object.entries(approvedOrigins)
+ const approvalListEmpty = approvedEntries.length === 0
+
+ return (
+ <div className="settings-page__content-row">
+ <div className="settings-page__content-item">
+ <span>{ t('connected') }</span>
+ <span className="settings-page__content-description">
+ { t('connectedDescription') }
+ </span>
+ </div>
+ <div className="settings-page__content-item">
+ {
+ approvalListEmpty
+ ? <div><i className="fa fa-ban" /></div>
+ : null
+ }
+ {
+ approvedEntries.map(([origin, { siteTitle, siteImage }]) => (
+ <ConnectedSiteEntry
+ key={origin}
+ origin={origin}
+ siteTitle={siteTitle}
+ siteImage={siteImage}
+ onDelete={() => {
+ rejectProviderRequestByOrigin(origin)
+ }}
+ />
+ ))
+ }
+ </div>
+ <div className="settings-page__content-item-col">
+ <Button
+ disabled={approvalListEmpty}
+ type="warning"
+ large
+ className="settings-tab__button--orange"
+ onClick={event => {
+ event.preventDefault()
+ showClearApprovalModal()
+ }}
+ >
+ { t('clearApprovalData') }
+ </Button>
+ </div>
+ </div>
+ )
+ }
+
+ render () {
+ return (
+ <div className="settings-page__body">
+ { this.renderNewOriginInput() }
+ { this.renderApprovedOriginsList() }
+ </div>
+ )
+ }
+}
diff --git a/ui/app/pages/settings/connections-tab/connections-tab.container.js b/ui/app/pages/settings/connections-tab/connections-tab.container.js
new file mode 100644
index 000000000..cf3efc2b4
--- /dev/null
+++ b/ui/app/pages/settings/connections-tab/connections-tab.container.js
@@ -0,0 +1,39 @@
+import ConnectionsTab from './connections-tab.component'
+import { compose } from 'recompose'
+import { connect } from 'react-redux'
+import { withRouter } from 'react-router-dom'
+import {
+ approveProviderRequestByOrigin,
+ rejectProviderRequestByOrigin,
+ showModal,
+} from '../../../store/actions'
+
+export const mapStateToProps = state => {
+ const {
+ activeTab,
+ metamask,
+ } = state
+ const {
+ approvedOrigins,
+ } = metamask
+
+ return {
+ activeTab,
+ approvedOrigins,
+ }
+}
+
+export const mapDispatchToProps = dispatch => {
+ return {
+ approveProviderRequestByOrigin: (origin) => dispatch(approveProviderRequestByOrigin(origin)),
+ rejectProviderRequestByOrigin: (origin) => dispatch(rejectProviderRequestByOrigin(origin)),
+ showClearApprovalModal: () => dispatch(showModal({
+ name: 'CLEAR_APPROVED_ORIGINS',
+ })),
+ }
+}
+
+export default compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(ConnectionsTab)
diff --git a/ui/app/pages/settings/connections-tab/index.js b/ui/app/pages/settings/connections-tab/index.js
new file mode 100644
index 000000000..b04f4e33a
--- /dev/null
+++ b/ui/app/pages/settings/connections-tab/index.js
@@ -0,0 +1 @@
+export { default } from './connections-tab.container'
diff --git a/ui/app/pages/settings/connections-tab/index.scss b/ui/app/pages/settings/connections-tab/index.scss
new file mode 100644
index 000000000..249a7193f
--- /dev/null
+++ b/ui/app/pages/settings/connections-tab/index.scss
@@ -0,0 +1 @@
+@import './connected-site-row/index';
diff --git a/ui/app/pages/settings/index.scss b/ui/app/pages/settings/index.scss
index 73f36806d..4cbe5632e 100644
--- a/ui/app/pages/settings/index.scss
+++ b/ui/app/pages/settings/index.scss
@@ -4,6 +4,8 @@
@import 'settings-tab/index';
+@import 'connections-tab/index';
+
@import 'contact-list-tab/index';
.settings-page {
diff --git a/ui/app/pages/settings/security-tab/security-tab.component.js b/ui/app/pages/settings/security-tab/security-tab.component.js
index 0d367abfb..600ec7069 100644
--- a/ui/app/pages/settings/security-tab/security-tab.component.js
+++ b/ui/app/pages/settings/security-tab/security-tab.component.js
@@ -1,6 +1,5 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
-import { exportAsFile } from '../../../helpers/utils/util'
import ToggleButton from '../../../components/ui/toggle-button'
import { REVEAL_SEED_ROUTE } from '../../../helpers/constants/routes'
import Button from '../../../components/ui/button'
@@ -12,11 +11,8 @@ export default class SecurityTab extends PureComponent {
}
static propTypes = {
- setPrivacyMode: PropTypes.func,
- privacyMode: PropTypes.bool,
displayWarning: PropTypes.func,
revealSeedConfirmation: PropTypes.func,
- showClearApprovalModal: PropTypes.func,
warning: PropTypes.string,
history: PropTypes.object,
mobileSync: PropTypes.bool,
@@ -24,71 +20,6 @@ export default class SecurityTab extends PureComponent {
setParticipateInMetaMetrics: PropTypes.func,
}
- renderStateLogs () {
- const { t } = this.context
- const { displayWarning } = this.props
-
- return (
- <div className="settings-page__content-row">
- <div className="settings-page__content-item">
- <span>{ t('stateLogs') }</span>
- <span className="settings-page__content-description">
- { t('stateLogsDescription') }
- </span>
- </div>
- <div className="settings-page__content-item">
- <div className="settings-page__content-item-col">
- <Button
- type="secondary"
- large
- onClick={() => {
- window.logStateString((err, result) => {
- if (err) {
- displayWarning(t('stateLogError'))
- } else {
- exportAsFile('MetaMask State Logs.json', result)
- }
- })
- }}
- >
- { t('downloadStateLogs') }
- </Button>
- </div>
- </div>
- </div>
- )
- }
-
- renderClearApproval () {
- const { t } = this.context
- const { showClearApprovalModal } = this.props
- return (
- <div className="settings-page__content-row">
- <div className="settings-page__content-item">
- <span>{ t('approvalData') }</span>
- <span className="settings-page__content-description">
- { t('approvalDataDescription') }
- </span>
- </div>
- <div className="settings-page__content-item">
- <div className="settings-page__content-item-col">
- <Button
- type="warning"
- large
- className="settings-tab__button--orange"
- onClick={event => {
- event.preventDefault()
- showClearApprovalModal()
- }}
- >
- { t('clearApprovalData') }
- </Button>
- </div>
- </div>
- </div>
- )
- }
-
renderSeedWords () {
const { t } = this.context
const { history } = this.props
@@ -123,32 +54,6 @@ export default class SecurityTab 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)}
- offLabel={t('off')}
- onLabel={t('on')}
- />
- </div>
- </div>
- </div>
- )
- }
-
renderMetaMetricsOptIn () {
const { t } = this.context
const { participateInMetaMetrics, setParticipateInMetaMetrics } = this.props
@@ -181,8 +86,6 @@ export default class SecurityTab extends PureComponent {
return (
<div className="settings-page__body">
{ warning && <div className="settings-tab__error">{ warning }</div> }
- { this.renderPrivacyOptIn() }
- { this.renderClearApproval() }
{ this.renderSeedWords() }
{ this.renderMetaMetricsOptIn() }
</div>
diff --git a/ui/app/pages/settings/security-tab/security-tab.container.js b/ui/app/pages/settings/security-tab/security-tab.container.js
index 6036f4eda..fe79d86af 100644
--- a/ui/app/pages/settings/security-tab/security-tab.container.js
+++ b/ui/app/pages/settings/security-tab/security-tab.container.js
@@ -5,23 +5,17 @@ import { withRouter } from 'react-router-dom'
import {
displayWarning,
revealSeedConfirmation,
- setFeatureFlag,
- showModal,
setParticipateInMetaMetrics,
} from '../../../store/actions'
const mapStateToProps = state => {
const { appState: { warning }, metamask } = state
const {
- featureFlags: {
- privacyMode,
- } = {},
participateInMetaMetrics,
} = metamask
return {
warning,
- privacyMode,
participateInMetaMetrics,
}
}
@@ -30,8 +24,6 @@ const mapDispatchToProps = dispatch => {
return {
displayWarning: warning => dispatch(displayWarning(warning)),
revealSeedConfirmation: () => dispatch(revealSeedConfirmation()),
- setPrivacyMode: enabled => dispatch(setFeatureFlag('privacyMode', enabled)),
- showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })),
setParticipateInMetaMetrics: (val) => dispatch(setParticipateInMetaMetrics(val)),
}
}
diff --git a/ui/app/pages/settings/settings.component.js b/ui/app/pages/settings/settings.component.js
index 55fbebfac..781b38eff 100644
--- a/ui/app/pages/settings/settings.component.js
+++ b/ui/app/pages/settings/settings.component.js
@@ -4,6 +4,7 @@ import { Switch, Route, matchPath, withRouter } from 'react-router-dom'
import TabBar from '../../components/app/tab-bar'
import c from 'classnames'
import SettingsTab from './settings-tab'
+import ConnectionsTab from './connections-tab'
import NetworksTab from './networks-tab'
import AdvancedTab from './advanced-tab'
import InfoTab from './info-tab'
@@ -14,6 +15,7 @@ import {
ADVANCED_ROUTE,
SECURITY_ROUTE,
GENERAL_ROUTE,
+ CONNECTIONS_ROUTE,
ABOUT_US_ROUTE,
SETTINGS_ROUTE,
NETWORKS_ROUTE,
@@ -148,6 +150,7 @@ class SettingsPage extends PureComponent {
<TabBar
tabs={[
{ content: t('general'), description: t('generalSettingsDescription'), key: GENERAL_ROUTE },
+ { content: t('connections'), description: t('connectionsSettingsDescription'), key: CONNECTIONS_ROUTE },
{ content: t('advanced'), description: t('advancedSettingsDescription'), key: ADVANCED_ROUTE },
{ content: t('contacts'), description: t('contactsSettingsDescription'), key: CONTACT_LIST_ROUTE },
{ content: t('securityAndPrivacy'), description: t('securitySettingsDescription'), key: SECURITY_ROUTE },
@@ -175,6 +178,11 @@ class SettingsPage extends PureComponent {
/>
<Route
exact
+ path={CONNECTIONS_ROUTE}
+ component={ConnectionsTab}
+ />
+ <Route
+ exact
path={ABOUT_US_ROUTE}
component={InfoTab}
/>
diff --git a/ui/app/pages/settings/settings.container.js b/ui/app/pages/settings/settings.container.js
index 25c6c0994..8fe6614df 100644
--- a/ui/app/pages/settings/settings.container.js
+++ b/ui/app/pages/settings/settings.container.js
@@ -8,6 +8,7 @@ import { ENVIRONMENT_TYPE_POPUP } from '../../../../app/scripts/lib/enums'
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
import {
+ CONNECTIONS_ROUTE,
ADVANCED_ROUTE,
SECURITY_ROUTE,
GENERAL_ROUTE,
@@ -24,6 +25,7 @@ import {
const ROUTES_TO_I18N_KEYS = {
[GENERAL_ROUTE]: 'general',
+ [CONNECTIONS_ROUTE]: 'connections',
[ADVANCED_ROUTE]: 'advanced',
[SECURITY_ROUTE]: 'securityAndPrivacy',
[ABOUT_US_ROUTE]: 'about',
diff --git a/ui/app/store/actions.js b/ui/app/store/actions.js
index c4fea1fd8..cbf8e6283 100644
--- a/ui/app/store/actions.js
+++ b/ui/app/store/actions.js
@@ -349,7 +349,6 @@ var actions = {
approveProviderRequestByOrigin,
rejectProviderRequestByOrigin,
- forceApproveProviderRequestByOrigin,
clearApprovedOrigins,
setFirstTimeFlowType,
@@ -2650,12 +2649,6 @@ function approveProviderRequestByOrigin (origin) {
}
}
-function forceApproveProviderRequestByOrigin (origin) {
- return () => {
- background.forceApproveProviderRequestByOrigin(origin)
- }
-}
-
function rejectProviderRequestByOrigin (origin) {
return () => {
background.rejectProviderRequestByOrigin(origin)