From 247659ca651b683baba4eff2fbb49a9cb89d4503 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Thu, 15 Aug 2019 18:37:18 -0230 Subject: 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 --- app/_locales/cs/messages.json | 12 - app/_locales/de/messages.json | 12 - app/_locales/en/messages.json | 47 ++-- app/_locales/es/messages.json | 12 - app/_locales/fr/messages.json | 12 - app/_locales/hn/messages.json | 12 - app/_locales/ht/messages.json | 12 - app/_locales/it/messages.json | 12 - app/_locales/ja/messages.json | 12 - app/_locales/ko/messages.json | 12 - app/_locales/nl/messages.json | 12 - app/_locales/ph/messages.json | 12 - app/_locales/pt/messages.json | 12 - app/_locales/ru/messages.json | 12 - app/_locales/sk/messages.json | 12 - app/_locales/sl/messages.json | 12 - app/_locales/th/messages.json | 12 - app/_locales/tml/messages.json | 12 - app/_locales/tr/messages.json | 12 - app/_locales/vi/messages.json | 12 - app/_locales/zh_CN/messages.json | 12 - app/_locales/zh_TW/messages.json | 12 - app/scripts/background.js | 4 +- app/scripts/controllers/preferences.js | 1 - app/scripts/controllers/provider-approval.js | 98 +++----- app/scripts/metamask-controller.js | 7 +- app/scripts/migrations/036.js | 30 +++ app/scripts/migrations/index.js | 1 + .../unit/app/controllers/provider-approval-test.js | 260 +++++++++++++++++++++ test/unit/migrations/036-test.js | 119 ++++++++++ ui/app/helpers/constants/routes.js | 3 +- ui/app/pages/home/home.component.js | 29 --- ui/app/pages/home/home.container.js | 22 +- .../connected-site-row.component.js | 31 +++ .../connections-tab/connected-site-row/index.js | 1 + .../connections-tab/connected-site-row/index.scss | 14 ++ .../connections-tab/connections-tab.component.js | 133 +++++++++++ .../connections-tab/connections-tab.container.js | 39 ++++ ui/app/pages/settings/connections-tab/index.js | 1 + ui/app/pages/settings/connections-tab/index.scss | 1 + ui/app/pages/settings/index.scss | 2 + .../security-tab/security-tab.component.js | 97 -------- .../security-tab/security-tab.container.js | 8 - ui/app/pages/settings/settings.component.js | 8 + ui/app/pages/settings/settings.container.js | 2 + ui/app/store/actions.js | 7 - 46 files changed, 711 insertions(+), 506 deletions(-) create mode 100644 app/scripts/migrations/036.js create mode 100644 test/unit/app/controllers/provider-approval-test.js create mode 100644 test/unit/migrations/036-test.js create mode 100644 ui/app/pages/settings/connections-tab/connected-site-row/connected-site-row.component.js create mode 100644 ui/app/pages/settings/connections-tab/connected-site-row/index.js create mode 100644 ui/app/pages/settings/connections-tab/connected-site-row/index.scss create mode 100644 ui/app/pages/settings/connections-tab/connections-tab.component.js create mode 100644 ui/app/pages/settings/connections-tab/connections-tab.container.js create mode 100644 ui/app/pages/settings/connections-tab/index.js create mode 100644 ui/app/pages/settings/connections-tab/index.scss 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 @@ -6,27 +6,23 @@ const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware * A controller that services user-approved requests for a full Ethereum provider API */ 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 @@ -120,20 +105,6 @@ export default class Home extends PureComponent { key="home-privacyModeDefault" />, }, - { - shouldBeRendered: viewingUnconnectedDapp, - component: { - forceApproveProviderRequestByOrigin(activeTab.origin) - }} - ignoreText={t('dismiss')} - onIgnore={() => rejectProviderRequestByOrigin(activeTab.origin)} - infoText={t('shareAddressInfo', [activeTab.origin])} - key="home-shareAddressToConnect" - />, - }, { shouldBeRendered: shouldShowSeedPhraseReminder, component: { - 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 ( +
+
{origin}
+
+
+ ) + } +} 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 ( +
+
+ { t('addSite') } +
+ { t('addSiteDescription') } +
+
+
+
+ this.setState({ input: e.target.value })} + fullWidth + margin="dense" + min={0} + /> + +
+
+
+ ) + } + + renderApprovedOriginsList () { + const { t } = this.context + const { approvedOrigins, rejectProviderRequestByOrigin, showClearApprovalModal } = this.props + const approvedEntries = Object.entries(approvedOrigins) + const approvalListEmpty = approvedEntries.length === 0 + + return ( +
+
+ { t('connected') } + + { t('connectedDescription') } + +
+
+ { + approvalListEmpty + ?
+ : null + } + { + approvedEntries.map(([origin, { siteTitle, siteImage }]) => ( + { + rejectProviderRequestByOrigin(origin) + }} + /> + )) + } +
+
+ +
+
+ ) + } + + render () { + return ( +
+ { this.renderNewOriginInput() } + { this.renderApprovedOriginsList() } +
+ ) + } +} 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 ( -
-
- { t('stateLogs') } - - { t('stateLogsDescription') } - -
-
-
- -
-
-
- ) - } - - renderClearApproval () { - const { t } = this.context - const { showClearApprovalModal } = this.props - return ( -
-
- { t('approvalData') } - - { t('approvalDataDescription') } - -
-
-
- -
-
-
- ) - } - 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 ( -
-
- { t('privacyMode') } -
- { t('privacyModeDescription') } -
-
-
-
- setPrivacyMode(!value)} - offLabel={t('off')} - onLabel={t('on')} - /> -
-
-
- ) - } - renderMetaMetricsOptIn () { const { t } = this.context const { participateInMetaMetrics, setParticipateInMetaMetrics } = this.props @@ -181,8 +86,6 @@ export default class SecurityTab extends PureComponent { return (
{ warning &&
{ warning }
} - { this.renderPrivacyOptIn() } - { this.renderClearApproval() } { this.renderSeedWords() } { this.renderMetaMetricsOptIn() }
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 { + { - background.forceApproveProviderRequestByOrigin(origin) - } -} - function rejectProviderRequestByOrigin (origin) { return () => { background.rejectProviderRequestByOrigin(origin) -- cgit