1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Connect Screen Multi Select (#8078)

* Add UI for selecting multiple accounts on the first permissions connect screen

* Make accounts list scrollable on connect screen

* Change title wording on connect screen to 'select your accounts'

* Add select all tooltip to info circle on top of connect screen account list

* Add security info footer to the first screen of the connect flow

* Apply redesigns to page 2 of connect flow

* Display number of accounts on connect flow second screen if there are multiple to connect

* Update e2e tests for connect screen multi-select changes

* Remove unused chooseAnAcount message

* Fix styling/display of redirect elements on second page of connect flow

* Assorted small fixes in permissions connect

* Remove unnecessary tiny delays in spec files

* Remove incorrect use of bem modified in choose-account

* Remove unused locale

* Use Set for managing selected accounts in choose-acount and permissions-connect componets

* Compone!

* Move connect flow header into a reusable component, and implement new header designs

* Update locales and add missing locales

* Improve permission list item design (second screen of connect flow)

* Check box component improvements

* Fixes in variables.scss

* Simplfy code in selectAll of choose-account.component

* Hide checkboxes on first pages on connect flow when there is only one account

* Allow autofill of default new account modal text with right arrow

* Disable next button on first screen of connect flow when no accounts selected

* Improve choose-account/index.scss

* Remove metamask secure graphic

* Fix connect flow redirect screen

* Fix connectToMultiple locale

* Remove locales no longer used after connect flow multiple connect updates

* Fix size of dapp icon on redirect screen of connect flow

* Clean up choose-account code

* Stop using placeholder in new-account-modal

* Remove unused styles in permission-page-container/index.scss

* Pass origin instead of site name to PermissionsConnectHeader in connect flow

* Make iconName a required prop in permissions-connect-header

* Show checkbox in cases where there is one account in the choose-account list

* Do not render select all checkbox when only 1 list item, instead of just hiding it

* Small cleanup in choose-account/index.scss
This commit is contained in:
Dan J Miller 2020-04-02 06:39:53 -02:30 committed by GitHub
parent 4efa6caec7
commit d8179ff030
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 635 additions and 521 deletions

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "አይቀበሉ" "message": "አይቀበሉ"
}, },
"likeToConnect": {
"message": "$1ከመለያዎ ጋር ለመገናኘት ይፈልጋል"
},
"about": { "about": {
"message": "ስለ" "message": "ስለ"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "رفض" "message": "رفض"
}, },
"likeToConnect": {
"message": "يرغب $1 في الاتصال بحسابك"
},
"about": { "about": {
"message": "حول" "message": "حول"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Отхвърляне" "message": "Отхвърляне"
}, },
"likeToConnect": {
"message": "$1 би искал да се свърже с вашия акаунт"
},
"about": { "about": {
"message": "Информация" "message": "Информация"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "প্রত্যাখ্যান" "message": "প্রত্যাখ্যান"
}, },
"likeToConnect": {
"message": "$1 আপনার অ্যাকাউন্টের সাথে সংযোগ করতে চায়"
},
"about": { "about": {
"message": "সম্পর্কে" "message": "সম্পর্কে"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Rebutja" "message": "Rebutja"
}, },
"likeToConnect": {
"message": "a $1 li agradaria connectar-se al teu compte"
},
"about": { "about": {
"message": "Informació" "message": "Informació"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Afvis" "message": "Afvis"
}, },
"likeToConnect": {
"message": "$1 ønsker at forbinde til din konto"
},
"about": { "about": {
"message": "Om" "message": "Om"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Ablehnen" "message": "Ablehnen"
}, },
"likeToConnect": {
"message": "$1 möchte sich mit deinem Account verbinden"
},
"about": { "about": {
"message": "Über" "message": "Über"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Απόρριψη" "message": "Απόρριψη"
}, },
"likeToConnect": {
"message": "Αίτημα σύνδεσης στον λογαριασμό σας από $1"
},
"about": { "about": {
"message": "Σχετικά με" "message": "Σχετικά με"
}, },

View File

