// Copyright (c) 2021 Proton Technologies AG // // This file is part of ProtonMail Bridge. // // ProtonMail Bridge is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ProtonMail Bridge is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with ProtonMail Bridge. If not, see . // Dialog with adding new user import QtQuick 2.8 import ProtonUI 1.0 Dialog { id: root title : "" signal createAccount() property alias inputPassword : inputPassword property alias input2FAuth : input2FAuth property alias inputPasswMailbox : inputPasswMailbox // property alias username : inputUsername.text property alias usernameElided : usernameMetrics.elidedText isDialogBusy : currentIndex==waitingAuthIndex || currentIndex==addingAccIndex property bool isFirstAccount: false property color buttonOpaqueMain : "white" property int origin: 0 property int nameAndPasswordIndex : 0 property int waitingAuthIndex : 2 property int twoFAIndex : 1 property int mailboxIndex : 3 property int addingAccIndex : 4 property int newAccountIndex : 5 signal cancel() signal okay() TextMetrics { id: usernameMetrics font: dialogWaitingAuthText.font elideWidth : Style.dialog.widthInput elide : Qt.ElideMiddle text : root.username } Column { // 0 id: dialogNameAndPassword property int heightInputs : inputUsername.height + buttonRow.height + middleSep.height + inputPassword.height + middleSepPassw.height Rectangle { id: topSep color : "transparent" width : Style.main.dummy // Hacky hack: +10 is to make title of Dialog bigger so longer error can fit just fine. height : root.height/2 + 10 - (dialogNameAndPassword.heightInputs)/2 } InputField { id: inputUsername iconText : Style.fa.user_circle label : qsTr("Username", "enter username to add account") onAccepted : inputPassword.focusInput = true } Rectangle { id: middleSepPassw; color : "transparent"; width : Style.main.dummy; height : Style.dialog.heightSeparator} InputField { id: inputPassword label : qsTr("Password", "password entry field") iconText : Style.fa.lock isPassword : true onAccepted : root.okay() } Rectangle { id: middleSep; color : "transparent"; width : Style.main.dummy; height : 2*Style.dialog.heightSeparator } Row { id: buttonRow anchors.horizontalCenter: parent.horizontalCenter spacing: Style.dialog.fontSize ButtonRounded { id:buttonCancel fa_icon : Style.fa.times text : qsTr("Cancel", "dismisses current action") color_main : Style.dialog.text onClicked : root.cancel() } ButtonRounded { id: buttonNext fa_icon : Style.fa.check text : qsTr("Next", "navigate to next page in add account flow") color_main : buttonOpaqueMain color_minor : Style.dialog.textBlue isOpaque : true onClicked : root.okay() } } Rectangle { color : "transparent" width : Style.main.dummy height : root.height - (topSep.height + dialogNameAndPassword.heightInputs + Style.main.bottomMargin + signUpForAccount.height) } ClickIconText { id: signUpForAccount anchors.horizontalCenter: parent.horizontalCenter fontSize : Style.dialog.fontSize iconSize : Style.dialog.fontSize iconText : "+" text : qsTr ("Sign Up for an Account", "takes user to web page where they can create a ProtonMail account") textBold : true textUnderline : true textColor : Style.dialog.text onClicked : root.createAccount() } } Column { // 1 id: dialog2FA property int heightInputs : buttonRowPassw.height + middleSep2FA.height + input2FAuth.height Rectangle { color : "transparent" width : Style.main.dummy height : (root.height - dialog2FA.heightInputs)/2 } InputField { id: input2FAuth label : qsTr("Two Factor Code", "two factor code entry field") iconText : Style.fa.lock onAccepted : root.okay() } Rectangle { id: middleSep2FA; color : "transparent"; width : Style.main.dummy; height : 2*Style.dialog.heightSeparator } Row { id: buttonRowPassw anchors.horizontalCenter: parent.horizontalCenter spacing: Style.dialog.fontSize ButtonRounded { id: buttonBack fa_icon: Style.fa.times text: qsTr("Back", "navigate back in add account flow") color_main: Style.dialog.text onClicked : root.cancel() } ButtonRounded { id: buttonNextTwo fa_icon: Style.fa.check text: qsTr("Next", "navigate to next page in add account flow") color_main: buttonOpaqueMain color_minor: Style.dialog.textBlue isOpaque: true onClicked : root.okay() } } } Column { // 2 id: dialogWaitingAuth Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height-dialogWaitingAuthText.height) /2 } Text { id: dialogWaitingAuthText anchors.horizontalCenter: parent.horizontalCenter color: Style.dialog.text font.pointSize: Style.dialog.fontSize * Style.pt text : qsTr("Logging in") +"\n" + root.usernameElided horizontalAlignment: Text.AlignHCenter } } Column { // 3 id: dialogMailboxPassword property int heightInputs : buttonRowMailbox.height + inputPasswMailbox.height + middleSepMailbox.height Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height - dialogMailboxPassword.heightInputs)/2} InputField { id: inputPasswMailbox label : qsTr("Mailbox password for %1", "mailbox password entry field").arg(root.usernameElided) iconText : Style.fa.lock isPassword : true onAccepted : root.okay() } Rectangle { id: middleSepMailbox; color : "transparent"; width : Style.main.dummy; height : 2*Style.dialog.heightSeparator } Row { id: buttonRowMailbox anchors.horizontalCenter: parent.horizontalCenter spacing: Style.dialog.fontSize ButtonRounded { id: buttonBackBack fa_icon: Style.fa.times text: qsTr("Back", "navigate back in add account flow") color_main: Style.dialog.text onClicked : root.cancel() } ButtonRounded { id: buttonLogin fa_icon: Style.fa.check text: qsTr("Next", "navigate to next page in add account flow") color_main: buttonOpaqueMain color_minor: Style.dialog.textBlue isOpaque: true onClicked : root.okay() } } } Column { // 4 id: dialogWaitingAccount Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height - dialogWaitingAccountText.height )/2 } Text { id: dialogWaitingAccountText anchors.horizontalCenter: parent.horizontalCenter color: Style.dialog.text font { bold : true pointSize: Style.dialog.fontSize * Style.pt } text : qsTr("Adding account, please wait ...", "displayed after user has logged in, before new account is displayed") wrapMode: Text.Wrap } } Column { // 5 id: dialogFirstUserAdded Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height - dialogWaitingAccountText.height - okButton.height*2 )/2 } Text { id: textFirstUser anchors.horizontalCenter: parent.horizontalCenter color: Style.dialog.text font { bold : false pointSize: Style.dialog.fontSize * Style.pt } width: 2*root.width/3 horizontalAlignment: Text.AlignHCenter textFormat: Text.RichText text: ""+ qsTr("Now you need to configure your client(s) to use the Bridge. Instructions for configuring your client can be found at", "") + "
https://protonmail.com/bridge/clients." wrapMode: Text.Wrap onLinkActivated: { Qt.openUrlExternally(link) } MouseArea { anchors.fill: parent cursorShape: parent.hoveredLink=="" ? Qt.PointingHandCursor : Qt.WaitCursor acceptedButtons: Qt.NoButton } } Rectangle { color : "transparent"; width : Style.main.dummy; height : okButton.height} ButtonRounded{ id: okButton anchors.horizontalCenter: parent.horizontalCenter color_main: buttonOpaqueMain color_minor: Style.main.textBlue isOpaque: true text: qsTr("Okay", "confirms and dismisses a notification") onClicked: root.hide() } } function clear_user() { inputUsername.text = "" inputUsername.rightIcon = "" } function clear_passwd() { inputPassword.text = "" inputPassword.rightIcon = "" inputPassword.hidePasswordText() } function clear_2fa() { input2FAuth.text = "" input2FAuth.rightIcon = "" } function clear_passwd_mailbox() { inputPasswMailbox.text = "" inputPasswMailbox.rightIcon = "" inputPasswMailbox.hidePasswordText() } onCancel : { root.warning.visible=false if (currentIndex==0) { root.hide() } else { clear_passwd() clear_passwd_mailbox() currentIndex=0 } } function check_inputs() { var isOK = true switch (currentIndex) { case nameAndPasswordIndex : isOK &= inputUsername.checkNonEmpty() isOK &= inputPassword.checkNonEmpty() break case twoFAIndex : isOK &= input2FAuth.checkNonEmpty() break case mailboxIndex : isOK &= inputPasswMailbox.checkNonEmpty() break } if (isOK) { warning.visible = false warning.text= "" } else { setWarning(qsTr("Field required", "a field that must be filled in to submit form"),0) } return isOK } function setWarning(msg, changeIndex) { // show message root.warning.text = msg root.warning.visible = true } onOkay : { var isOK = check_inputs() if (isOK) { root.origin = root.currentIndex switch (root.currentIndex) { case nameAndPasswordIndex: case twoFAIndex: root.currentIndex = waitingAuthIndex break; case mailboxIndex: root.currentIndex = addingAccIndex } timer.start() } } onShow: { root.title = qsTr ("Log in to your ProtonMail account", "displayed on screen when user enters username to begin adding account") root.warning.visible = false inputUsername.forceFocus() root.isFirstAccount = go.isFirstStart && accountsModel.count==0 } function startAgain() { clear_passwd() clear_2fa() clear_passwd_mailbox() root.currentIndex = nameAndPasswordIndex root.inputPassword.focusInput = true } function finishLogin(){ root.currentIndex = addingAccIndex var auth = go.addAccount(inputPasswMailbox.text) if (auth<0) { startAgain() return } } Connections { target: timer onTriggered : { timer.repeat = false switch (root.origin) { case nameAndPasswordIndex: var auth = go.login(inputUsername.text, inputPassword.text) if (auth < 0) { startAgain() break } if (auth == 1) { root.currentIndex = twoFAIndex root.input2FAuth.focusInput = true break } if (auth == 2) { root.currentIndex = mailboxIndex root.inputPasswMailbox.focusInput = true break } root.inputPasswMailbox.text = inputPassword.text root.finishLogin() break; case twoFAIndex: var auth = go.auth2FA(input2FAuth.text) if (auth < 0) { startAgain() break } if (auth == 1) { root.currentIndex = mailboxIndex root.inputPasswMailbox.focusInput = true break } root.inputPasswMailbox.text = inputPassword.text root.finishLogin() break; case mailboxIndex: root.finishLogin() break; } } } onHide: { // because hide slot is conneceted to processFinished it will update // the list evertyime `go` obejcet is finished clear_passwd() clear_passwd_mailbox() clear_2fa() clear_user() go.loadAccounts() if (root.isFirstAccount && accountsModel.count==1) { root.isFirstAccount=false root.currentIndex=5 root.show() root.title=qsTr("Success, Account Added!", "shown after successful account addition") } } Keys.onPressed: { if (event.key == Qt.Key_Enter) { root.okay() } } }