@ -32,17 +32,31 @@
"showIncomingTransactionsDescription": { "showIncomingTransactionsDescription": {
"message": "Select this to use Etherscan to show incoming transactions in the transactions list" "message": "Select this to use Etherscan to show incoming transactions in the transactions list"
}, },
"cancelling": {
"message": "Cancelling..."
},
"cancelledConnectionWithMetaMask": { "cancelledConnectionWithMetaMask": {
"message": "Cancelled Connection With MetaMask" "message": "Cancelled Connection With MetaMask"
}, },
"chartOnlyAvailableEth": { "chartOnlyAvailableEth": {
"message": "Chart only available on Ethereum networks." "message": "Chart only available on Ethereum networks."
}, },
"connecting": {
"message": "Connecting..."
},
"connectWithMetaMask": {
"message": "Connect With MetaMask"
},
"connectingWithMetaMask": { "connectingWithMetaMask": {
"message": "Connecting With MetaMask..." "message": "Connecting With MetaMask..."
}, },
"chooseAnAcount": { "connectTo": {
"message": "Choose an account" "message": "Connect to $1",
"description": "$1 is the name/origin of a site/dapp that the user can connect to metamask"
},
"connectToMultiple": {
"message": "Connect to $1 accounts",
"description": "$1 is the number of accounts to connect to"
}, },
"contractInteraction": { "contractInteraction": {
"message": "Contract Interaction" "message": "Contract Interaction"
@ -50,9 +64,6 @@
"reject": { "reject": {
"message": "Reject" "message": "Reject"
}, },
"redirectingBackToDapp": {
"message": "Redirecting back to dapp..."
},
"about": { "about": {
"message": "About" "message": "About"
}, },
@ -266,6 +277,9 @@
"chainId": { "chainId": {
"message": "Chain ID" "message": "Chain ID"
}, },
"chooseAccountsToUse": {
"message": "Choose the account(s) to use on this site"
},
"clickToRevealSeed": { "clickToRevealSeed": {
"message": "Click here to reveal secret words" "message": "Click here to reveal secret words"
}, },
@ -290,6 +304,9 @@
"congratulations": { "congratulations": {
"message": "Congratulations" "message": "Congratulations"
}, },
"connectAccountOrCreate": {
"message": "Connect account or create new"
},
"connectHardwareWallet": { "connectHardwareWallet": {
"message": "Connect Hardware Wallet" "message": "Connect Hardware Wallet"
}, },
@ -788,9 +805,6 @@
"learnMore": { "learnMore": {
"message": "Learn more" "message": "Learn more"
}, },
"learnAboutRisks": {
"message": "Learn about the risks here."
},
"ledgerAccountRestriction": { "ledgerAccountRestriction": {
"message": "You need to make use your last account before you can add a new one." "message": "You need to make use your last account before you can add a new one."
}, },
@ -800,10 +814,6 @@
"likeToAddTokens": { "likeToAddTokens": {
"message": "Would you like to add these tokens?" "message": "Would you like to add these tokens?"
}, },
"likeToConnect": {
"message": "$1 would like to connect to your MetaMask account",
"description": "$1 is the name/url of a site/dapp asking to connect to MetaMask"
},
"links": { "links": {
"message": "Links" "message": "Links"
}, },
@ -1006,6 +1016,12 @@
"pending": { "pending": {
"message": "pending" "message": "pending"
}, },
"permissionCheckedIconDescription": {
"message": "You have approved this permission"
},
"permissionUncheckedIconDescription": {
"message": "You have not approved this permission"
},
"personalAddressDetected": { "personalAddressDetected": {
"message": "Personal address detected. Input the token contract address." "message": "Personal address detected. Input the token contract address."
}, },
@ -1185,6 +1201,12 @@
"seedPhraseReq": { "seedPhraseReq": {
"message": "Seed phrases contain 12, 15, 18, 21, or 24 words" "message": "Seed phrases contain 12, 15, 18, 21, or 24 words"
}, },
"selectingAllWillAllow": {
"message": "Selecting all will allow this site to view all of your current accounts. Make sure you trust this site."
},
"selectAll": {
"message": "Select all"
},
"selectCurrency": { "selectCurrency": {
"message": "Select Currency" "message": "Select Currency"
}, },
@ -1422,10 +1444,6 @@
"toWithColon": { "toWithColon": {
"message": "To:" "message": "To:"
}, },
"toConnectWith": {
"message": "To connect with $1",
"description": "$1 is the name or domain of a site/dapp that asking to connect with MetaMask"
},
"toETHviaShapeShift": { "toETHviaShapeShift": {
"message": "$1 to ETH via ShapeShift", "message": "$1 to ETH via ShapeShift",
"description": "system will fill in deposit type in start of message" "description": "system will fill in deposit type in start of message"

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Rechazar" "message": "Rechazar"
}, },
"likeToConnect": {
"message": "$1 quisiera conectar con tu cuenta"
},
"about": { "about": {
"message": "Acerca" "message": "Acerca"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Rechazar" "message": "Rechazar"
}, },
"likeToConnect": {
"message": "$1 desea conectarse a tu cuenta"
},
"about": { "about": {
"message": "Acerca de" "message": "Acerca de"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Lükka tagasi" "message": "Lükka tagasi"
}, },
"likeToConnect": {
"message": "$1 soovib teie kontoga ühenduse luua"
},
"about": { "about": {
"message": "Teave" "message": "Teave"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "عدم پذیرش" "message": "عدم پذیرش"
}, },
"likeToConnect": {
"message": "1$1 میخواهید تا با حساب تان وصل شوید"
},
"about": { "about": {
"message": "درباره" "message": "درباره"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Hylkää" "message": "Hylkää"
}, },
"likeToConnect": {
"message": "$1 haluaisi yhdistää tiliisi"
},
"about": { "about": {
"message": "Tietoja asetuksista" "message": "Tietoja asetuksista"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Tanggihan" "message": "Tanggihan"
}, },
"likeToConnect": {
"message": "Gusto ng $1 na kumonekta sa iyong account"
},
"about": { "about": {
"message": "Tungkol sa" "message": "Tungkol sa"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Rejeter" "message": "Rejeter"
}, },
"likeToConnect": {
"message": "$1 voudrait se connecter à votre compte"
},
"about": { "about": {
"message": "À propos" "message": "À propos"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "דחה" "message": "דחה"
}, },
"likeToConnect": {
"message": "$1 מבקש להתחבר לחשבון שלך"
},
"about": { "about": {
"message": "מידע כללי" "message": "מידע כללי"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "अस्‍वीकार करें" "message": "अस्‍वीकार करें"
}, },
"likeToConnect": {
"message": "$1 आपके खाते से कनेक्ट होता चाहता हैं"
},
"about": { "about": {
"message": "इसके बारे में" "message": "इसके बारे में"
}, },

View File

@ -12,9 +12,6 @@
"reject": { "reject": {
"message": "Odbaci" "message": "Odbaci"
}, },
"likeToConnect": {
"message": "Korisnik $1 želi se povezati na vaš račun"
},
"about": { "about": {
"message": "O opcijama" "message": "O opcijama"
}, },

View File

@ -12,9 +12,6 @@
"reject": { "reject": {
"message": "Elutasítás" "message": "Elutasítás"
}, },
"likeToConnect": {
"message": "$1 szeretne kapcsolódni az ön fiókjához"
},
"about": { "about": {
"message": "Névjegy" "message": "Névjegy"
}, },

View File

@ -12,9 +12,6 @@
"reject": { "reject": {
"message": "Tolak" "message": "Tolak"
}, },
"likeToConnect": {
"message": "$1 ingin menghubungkan ke akun Anda"
},
"about": { "about": {
"message": "Tentang" "message": "Tentang"
}, },

View File

@ -26,8 +26,9 @@
"connectingWithMetaMask": { "connectingWithMetaMask": {
"message": "Connettendo con MetaMask..." "message": "Connettendo con MetaMask..."
}, },
"chooseAnAcount": { "connectTo": {
"message": "Scegli un account" "message": "Collegati a $1",
"description": "$1 is the name/origin of a site/dapp that the user can connect to metamask"
}, },
"contractInteraction": { "contractInteraction": {
"message": "Interazione Contratto" "message": "Interazione Contratto"
@ -35,9 +36,6 @@
"reject": { "reject": {
"message": "Annulla" "message": "Annulla"
}, },
"redirectingBackToDapp": {
"message": "Ritornando alla dapp..."
},
"about": { "about": {
"message": "Informazioni" "message": "Informazioni"
}, },
@ -773,9 +771,6 @@
"learnMore": { "learnMore": {
"message": "Scopri di più" "message": "Scopri di più"
}, },
"learnAboutRisks": {
"message": "Scopri di piu sui rischi qua."
},
"ledgerAccountRestriction": { "ledgerAccountRestriction": {
"message": "E' necessario utilizzare l'ultimo account prima di poterne aggiungere uno nuovo." "message": "E' necessario utilizzare l'ultimo account prima di poterne aggiungere uno nuovo."
}, },
@ -785,10 +780,6 @@
"likeToAddTokens": { "likeToAddTokens": {
"message": "Vorresti aggiungere questi token?" "message": "Vorresti aggiungere questi token?"
}, },
"likeToConnect": {
"message": "$1 vorrebbe connettersi al tuo account",
"description": "$1 is the name/url of a site/dapp asking to connect to MetaMask"
},
"links": { "links": {
"message": "Collegamenti" "message": "Collegamenti"
}, },
@ -1401,10 +1392,6 @@
"toWithColon": { "toWithColon": {
"message": "To:" "message": "To:"
}, },
"toConnectWith": {
"message": "Di connettersi con $1",
"description": "$1 is the name or domain of a site/dapp that asking to connect with MetaMask"
},
"toETHviaShapeShift": { "toETHviaShapeShift": {
"message": "$1 a ETH con ShapeShift", "message": "$1 a ETH con ShapeShift",
"description": "system will fill in deposit type in start of message" "description": "system will fill in deposit type in start of message"

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "拒否" "message": "拒否"
}, },
"likeToConnect": {
"message": "$1 はあなたのアカウントにアクセスしようとしています。"
},
"aboutSettingsDescription": { "aboutSettingsDescription": {
"message": "バージョンやサポート、問合せ先など" "message": "バージョンやサポート、問合せ先など"
}, },

View File

@ -12,9 +12,6 @@
"reject": { "reject": {
"message": "ತಿರಸ್ಕರಿಸಿ" "message": "ತಿರಸ್ಕರಿಸಿ"
}, },
"likeToConnect": {
"message": "$1 ನಿಮ್ಮ ಖಾತೆಗೆ ಸಂಪರ್ಕಿಸಲು ಬಯಸುತ್ತಿದೆ"
},
"about": { "about": {
"message": "ಕುರಿತು" "message": "ಕುರಿತು"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "거부" "message": "거부"
}, },
"likeToConnect": {
"message": "$1이 당신의 계정에 연결하길 원합니다."
},
"about": { "about": {
"message": "정보" "message": "정보"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Atmesti" "message": "Atmesti"
}, },
"likeToConnect": {
"message": "$1 norėtų prisijungti prie jūsų paskyros"
},
"about": { "about": {
"message": "Apie" "message": "Apie"
}, },

View File

@ -12,9 +12,6 @@
"reject": { "reject": {
"message": "Noraidīt" "message": "Noraidīt"
}, },
"likeToConnect": {
"message": "$1 vēlas izveidot savienojumu ar jūsu kontu"
},
"about": { "about": {
"message": "Par" "message": "Par"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Tolak" "message": "Tolak"
}, },
"likeToConnect": {
"message": "$1 ingin menyambung kepada akaun anda"
},
"about": { "about": {
"message": "Mengenai" "message": "Mengenai"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Avslå" "message": "Avslå"
}, },
"likeToConnect": {
"message": "$1 ønsker å forbindes med kontoen din "
},
"about": { "about": {
"message": "Info" "message": "Info"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Odrzuć" "message": "Odrzuć"
}, },
"likeToConnect": {
"message": "$1 chce połączyć się z Twoim kontem"
},
"about": { "about": {
"message": "Informacje" "message": "Informacje"
}, },

View File

@ -12,9 +12,6 @@
"reject": { "reject": {
"message": "Rejeitar" "message": "Rejeitar"
}, },
"likeToConnect": {
"message": "$1 gostaria de se conectar à sua conta"
},
"about": { "about": {
"message": "Sobre" "message": "Sobre"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Respingeți" "message": "Respingeți"
}, },
"likeToConnect": {
"message": "$1 ar dori să se conecteze la contul dvs."
},
"about": { "about": {
"message": "Despre" "message": "Despre"
}, },

View File

@ -527,9 +527,6 @@
"contractInteraction": { "contractInteraction": {
"message": "Взаимодействие с контрактом" "message": "Взаимодействие с контрактом"
}, },
"likeToConnect": {
"message": "$1 запрашивает доступ к вашему аккаунту"
},
"about": { "about": {
"message": "О нас" "message": "О нас"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Odmítnout" "message": "Odmítnout"
}, },
"likeToConnect": {
"message": "$1 sa chce pripojiť k vášmu účtu"
},
"about": { "about": {
"message": "Informácie" "message": "Informácie"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Zavrni" "message": "Zavrni"
}, },
"likeToConnect": {
"message": "$1 se želi povezati z vašim računom."
},
"about": { "about": {
"message": "O možnostih" "message": "O možnostih"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Одбиј" "message": "Одбиј"
}, },
"likeToConnect": {
"message": "$1 bi hteo da se poveže sa vašim nalogom"
},
"about": { "about": {
"message": "Основни подаци" "message": "Основни подаци"
}, },

View File

@ -12,9 +12,6 @@
"reject": { "reject": {
"message": "Avvisa" "message": "Avvisa"
}, },
"likeToConnect": {
"message": "$1 vill ansluta till ditt konto"
},
"about": { "about": {
"message": "Om" "message": "Om"
}, },

View File

@ -12,9 +12,6 @@
"reject": { "reject": {
"message": "Kataa" "message": "Kataa"
}, },
"likeToConnect": {
"message": "$1 ingependa kuunganishwa kwenye akaunti yako"
},
"about": { "about": {
"message": "Kuhusu" "message": "Kuhusu"
}, },

View File

@ -2,9 +2,6 @@
"reject": { "reject": {
"message": "ปฏิเสธ" "message": "ปฏิเสธ"
}, },
"likeToConnect": {
"message": "$1 ต้องการเชื่อมต่อกับบัญชีของคุณ"
},
"about": { "about": {
"message": "เกี่ยวกับ" "message": "เกี่ยวกับ"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "Відхилити" "message": "Відхилити"
}, },
"likeToConnect": {
"message": "$1 бажає підключитися до вашого облікового запису"
},
"about": { "about": {
"message": "Про Google Chrome" "message": "Про Google Chrome"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "拒绝" "message": "拒绝"
}, },
"likeToConnect": {
"message": "$1 希望关联您的账户"
},
"about": { "about": {
"message": "关于" "message": "关于"
}, },

View File

@ -8,9 +8,6 @@
"reject": { "reject": {
"message": "拒絕" "message": "拒絕"
}, },
"likeToConnect": {
"message": "$1 請求訪問帳戶權限"
},
"about": { "about": {
"message": "關於" "message": "關於"
}, },

View File

@ -1,3 +1,3 @@
<svg width="131" height="2" viewBox="0 0 131 2" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="131" height="2" viewBox="0 0 131 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 1H134" stroke="#CDD1E4" stroke-linejoin="round" stroke-dasharray="8 0"/> <path d="M0 1H134" stroke="#CDD1E4" stroke-linejoin="round" stroke-dasharray="8 7"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 188 B

View File

@ -127,6 +127,7 @@ describe('MetaMask', function () {
await driver.clickElement(By.css('.permissions-connect-choose-account__account')) await driver.clickElement(By.css('.permissions-connect-choose-account__account'))
await driver.clickElement(By.xpath(`//button[contains(text(), 'Next')]`))
await driver.clickElement(By.xpath(`//button[contains(text(), 'Submit')]`)) await driver.clickElement(By.xpath(`//button[contains(text(), 'Submit')]`))
await driver.waitUntilXWindowHandles(2) await driver.waitUntilXWindowHandles(2)

View File

@ -415,6 +415,7 @@ describe('MetaMask', function () {
await driver.clickElement(By.css('.permissions-connect-choose-account__account')) await driver.clickElement(By.css('.permissions-connect-choose-account__account'))
await driver.clickElement(By.xpath(`//button[contains(text(), 'Next')]`))
await driver.clickElement(By.xpath(`//button[contains(text(), 'Submit')]`)) await driver.clickElement(By.xpath(`//button[contains(text(), 'Submit')]`))
await driver.waitUntilXWindowHandles(2) await driver.waitUntilXWindowHandles(2)

View File

@ -125,6 +125,7 @@ describe('MetaMask', function () {
await driver.clickElement(By.css('.permissions-connect-choose-account__account')) await driver.clickElement(By.css('.permissions-connect-choose-account__account'))
await driver.clickElement(By.xpath(`//button[contains(text(), 'Next')]`))
await driver.clickElement(By.xpath(`//button[contains(text(), 'Submit')]`)) await driver.clickElement(By.xpath(`//button[contains(text(), 'Submit')]`))
await driver.waitUntilXWindowHandles(2) await driver.waitUntilXWindowHandles(2)

View File

@ -86,6 +86,7 @@ describe('MetaMask', function () {
await driver.clickElement(By.css('.permissions-connect-choose-account__account')) await driver.clickElement(By.css('.permissions-connect-choose-account__account'))
await driver.clickElement(By.xpath(`//button[contains(text(), 'Next')]`))
await driver.clickElement(By.xpath(`//button[contains(text(), 'Submit')]`)) await driver.clickElement(By.xpath(`//button[contains(text(), 'Submit')]`))
await driver.waitUntilXWindowHandles(2) await driver.waitUntilXWindowHandles(2)

View File

@ -95,3 +95,7 @@
@import './connected-status-indicator/index'; @import './connected-status-indicator/index';
@import './dropdowns/account-details-dropdown/index'; @import './dropdowns/account-details-dropdown/index';
@import '../ui/check-box/index';
@import 'permissions-connect-header/index';

View File

@ -15,7 +15,7 @@ export default class NewAccountModal extends Component {
} }
state = { state = {
alias: '', alias: this.context.t('newAccountNumberName', [this.props.newAccountNumber]),
} }
onChange = (e) => { onChange = (e) => {
@ -53,7 +53,6 @@ export default class NewAccountModal extends Component {
onChange={this.onChange} onChange={this.onChange}
onKeyPress={this.onKeyPress} onKeyPress={this.onKeyPress}
value={this.state.alias} value={this.state.alias}
placeholder={ t('account', [this.props.newAccountNumber]) }
autoFocus autoFocus
/> />
</div> </div>

View File

@ -18,8 +18,7 @@
@extend %header--18; @extend %header--18;
line-height: 25px; line-height: 25px;
text-align: center; text-align: center;
position: fixed; margin-top: 32px;
left: 0;
width: 100%; width: 100%;
} }
@ -31,214 +30,38 @@
color: #7C808E; color: #7C808E;
&--redirect { &--redirect {
margin-top: 60px; margin-top: 50px;
} width: 100%;
h1, h2 {
color: #4A4A4A;
display: flex; display: flex;
justify-content: center; align-items: center;
text-align: center; padding-top: 8px;
}
h2 {
font-size: 16px;
line-height: 18px;
padding: 20px;
}
h1 {
font-size: 22px;
line-height: 26px;
padding: 20px;
}
p {
padding: 0 40px;
text-align: center;
font-size: 12px;
line-height: 18px;
} }
a, a:hover { a, a:hover {
color: $dodger-blue; color: $dodger-blue;
} }
section {
h1 {
padding: 30px 0px 0px 0px;
}
h2 {
padding: 0px 0px 20px 0px;
}
}
&__requested { &__requested {
text-align: left; text-align: left;
} }
&__revoke-note { &__revoke-note {
margin-top: 24px; margin-top: 60px;
}
&__checkbox {
margin-right: 10px;
} }
&__permission { &__permission {
display: flex;
margin-top: 18px; margin-top: 18px;
i { i {
color: #6A737D; font-size: 1.4rem;
color: $Grey-200;
} }
label { label {
margin-left: 6px; margin-left: 6px;
color: #24292E; color: $Black-100;
} }
} }
.permission-approval-visual {
display: flex;
flex-direction: row;
justify-content: space-evenly;
position: relative;
margin: 0 32px;
margin-top: 40px;
section {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
h1 {
font-size: 14px;
line-height: 18px;
padding: 8px 0 0;
}
h2 {
font-size: 12px;
line-height: 17px;
color: #6A737D;
padding: 0;
}
&__check {
width: 40px;
height: 40px;
background: white url("/images/permissions-check.svg") no-repeat;
margin-top: 24px;
z-index: 1;
}
&__reject {
background: white;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
i {
color: #D73A49;
transform: scale(3);
}
}
&__broken-line {
z-index: 0;
position: absolute;
top: 43px;
}
&__identicon, .icon-with-fallback__identicon {
width: 32px;
height: 32px;
z-index: 1;
&--default {
background-color: #777A87;
color: white;
width: 64px;
height: 64px;
border-radius: 32px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
z-index: 1;
}
}
&__identicon-container, .icon-with-fallback__identicon-container {
padding: 1rem;
flex: 1;
position: relative;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
&__identicon-border, .icon-with-fallback__identicon-border {
height: 64px;
width: 64px;
border-radius: 50%;
border: 1px solid white;
background: #FFFFFF;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25);
}
&__identicon-border {
display: flex;
justify-content: center;
align-items: center;
}
.icon-with-fallback__identicon-border {
position: absolute;
}
&:before {
border-top: 2px dashed #CDD1E4;
content: "";
margin: 0 auto;
position: absolute;
top: 32px;
left: 0;
bottom: 0;
right: 0;
width: 65%;
z-index: -1;
}
&__account-info {
display: flex;
flex-direction: column;
align-items: center;
&__label {
@extend %content-text;
line-height: 20px;
color: #000000;
}
&__address {
@extend %font;
font-size: 12px;
line-height: 17px;
color: #6A737D;
}
}
}
.secure-badge {
display: flex;
justify-content: center;
padding: 25px;
}
} }
&__permissions-header { &__permissions-header {
@ -266,10 +89,6 @@
} }
} }
&__permissions-header-redirect {
text-align: center;
}
@media screen and (max-width: 575px) { @media screen and (max-width: 575px) {
width: 100%; width: 100%;
margin-top: 25px; margin-top: 25px;
@ -288,3 +107,106 @@
} }
} }
} }
.permission-result {
@extend %header--24;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
text-align: center;
color: $Black-100;
height: 114px;
&__icons {
display: flex;
}
&__center-icon {
display: flex;
position: relative;
justify-content: center;
align-items: center;
font-size: 12px;
}
h1 {
font-size: 14px;
line-height: 18px;
padding: 8px 0 0;
}
h2 {
font-size: 12px;
line-height: 17px;
color: #6A737D;
padding: 0;
}
&__check {
width: 40px;
height: 40px;
background: white url("/images/permissions-check.svg") no-repeat;
position: absolute;
}
&__reject {
position: absolute;
background: white;
display: flex;
justify-content: center;
align-items: center;
i {
color: #D73A49;
transform: scale(3);
}
}
&__identicon, .icon-with-fallback__identicon {
width: 32px;
height: 32px;
&--default {
background-color: #777A87;
color: white;
width: 64px;
height: 64px;
border-radius: 32px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
}
&__identicon-container, .icon-with-fallback__identicon-container {
height: auto;
position: relative;
display: flex;
justify-content: center;
align-items: center;
height: 64px;
width: 64px;
}
&__identicon-border, .icon-with-fallback__identicon-border {
height: 64px;
width: 64px;
border-radius: 50%;
border: 1px solid white;
background: #FFFFFF;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25);
}
&__identicon-border {
display: flex;
justify-content: center;
align-items: center;
}
.icon-with-fallback__identicon-border {
position: absolute;
}
}

View File

@ -2,17 +2,17 @@ import PropTypes from 'prop-types'
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import Identicon from '../../../ui/identicon' import Identicon from '../../../ui/identicon'
import IconWithFallBack from '../../../ui/icon-with-fallback' import IconWithFallBack from '../../../ui/icon-with-fallback'
import PermissionsConnectHeader from '../../permissions-connect-header'
import classnames from 'classnames' import classnames from 'classnames'
export default class PermissionPageContainerContent extends PureComponent { export default class PermissionPageContainerContent extends PureComponent {
static propTypes = { static propTypes = {
requestMetadata: PropTypes.object.isRequired,
domainMetadata: PropTypes.object.isRequired, domainMetadata: PropTypes.object.isRequired,
selectedPermissions: PropTypes.object.isRequired, selectedPermissions: PropTypes.object.isRequired,
permissionsDescriptions: PropTypes.object.isRequired, permissionsDescriptions: PropTypes.object.isRequired,
onPermissionToggle: PropTypes.func.isRequired, onPermissionToggle: PropTypes.func.isRequired,
selectedAccount: PropTypes.object, selectedIdentities: PropTypes.array,
redirect: PropTypes.bool, redirect: PropTypes.bool,
permissionRejected: PropTypes.bool, permissionRejected: PropTypes.bool,
} }
@ -20,55 +20,38 @@ export default class PermissionPageContainerContent extends PureComponent {
static defaultProps = { static defaultProps = {
redirect: null, redirect: null,
permissionRejected: null, permissionRejected: null,
selectedAccount: {}, selectedIdentities: [],
} }
static contextTypes = { static contextTypes = {
t: PropTypes.func, t: PropTypes.func,
} }
renderAccountInfo = (account) => { renderRedirect () {
const { t } = this.context
const { permissionRejected, selectedIdentities, domainMetadata } = this.props
return ( return (
<div className="permission-approval-visual__account-info"> <div className="permission-result">
<div className="permission-approval-visual__account-info__label"> { permissionRejected ? t('cancelling') : t('connecting') }
{ account.label } <div className="permission-result__icons">
</div>
<div className="permission-approval-visual__account-info__address">
{ account.truncatedAddress }
</div>
</div>
)
}
renderPermissionApprovalVisual = () => {
const {
requestMetadata, domainMetadata, selectedAccount, redirect, permissionRejected,
} = this.props
return (
<div className="permission-approval-visual">
<section>
<IconWithFallBack icon={domainMetadata.icon} name={domainMetadata.name} /> <IconWithFallBack icon={domainMetadata.icon} name={domainMetadata.name} />
{ redirect ? null : <h1>{domainMetadata.name}</h1> } <div className="permission-result__center-icon">
{ redirect ? null : <h2>{requestMetadata.origin}</h2> }
</section>
{ permissionRejected { permissionRejected
? <span className="permission-approval-visual__reject" ><i className="fa fa-times-circle" /></span> ? <span className="permission-result__reject" ><i className="fa fa-times-circle" /></span>
: <span className="permission-approval-visual__check" /> : <span className="permission-result__check" />
} }
<img className="permission-approval-visual__broken-line" src="/images/broken-line.svg" /> <img className="permission-result__broken-line" src="/images/broken-line.svg" />
<section> </div>
<div className="permission-approval-visual__identicon-container"> <div className="permission-result__identicon-container">
<div className="permission-approval-visual__identicon-border"> <div className="permission-result__identicon-border">
<Identicon <Identicon
className="permission-approval-visual__identicon" className="permission-result__identicon"
address={selectedAccount.address} address={selectedIdentities[0].address}
diameter={54} diameter={54}
/> />
</div> </div>
</div> </div>
{ redirect ? null : this.renderAccountInfo(selectedAccount) } </div>
</section>
</div> </div>
) )
} }
@ -100,8 +83,8 @@ export default class PermissionPageContainerContent extends PureComponent {
}} }}
> >
{ selectedPermissions[methodName] { selectedPermissions[methodName]
? <i className="fa fa-check-circle fa-sm" /> ? <i title={t('permissionCheckedIconDescription')} className="fa fa-check-square" />
: <i className="fa fa-circle fa-sm" /> : <i title={t('permissionUncheckedIconDescription')} className="fa fa-square" />
} }
<label>{description}</label> <label>{description}</label>
</div> </div>
@ -117,7 +100,7 @@ export default class PermissionPageContainerContent extends PureComponent {
} }
render () { render () {
const { domainMetadata, redirect, permissionRejected } = this.props const { domainMetadata, redirect, permissionRejected, selectedIdentities } = this.props
const { t } = this.context const { t } = this.context
let titleArgs let titleArgs
@ -127,8 +110,14 @@ export default class PermissionPageContainerContent extends PureComponent {
titleArgs = [ 'connectingWithMetaMask' ] titleArgs = [ 'connectingWithMetaMask' ]
} else if (domainMetadata.extensionId) { } else if (domainMetadata.extensionId) {
titleArgs = [ 'externalExtension', [domainMetadata.extensionId] ] titleArgs = [ 'externalExtension', [domainMetadata.extensionId] ]
} else if (selectedIdentities.length > 1) {
titleArgs = ['connectToMultiple', [ selectedIdentities.length ] ]
} else { } else {
titleArgs = [ 'likeToConnect', [domainMetadata.name] ] titleArgs = [
'connectTo', [
`${selectedIdentities[0].label} (...${selectedIdentities[0].address.slice(selectedIdentities[0].address.length - 4)})`,
],
]
} }
return ( return (
@ -137,27 +126,24 @@ export default class PermissionPageContainerContent extends PureComponent {
'permission-approval-container__content--redirect': redirect, 'permission-approval-container__content--redirect': redirect,
})} })}
> >
<div className="permission-approval-container__title">
{ t(...titleArgs) }
</div>
{this.renderPermissionApprovalVisual()}
{ !redirect { !redirect
? ( ? (
<section className="permission-approval-container__permissions-container"> <div>
<div className="permission-approval-container__permissions-header"> <PermissionsConnectHeader
{ domainMetadata.extensionId icon={domainMetadata.icon}
iconName={domainMetadata.origin}
headerTitle={t(...titleArgs)}
headerText={ domainMetadata.extensionId
? t('thisWillAllowExternalExtension', [domainMetadata.extensionId]) ? t('thisWillAllowExternalExtension', [domainMetadata.extensionId])
: t('thisWillAllow', [domainMetadata.name]) : t('thisWillAllow', [domainMetadata.origin])
} }
</div> />
<section className="permission-approval-container__permissions-container">
{ this.renderRequestedPermissions() } { this.renderRequestedPermissions() }
</section> </section>
)
: (
<div className="permission-approval-container__permissions-header-redirect">
{ t('redirectingBackToDapp') }
</div> </div>
) )
: this.renderRedirect()
} }
</div> </div>
) )

View File

@ -9,7 +9,7 @@ export default class PermissionPageContainer extends Component {
static propTypes = { static propTypes = {
approvePermissionsRequest: PropTypes.func.isRequired, approvePermissionsRequest: PropTypes.func.isRequired,
rejectPermissionsRequest: PropTypes.func.isRequired, rejectPermissionsRequest: PropTypes.func.isRequired,
selectedIdentity: PropTypes.object, selectedIdentities: PropTypes.array,
permissionsDescriptions: PropTypes.object.isRequired, permissionsDescriptions: PropTypes.object.isRequired,
request: PropTypes.object, request: PropTypes.object,
redirect: PropTypes.bool, redirect: PropTypes.bool,
@ -23,7 +23,7 @@ export default class PermissionPageContainer extends Component {
permissionRejected: null, permissionRejected: null,
request: {}, request: {},
requestMetadata: {}, requestMetadata: {},
selectedIdentity: {}, selectedIdentities: [],
} }
static contextTypes = { static contextTypes = {
@ -88,7 +88,7 @@ export default class PermissionPageContainer extends Component {
onSubmit = () => { onSubmit = () => {
const { const {
request: _request, approvePermissionsRequest, rejectPermissionsRequest, selectedIdentity, request: _request, approvePermissionsRequest, rejectPermissionsRequest, selectedIdentities,
} = this.props } = this.props
const request = { const request = {
@ -103,7 +103,7 @@ export default class PermissionPageContainer extends Component {
}) })
if (Object.keys(request.permissions).length > 0) { if (Object.keys(request.permissions).length > 0) {
approvePermissionsRequest(request, [selectedIdentity.address]) approvePermissionsRequest(request, selectedIdentities.map((selectedIdentity) => selectedIdentity.address))
} else { } else {
rejectPermissionsRequest(request.metadata.id) rejectPermissionsRequest(request.metadata.id)
} }
@ -114,7 +114,7 @@ export default class PermissionPageContainer extends Component {
requestMetadata, requestMetadata,
targetDomainMetadata, targetDomainMetadata,
permissionsDescriptions, permissionsDescriptions,
selectedIdentity, selectedIdentities,
redirect, redirect,
permissionRejected, permissionRejected,
} = this.props } = this.props
@ -127,7 +127,7 @@ export default class PermissionPageContainer extends Component {
selectedPermissions={this.state.selectedPermissions} selectedPermissions={this.state.selectedPermissions}
permissionsDescriptions={permissionsDescriptions} permissionsDescriptions={permissionsDescriptions}
onPermissionToggle={this.onPermissionToggle} onPermissionToggle={this.onPermissionToggle}
selectedAccount={selectedIdentity} selectedIdentities={selectedIdentities}
redirect={redirect} redirect={redirect}
permissionRejected={permissionRejected} permissionRejected={permissionRejected}
/> />

View File

@ -2,20 +2,15 @@ import { connect } from 'react-redux'
import PermissionPageContainer from './permission-page-container.component' import PermissionPageContainer from './permission-page-container.component'
import { import {
getPermissionsDescriptions, getPermissionsDescriptions,
getDomainMetadata, getTargetDomainMetadata,
} from '../../../selectors/selectors' } from '../../../selectors/selectors'
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const { request, cachedOrigin } = ownProps const { request, cachedOrigin } = ownProps
const { metadata: requestMetadata = {} } = request || {} const targetDomainMetadata = getTargetDomainMetadata(state, request, cachedOrigin)
const domainMetadata = getDomainMetadata(state)
const origin = requestMetadata.origin || cachedOrigin
const targetDomainMetadata = (domainMetadata[origin] || { name: origin, icon: null })
return { return {
permissionsDescriptions: getPermissionsDescriptions(state), permissionsDescriptions: getPermissionsDescriptions(state),
requestMetadata,
targetDomainMetadata, targetDomainMetadata,
} }
} }

View File

@ -0,0 +1,39 @@
.permissions-connect-header {
display: flex;
flex-direction: column;
justify-content: center;
&__icon {
display: flex;
flex-direction: column;
align-items: center;
.icon-with-fallback__identicon-container,
.icon-with-fallback__identicon-border {
height: 64px;
width: 64px;
}
.icon-with-fallback__identicon-border {
border: 1px solid $Grey-100;
}
.icon-with-fallback__identicon-container {
margin-bottom: 8px;
}
}
&__title {
@extend %header--24;
text-align: center;
color: $Black-100;
margin-top: 16px;
}
&__text {
@extend %content-text;
text-align: center;
margin-top: 4px;
color: $Grey-500;
}
}

View File

@ -0,0 +1,44 @@
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import IconWithFallBack from '../../ui/icon-with-fallback'
export default class PermissionsConnectHeader extends Component {
static propTypes = {
icon: PropTypes.string,
iconName: PropTypes.string.isRequired,
headerTitle: PropTypes.node,
headerText: PropTypes.string,
}
static defaultProps = {
icon: null,
headerTitle: '',
headerText: '',
}
renderHeaderIcon () {
const { icon, iconName } = this.props
return (
<div className="permissions-connect-header__icon">
<IconWithFallBack icon={ icon } name={ iconName } />
<div className="permissions-connect-header__text">{iconName }</div>
</div>
)
}
render () {
const { headerTitle, headerText } = this.props
return (
<div className="permissions-connect-header">
{ this.renderHeaderIcon() }
<div className="permissions-connect-header__title">
{ headerTitle }
</div>
<div className="permissions-connect-header__text">
{ headerText }
</div>
</div>
)
}
}

View File

@ -0,0 +1,36 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
export default class CheckBox extends PureComponent {
static propTypes = {
className: PropTypes.string,
checked: PropTypes.bool,
onClick: PropTypes.func.isRequired,
}
static defaultProps = {
className: '',
checked: false,
}
render () {
const { className, checked, onClick } = this.props
return (
<div
onClick={ () => onClick() }
className={classnames('check-box', className, {
'check-box--checked': checked,
'check-box--un-checked': !checked,
})}
>
{
checked
? <i className="fa fa-check" />
: null
}
</div>
)
}
}

View File

@ -0,0 +1 @@
export { default } from './check-box.component'

View File

@ -0,0 +1,26 @@
.check-box {
cursor: pointer;
&--checked {
background: $curious-blue;
border: 2px solid $curious-blue;
border-radius: 2px;
width: 18px;
height: 18px;
display: flex;
justify-content: center;
align-items: center;
i {
color: $white;
}
}
&--un-checked {
background: $white;
border: 2px solid $Grey-100;
border-radius: 2px;
width: 18px;
height: 18px;
}
}

View File

@ -176,6 +176,8 @@ $Orange-300: #faa66c;
$Orange-600: #c65507; $Orange-600: #c65507;
$Orange-500: #F66A0A; $Orange-500: #F66A0A;
$Black-100: #24292E;
// Font Sizes // Font Sizes
%h3 { %h3 {
font-size: 1.5rem; font-size: 1.5rem;
@ -265,7 +267,7 @@ $xxlarge-spacing: 64px;
%header--24 { %header--24 {
@extend %font; @extend %font;
font-size: 18px; font-size: 24px;
} }
%content-text { %content-text {

View File

@ -1,8 +1,13 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import React, { Component } from 'react' import React, { Component } from 'react'
import classnames from 'classnames'
import Identicon from '../../../components/ui/identicon' import Identicon from '../../../components/ui/identicon'
import Button from '../../../components/ui/button'
import CheckBox from '../../../components/ui/check-box'
import Tooltip from '../../../components/ui/tooltip-v2'
import { PRIMARY } from '../../../helpers/constants/common' import { PRIMARY } from '../../../helpers/constants/common'
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display' import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display'
import PermissionsConnectHeader from '../../../components/app/permissions-connect-header'
export default class ChooseAccount extends Component { export default class ChooseAccount extends Component {
static propTypes = { static propTypes = {
@ -12,13 +17,18 @@ export default class ChooseAccount extends Component {
lastConnectedDate: PropTypes.string, lastConnectedDate: PropTypes.string,
balance: PropTypes.string, balance: PropTypes.string,
})).isRequired, })).isRequired,
originName: PropTypes.string.isRequired, selectAccounts: PropTypes.func.isRequired,
selectAccount: PropTypes.func.isRequired,
selectNewAccountViaModal: PropTypes.func.isRequired, selectNewAccountViaModal: PropTypes.func.isRequired,
nativeCurrency: PropTypes.string.isRequired, nativeCurrency: PropTypes.string.isRequired,
addressLastConnectedMap: PropTypes.object, addressLastConnectedMap: PropTypes.object,
cancelPermissionsRequest: PropTypes.func.isRequired, cancelPermissionsRequest: PropTypes.func.isRequired,
permissionsRequestId: PropTypes.string.isRequired, permissionsRequestId: PropTypes.string.isRequired,
selectedAccountAddresses: PropTypes.object.isRequired,
targetDomainMetadata: PropTypes.object,
}
state = {
selectedAccounts: this.props.selectedAccountAddresses,
} }
static defaultProps = { static defaultProps = {
@ -29,8 +39,42 @@ export default class ChooseAccount extends Component {
t: PropTypes.func, t: PropTypes.func,
} }
handleAccountClick (address) {
const { selectedAccounts } = this.state
const newSelectedAccounts = new Set(selectedAccounts)
if (newSelectedAccounts.has(address)) {
newSelectedAccounts.delete(address)
} else {
newSelectedAccounts.add(address)
}
this.setState({ selectedAccounts: newSelectedAccounts })
}
selectAll () {
const { accounts } = this.props
const newSelectedAccounts = new Set(accounts.map((account) => account.address))
this.setState({ selectedAccounts: newSelectedAccounts })
}
deselectAll () {
this.setState({ selectedAccounts: new Set() })
}
allAreSelected () {
const { accounts } = this.props
const { selectedAccounts } = this.state
return accounts.every(({ address }) => selectedAccounts.has(address))
}
renderAccountsList = () => { renderAccountsList = () => {
const { accounts, selectAccount, nativeCurrency, addressLastConnectedMap } = this.props const { accounts, nativeCurrency, addressLastConnectedMap } = this.props
const { selectedAccounts } = this.state
return ( return (
<div className="permissions-connect-choose-account__accounts-list"> <div className="permissions-connect-choose-account__accounts-list">
{ {
@ -39,10 +83,14 @@ export default class ChooseAccount extends Component {
return ( return (
<div <div
key={`permissions-connect-choose-account-${index}`} key={`permissions-connect-choose-account-${index}`}
onClick={ () => selectAccount(address) } onClick={ () => this.handleAccountClick(address) }
className="permissions-connect-choose-account__account" className="permissions-connect-choose-account__account"
> >
<div className="permissions-connect-choose-account__account-info-wrapper"> <div className="permissions-connect-choose-account__account-info-wrapper">
<CheckBox
className="permissions-connect-choose-account__list-check-box"
checked={ selectedAccounts.has(address) }
/>
<Identicon <Identicon
diameter={34} diameter={34}
address={address} address={address}
@ -76,31 +124,86 @@ export default class ChooseAccount extends Component {
) )
} }
renderAccountsListHeader () {
const { t } = this.context
const { selectNewAccountViaModal, accounts } = this.props
return (
<div
className={classnames({
'permissions-connect-choose-account__accounts-list-header--one-item': accounts.length === 1,
'permissions-connect-choose-account__accounts-list-header--two-items': accounts.length > 1,
})}
>
{ accounts.length > 1
? (
<div className="permissions-connect-choose-account__select-all">
<CheckBox
className="permissions-connect-choose-account__header-check-box"
checked={this.allAreSelected()}
onClick={() => (this.allAreSelected() ? this.deselectAll() : this.selectAll())}
/>
<div className="permissions-connect-choose-account__text-grey">{ this.context.t('selectAll') }</div>
<Tooltip
position="bottom"
html={(
<div style={{ width: 200, padding: 4 }}>
{t('selectingAllWillAllow')}
</div>
)}
>
<i className="fa fa-info-circle" />
</Tooltip>
</div>
)
: null
}
<div
className="permissions-connect-choose-account__text-blue"
onClick={() => selectNewAccountViaModal(this.handleAccountClick.bind(this))}
>
{ this.context.t('newAccount') }
</div>
</div>
)
}
render () { render () {
const { originName, selectNewAccountViaModal, permissionsRequestId, cancelPermissionsRequest } = this.props const {
selectAccounts,
permissionsRequestId,
cancelPermissionsRequest,
targetDomainMetadata,
accounts,
} = this.props
const { selectedAccounts } = this.state
const { t } = this.context const { t } = this.context
return ( return (
<div className="permissions-connect-choose-account"> <div className="permissions-connect-choose-account">
<div className="permissions-connect-choose-account__title"> <PermissionsConnectHeader
{ t('chooseAnAcount') } icon={targetDomainMetadata.icon}
</div> iconName={targetDomainMetadata.origin}
<div className="permissions-connect-choose-account__text"> headerTitle={t('connectWithMetaMask')}
{ t('toConnectWith', [originName]) } headerText={accounts.length > 0
</div> ? t('chooseAccountsToUse')
: t('connectAccountOrCreate')
}
/>
{ this.renderAccountsListHeader() }
{ this.renderAccountsList() } { this.renderAccountsList() }
<div className="permissions-connect-choose-account__bottom-buttons"> <div className="permissions-connect-choose-account__bottom-buttons">
<div <Button
onClick={ () => cancelPermissionsRequest(permissionsRequestId) } onClick={ () => cancelPermissionsRequest(permissionsRequestId) }
className="permissions-connect-choose-account__cancel" type="default"
> >
{ t('cancel') } { t('cancel') }
</div> </Button>
<div <Button
onClick={ () => selectNewAccountViaModal() } onClick={ () => selectAccounts(selectedAccounts) }
className="permissions-connect-choose-account__new-account" type="primary"
disabled={ selectedAccounts.size === 0 }
> >
{ t('newAccount') } { t('next') }
</div> </Button>
</div> </div>
</div> </div>
) )

View File

@ -1,34 +1,93 @@
.permissions-connect-choose-account { .permissions-connect-choose-account {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-top: 40px;
width: 100%;
align-items: center; align-items: center;
margin-top: 40px;
width: 428px;
margin-left: auto;
margin-right: auto;
&__title { &__title {
@extend %header--18; @extend %header--18;
} }
&__text { &__text, &__text--blue, &__text--grey {
@extend %content-text; @extend %content-text;
line-height: 25px; line-height: 25px;
} }
&__text-blue {
color: $curious-blue;
cursor: pointer;
}
&__text-grey {
color: $Grey-500;
}
&__accounts-list { &__accounts-list {
width: 393px; width: 100%;
border: 1px solid #D0D5DA; border: 1px solid #D0D5DA;
box-sizing: border-box; box-sizing: border-box;
border-radius: 8px; border-radius: 8px;
margin-top: 36px; margin-top: 8px;
max-height: 338px;
overflow-y: auto;
@media screen and (max-width: 575px) { @media screen and (max-width: 575px) {
width: 100%; width: 100%;
} }
} }
&__accounts-list-header--one-item,
&__accounts-list-header--two-items {
display: flex;
margin-top: 36px;
width: 100%;
padding-right: 2px;
}
&__accounts-list-header--one-item {
justify-content: flex-end;
}
&__accounts-list-header--two-items {
justify-content: space-between;
}
&__account-info-wrapper { &__account-info-wrapper {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center;
}
&__list-check-box {
margin-right: 24px;
}
&__header-check-box {
margin-right: 16px;
}
&__select-all {
display: flex;
margin-left: 16px;
align-items: center;
.fa-info-circle, .fa-info-circle:hover {
color: $silver;
cursor: pointer;
margin-left: 8px;
font-size: 0.9rem;
}
.fa-info-circle {
cursor: pointer;
}
.fa-info-circle:hover {
color: $mid-gray;
}
} }
&__account { &__account {
@ -76,22 +135,23 @@
} }
} }
&__new-account, &__cancel {
@extend %content-text;
line-height: 20px;
color: #037DD6;
margin-top: 24px;
cursor: pointer;
}
&__cancel { &__cancel {
color: $Red-400; color: $Red-400;
} }
&__bottom-buttons { &__bottom-buttons {
display: flex; display: flex;
justify-content: space-around; justify-content: space-between;
width: 393px; width: 100%;
margin-top: 16px;
button {
width: 124px;
}
.btn-default {
background: white;
}
} }
} }

View File

@ -1,5 +1,3 @@
@import 'permissions-connect-header/index';
@import 'permissions-connect-footer/index'; @import 'permissions-connect-footer/index';
@import 'choose-account/index'; @import 'choose-account/index';
@ -8,4 +6,20 @@
width: 100%; width: 100%;
position: relative; position: relative;
background: white; background: white;
&__page-count-wrapper {
margin-top: 26px;
margin-left: 20px;
display: flex;
justify-content: flex-end;
align-items: flex-end;
&__page-count {
@extend %content-text;
margin-right: 30px;
font-size: 12px;
line-height: 17px;
color: #6A737D;
}
}
} }

View File

@ -10,7 +10,6 @@ export default class PermissionsConnectFooter extends Component {
const { t } = this.context const { t } = this.context
return ( return (
<div className="permissions-connect-footer"> <div className="permissions-connect-footer">
<img src="/images/mm-secure.svg" />
<div className="permissions-connect-footer__text"> <div className="permissions-connect-footer__text">
<div>{ t('onlyConnectTrust') }</div> <div>{ t('onlyConnectTrust') }</div>
<div <div
@ -18,7 +17,7 @@ export default class PermissionsConnectFooter extends Component {
onClick={() => { onClick={() => {
global.platform.openWindow({ url: 'https://medium.com/metamask/privacy-mode-is-now-enabled-by-default-1c1c957f4d57' }) global.platform.openWindow({ url: 'https://medium.com/metamask/privacy-mode-is-now-enabled-by-default-1c1c957f4d57' })
}} }}
>{ t('learnAboutRisks') } >{ t('learnMore') }
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,15 +0,0 @@
.permissions-connect-header {
margin-top: 26px;
margin-left: 20px;
display: flex;
justify-content: space-between;
align-items: flex-end;
&__page-count {
@extend %content-text;
margin-right: 30px;
font-size: 12px;
line-height: 17px;
color: #6A737D;
}
}

View File

@ -1,23 +0,0 @@
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import MetaFoxLogo from '../../../components/ui/metafox-logo'
export default class PermissionsConnectHeader extends Component {
static propTypes = {
page: PropTypes.number.isRequired,
}
render () {
const { page } = this.props
return (
<div className="permissions-connect-header">
<MetaFoxLogo
unsetIconHeight
/>
<div className="permissions-connect-header__page-count">
{ `${page}/2` }
</div>
</div>
)
}
}

View File

@ -1,7 +1,6 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import React, { Component } from 'react' import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom' import { Switch, Route } from 'react-router-dom'
import PermissionsConnectHeader from './permissions-connect-header'
import PermissionsConnectFooter from './permissions-connect-footer' import PermissionsConnectFooter from './permissions-connect-footer'
import ChooseAccount from './choose-account' import ChooseAccount from './choose-account'
import { getEnvironmentType } from '../../../../app/scripts/lib/util' import { getEnvironmentType } from '../../../../app/scripts/lib/util'
@ -37,6 +36,7 @@ export default class PermissionConnect extends Component {
confirmPermissionPath: PropTypes.string.isRequired, confirmPermissionPath: PropTypes.string.isRequired,
page: PropTypes.string.isRequired, page: PropTypes.string.isRequired,
redirecting: PropTypes.bool, redirecting: PropTypes.bool,
targetDomainMetadata: PropTypes.object,
} }
static defaultProps = { static defaultProps = {
@ -56,7 +56,7 @@ export default class PermissionConnect extends Component {
state = { state = {
redirecting: false, redirecting: false,
selectedAccountAddress: '', selectedAccountAddresses: new Set(),
permissionAccepted: null, permissionAccepted: null,
originName: this.props.originName, originName: this.props.originName,
} }
@ -94,9 +94,9 @@ export default class PermissionConnect extends Component {
} }
} }
selectAccount = (address) => { selectAccounts = (addresses) => {
this.setState({ this.setState({
selectedAccountAddress: address, selectedAccountAddresses: addresses,
}, () => this.props.history.push(this.props.confirmPermissionPath)) }, () => this.props.history.push(this.props.confirmPermissionPath))
} }
@ -161,13 +161,25 @@ export default class PermissionConnect extends Component {
connectPath, connectPath,
confirmPermissionPath, confirmPermissionPath,
page, page,
targetDomainMetadata,
} = this.props } = this.props
const { selectedAccountAddress, permissionAccepted, originName, redirecting } = this.state const {
selectedAccountAddresses,
permissionAccepted,
originName,
redirecting,
} = this.state
return ( return (
<div className="permissions-connect"> <div className="permissions-connect">
{ !redirecting { !redirecting
? <PermissionsConnectHeader page={page} /> ? (
<div className="permissions-connect__page-count-wrapper">
<div className="permissions-connect-header__page-count">
{ `${page}/2` }
</div>
</div>
)
: null : null
} }
<Switch> <Switch>
@ -175,14 +187,15 @@ export default class PermissionConnect extends Component {
path={connectPath} path={connectPath}
exact exact
render={() => ( render={() => (
<div>
<ChooseAccount <ChooseAccount
accounts={accounts} accounts={accounts}
originName={originName} originName={originName}
nativeCurrency={nativeCurrency} nativeCurrency={nativeCurrency}
selectAccount={(address) => this.selectAccount(address)} selectAccounts={(addresses) => this.selectAccounts(addresses)}
selectNewAccountViaModal={() => { selectNewAccountViaModal={(handleAccountClick) => {
showNewAccountModal({ showNewAccountModal({
onCreateNewAccount: this.selectAccount, onCreateNewAccount: (address) => handleAccountClick(address),
newAccountNumber, newAccountNumber,
}) })
}} }}
@ -194,7 +207,11 @@ export default class PermissionConnect extends Component {
} }
}} }}
permissionsRequestId={permissionsRequestId} permissionsRequestId={permissionsRequestId}
selectedAccountAddresses={selectedAccountAddresses}
targetDomainMetadata={targetDomainMetadata}
/> />
{ !redirecting ? <PermissionsConnectFooter /> : null }
</div>
)} )}
/> />
<Route <Route
@ -212,11 +229,12 @@ export default class PermissionConnect extends Component {
rejectPermissionsRequest(requestId) rejectPermissionsRequest(requestId)
this.redirectFlow(false) this.redirectFlow(false)
}} }}
selectedIdentity={accounts.find((account) => account.address === selectedAccountAddress)} selectedIdentities={accounts.filter((account) => selectedAccountAddresses.has(account.address))}
redirect={redirecting} redirect={redirecting}
permissionRejected={ permissionAccepted === false } permissionRejected={ permissionAccepted === false }
cachedOrigin={originName}
/> />
<PermissionsConnectFooter /> { !redirecting ? <PermissionsConnectFooter /> : null }
</div> </div>
)} )}
/> />

View File

@ -7,6 +7,7 @@ import {
getAccountsWithLabels, getAccountsWithLabels,
getLastConnectedInfo, getLastConnectedInfo,
getPermissionsDomains, getPermissionsDomains,
getTargetDomainMetadata,
} from '../../selectors/selectors' } from '../../selectors/selectors'
import { formatDate } from '../../helpers/utils/util' import { formatDate } from '../../helpers/utils/util'
import { approvePermissionsRequest, rejectPermissionsRequest, showModal, getCurrentWindowTab, getRequestAccountTabIds } from '../../store/actions' import { approvePermissionsRequest, rejectPermissionsRequest, showModal, getCurrentWindowTab, getRequestAccountTabIds } from '../../store/actions'
@ -52,6 +53,8 @@ const mapStateToProps = (state, ownProps) => {
throw new Error('Incorrect path for permissions-connect component') throw new Error('Incorrect path for permissions-connect component')
} }
const targetDomainMetadata = getTargetDomainMetadata(state, permissionsRequest, origin)
return { return {
permissionsRequest, permissionsRequest,
permissionsRequestId, permissionsRequestId,
@ -65,6 +68,7 @@ const mapStateToProps = (state, ownProps) => {
connectPath, connectPath,
confirmPermissionPath, confirmPermissionPath,
page, page,
targetDomainMetadata,
} }
} }

View File

@ -376,6 +376,17 @@ export function getDomainMetadata (state) {
return state.metamask.domainMetadata return state.metamask.domainMetadata
} }
export function getTargetDomainMetadata (state, request, defaultOrigin) {
const domainMetadata = getDomainMetadata(state)
const { metadata: requestMetadata = {} } = request || {}
const origin = requestMetadata.origin || defaultOrigin
const targetDomainMetadata = (domainMetadata[origin] || { name: origin, icon: null })
targetDomainMetadata.origin = origin
return targetDomainMetadata
}
export function getActiveTab (state) { export function getActiveTab (state) {
return state.activeTab return state.activeTab
} }