mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 20:56:51 +00:00
feat(GODT-2772): new login layout.
This commit is contained in:
@ -400,7 +400,7 @@ Status GRPCService::Login(ServerContext *, LoginRequest const *request, Empty *)
|
|||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
if (usersTab.nextUserTwoPasswordsRequired()) {
|
if (usersTab.nextUserTwoPasswordsRequired()) {
|
||||||
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent());
|
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent(loginUsername_));
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,7 +425,7 @@ Status GRPCService::Login2FA(ServerContext *, LoginRequest const *request, Empty
|
|||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
if (usersTab.nextUserTwoPasswordsRequired()) {
|
if (usersTab.nextUserTwoPasswordsRequired()) {
|
||||||
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent());
|
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent(loginUsername_));
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -233,7 +233,7 @@ signals: // Signals received from the Go backend, to be forwarded to QML
|
|||||||
void login2FARequested(QString const &username); ///< Signal for the 'login2FARequested' gRPC stream event.
|
void login2FARequested(QString const &username); ///< Signal for the 'login2FARequested' gRPC stream event.
|
||||||
void login2FAError(QString const &errorMsg); ///< Signal for the 'login2FAError' gRPC stream event.
|
void login2FAError(QString const &errorMsg); ///< Signal for the 'login2FAError' gRPC stream event.
|
||||||
void login2FAErrorAbort(QString const &errorMsg); ///< Signal for the 'login2FAErrorAbort' gRPC stream event.
|
void login2FAErrorAbort(QString const &errorMsg); ///< Signal for the 'login2FAErrorAbort' gRPC stream event.
|
||||||
void login2PasswordRequested(); ///< Signal for the 'login2PasswordRequested' gRPC stream event.
|
void login2PasswordRequested(QString const &username); ///< Signal for the 'login2PasswordRequested' gRPC stream event.
|
||||||
void login2PasswordError(QString const &errorMsg); ///< Signal for the 'login2PasswordError' gRPC stream event.
|
void login2PasswordError(QString const &errorMsg); ///< Signal for the 'login2PasswordError' gRPC stream event.
|
||||||
void login2PasswordErrorAbort(QString const &errorMsg); ///< Signal for the 'login2PasswordErrorAbort' gRPC stream event.
|
void login2PasswordErrorAbort(QString const &errorMsg); ///< Signal for the 'login2PasswordErrorAbort' gRPC stream event.
|
||||||
void loginFinished(int index, bool wasSignedOut); ///< Signal for the 'loginFinished' gRPC stream event.
|
void loginFinished(int index, bool wasSignedOut); ///< Signal for the 'loginFinished' gRPC stream event.
|
||||||
|
|||||||
@ -45,33 +45,16 @@ Item {
|
|||||||
icon.source = "/qml/icons/img-mail-clients.svg";
|
icon.source = "/qml/icons/img-mail-clients.svg";
|
||||||
}
|
}
|
||||||
function showLogin() {
|
function showLogin() {
|
||||||
descriptionLabel.text = qsTr("Let's start by signing in to your Proton account.");
|
showOnboarding()
|
||||||
linkLabel1.setLink("https://proton.me/mail/pricing", qsTr("Create or upgrade your account"));
|
}
|
||||||
linkLabel2.clear();
|
|
||||||
showLoginCommon();
|
|
||||||
}
|
|
||||||
function showLogin2FA() {
|
function showLogin2FA() {
|
||||||
descriptionLabel.text = qsTr("You have enabled two-factor authentication. Please enter the 6-digit code provided by your authenticator application.");
|
showOnboarding()
|
||||||
linkLabel1.clear();
|
|
||||||
linkLabel2.clear();
|
|
||||||
showLoginCommon();
|
|
||||||
}
|
|
||||||
function showLoginCommon() {
|
|
||||||
titleLabel.text = qsTr("Sign in to your Proton Account");
|
|
||||||
icon.Layout.preferredHeight = 72;
|
|
||||||
icon.Layout.preferredWidth = 72;
|
|
||||||
icon.source = "/qml/icons/ic-bridge.svg";
|
|
||||||
icon.sourceSize.height = 128;
|
|
||||||
icon.sourceSize.width = 128;
|
|
||||||
}
|
}
|
||||||
function showLoginMailboxPassword() {
|
function showLoginMailboxPassword() {
|
||||||
descriptionLabel.text = qsTr("You have secured your account with a separate mailbox password.");
|
showOnboarding()
|
||||||
linkLabel1.clear();
|
|
||||||
linkLabel2.clear();
|
|
||||||
showLoginCommon();
|
|
||||||
}
|
}
|
||||||
function showOnboarding() {
|
function showOnboarding() {
|
||||||
titleLabel.text = qsTr("Welcome to\nProton Mail Bridge");
|
titleLabel.text = (Backend.users.count === 0) ? qsTr("Welcome to\nProton Mail Bridge") : qsTr("Add a Proton Mail account");
|
||||||
descriptionLabel.text = qsTr("Bridge is the gateway between your Proton account and your email client. It runs in the background and encrypts and decrypts your messages seamlessly. ");
|
descriptionLabel.text = qsTr("Bridge is the gateway between your Proton account and your email client. It runs in the background and encrypts and decrypts your messages seamlessly. ");
|
||||||
linkLabel1.setLink("https://proton.me/support/bridge", qsTr("Why do I need Bridge?"));
|
linkLabel1.setLink("https://proton.me/support/bridge", qsTr("Why do I need Bridge?"));
|
||||||
linkLabel2.clear();
|
linkLabel2.clear();
|
||||||
@ -81,11 +64,6 @@ Item {
|
|||||||
icon.sourceSize.height = 148;
|
icon.sourceSize.height = 148;
|
||||||
icon.sourceSize.width = 265;
|
icon.sourceSize.width = 265;
|
||||||
}
|
}
|
||||||
function showOutlookSelector() {
|
|
||||||
showClientConfigCommon();
|
|
||||||
linkLabel1.setLink("https://proton.me/support/bridge", qsTr("My version of Outlook is not listed"));
|
|
||||||
linkLabel2.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onLogin2FARequested() {
|
function onLogin2FARequested() {
|
||||||
|
|||||||
@ -43,9 +43,6 @@ FocusScope {
|
|||||||
mailboxPasswordLayout.reset();
|
mailboxPasswordLayout.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight
|
|
||||||
implicitWidth: children[0].implicitWidth
|
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
id: stackLayout
|
id: stackLayout
|
||||||
function loginFailed() {
|
function loginFailed() {
|
||||||
@ -91,9 +88,10 @@ FocusScope {
|
|||||||
root.reset();
|
root.reset();
|
||||||
errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
|
errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
|
||||||
}
|
}
|
||||||
function onLogin2PasswordRequested() {
|
function onLogin2PasswordRequested(username) {
|
||||||
console.assert(stackLayout.currentIndex === Login.RootStack.Login || stackLayout.currentIndex === Login.RootStack.TOTP, "Unexpected login2PasswordRequested");
|
console.assert(stackLayout.currentIndex === Login.RootStack.Login || stackLayout.currentIndex === Login.RootStack.TOTP, "Unexpected login2PasswordRequested");
|
||||||
stackLayout.currentIndex = Login.RootStack.MailboxPassword;
|
stackLayout.currentIndex = Login.RootStack.MailboxPassword;
|
||||||
|
mailboxPasswordUsernameLabel.text = username;
|
||||||
secondPasswordTextField.focus = true;
|
secondPasswordTextField.focus = true;
|
||||||
}
|
}
|
||||||
function onLoginAlreadyLoggedIn(_) {
|
function onLoginAlreadyLoggedIn(_) {
|
||||||
@ -124,299 +122,353 @@ FocusScope {
|
|||||||
|
|
||||||
target: Backend
|
target: Backend
|
||||||
}
|
}
|
||||||
ColumnLayout {
|
Item {
|
||||||
id: loginLayout
|
ColumnLayout {
|
||||||
function reset(clearUsername = false) {
|
id: loginLayout
|
||||||
signInButton.loading = false;
|
function reset(clearUsername = false) {
|
||||||
errorLabel.text = "";
|
signInButton.loading = false;
|
||||||
usernameTextField.enabled = true;
|
errorLabel.text = "";
|
||||||
usernameTextField.error = false;
|
usernameTextField.enabled = true;
|
||||||
usernameTextField.errorString = "";
|
usernameTextField.error = false;
|
||||||
usernameTextField.focus = true;
|
usernameTextField.errorString = "";
|
||||||
if (clearUsername) {
|
usernameTextField.focus = true;
|
||||||
usernameTextField.text = "";
|
if (clearUsername) {
|
||||||
|
usernameTextField.text = "";
|
||||||
|
}
|
||||||
|
passwordTextField.enabled = true;
|
||||||
|
passwordTextField.error = false;
|
||||||
|
passwordTextField.errorString = "";
|
||||||
|
passwordTextField.text = "";
|
||||||
}
|
}
|
||||||
passwordTextField.enabled = true;
|
|
||||||
passwordTextField.error = false;
|
|
||||||
passwordTextField.errorString = "";
|
|
||||||
passwordTextField.text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
spacing: 0
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
Label {
|
ColumnLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.fillWidth: true
|
||||||
colorScheme: wizard.colorScheme
|
spacing: 8
|
||||||
text: qsTr("Sign in")
|
|
||||||
type: Label.LabelType.Heading
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: subTitle
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.topMargin: 8
|
|
||||||
color: wizard.colorScheme.text_weak
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
text: qsTr("Enter your Proton Account details.")
|
|
||||||
type: Label.LabelType.Lead
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 48
|
|
||||||
spacing: 0
|
|
||||||
visible: errorLabel.text.length > 0
|
|
||||||
|
|
||||||
ColorImage {
|
Label {
|
||||||
color: wizard.colorScheme.signal_danger
|
Layout.alignment: Qt.AlignHCenter
|
||||||
height: errorLabel.lineHeight
|
Layout.fillWidth: true
|
||||||
source: "/qml/icons/ic-exclamation-circle-filled.svg"
|
colorScheme: wizard.colorScheme
|
||||||
sourceSize.height: errorLabel.lineHeight
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Sign in")
|
||||||
|
type: Label.LabelType.Title
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: wizard.colorScheme.text_weak
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Enter your Proton Account details.")
|
||||||
|
type: Label.LabelType.Body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 0
|
||||||
|
visible: errorLabel.text.length > 0
|
||||||
|
|
||||||
|
ColorImage {
|
||||||
|
color: wizard.colorScheme.signal_danger
|
||||||
|
height: errorLabel.lineHeight
|
||||||
|
source: "/qml/icons/ic-exclamation-circle-filled.svg"
|
||||||
|
sourceSize.height: errorLabel.lineHeight
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: errorLabel
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 4
|
||||||
|
color: wizard.colorScheme.signal_danger
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: usernameTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
focus: true
|
||||||
|
label: qsTr("Email or username")
|
||||||
|
validateOnEditingFinished: false
|
||||||
|
validator: function (str) {
|
||||||
|
if (str.length === 0) {
|
||||||
|
return qsTr("Enter email or username");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: passwordTextField.forceActiveFocus()
|
||||||
|
onTextChanged: {
|
||||||
|
// remove "invalid username / password error"
|
||||||
|
if (error || errorLabel.text.length > 0) {
|
||||||
|
errorLabel.text = "";
|
||||||
|
usernameTextField.error = false;
|
||||||
|
passwordTextField.error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: passwordTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
label: qsTr("Password")
|
||||||
|
validateOnEditingFinished: false
|
||||||
|
validator: function (str) {
|
||||||
|
if (str.length === 0) {
|
||||||
|
return qsTr("Enter password");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: signInButton.checkAndSignIn()
|
||||||
|
onTextChanged: {
|
||||||
|
// remove "invalid username / password error"
|
||||||
|
if (error || errorLabel.text.length > 0) {
|
||||||
|
errorLabel.text = "";
|
||||||
|
usernameTextField.error = false;
|
||||||
|
passwordTextField.error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: signInButton
|
||||||
|
function checkAndSignIn() {
|
||||||
|
usernameTextField.validate();
|
||||||
|
passwordTextField.validate();
|
||||||
|
if (usernameTextField.error || passwordTextField.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
usernameTextField.enabled = false;
|
||||||
|
passwordTextField.enabled = false;
|
||||||
|
loading = true;
|
||||||
|
Backend.login(usernameTextField.text, Qt.btoa(passwordTextField.text));
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
enabled: !loading
|
||||||
|
text: loading ? qsTr("Signing in") : qsTr("Sign in")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
checkAndSignIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
enabled: !signInButton.loading
|
||||||
|
secondary: true
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LinkLabel {
|
||||||
|
id: linkLabel
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
text: linkLabel.link("https://proton.me/mail/pricing", qsTr("Create or upgrade your account"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
ColumnLayout {
|
||||||
|
id: totpLayout
|
||||||
|
function reset() {
|
||||||
|
twoFAButton.loading = false;
|
||||||
|
twoFactorPasswordTextField.enabled = true;
|
||||||
|
twoFactorPasswordTextField.error = false;
|
||||||
|
twoFactorPasswordTextField.errorString = "";
|
||||||
|
twoFactorPasswordTextField.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Two-factor authentication")
|
||||||
|
type: Label.LabelType.Title
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: twoFactorUsernameLabel
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: wizard.colorScheme.text_weak
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: ""
|
||||||
|
type: Label.LabelType.Body
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
id: errorLabel
|
id: descriptionLabel
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 4
|
|
||||||
color: wizard.colorScheme.signal_danger
|
|
||||||
colorScheme: wizard.colorScheme
|
colorScheme: wizard.colorScheme
|
||||||
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("You have enabled two-factor authentication. Please enter the 6-digit code provided by your authenticator application.")
|
||||||
|
type: Label.LabelType.Body
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
TextField {
|
||||||
TextField {
|
id: twoFactorPasswordTextField
|
||||||
id: usernameTextField
|
Layout.fillWidth: true
|
||||||
Layout.fillWidth: true
|
colorScheme: wizard.colorScheme
|
||||||
Layout.topMargin: 48
|
label: qsTr("Two-factor code")
|
||||||
colorScheme: wizard.colorScheme
|
validateOnEditingFinished: false
|
||||||
focus: true
|
validator: function (str) {
|
||||||
label: qsTr("Email or username")
|
if (str.length === 0) {
|
||||||
validateOnEditingFinished: false
|
return qsTr("Enter the 6-digit code");
|
||||||
validator: function (str) {
|
}
|
||||||
if (str.length === 0) {
|
|
||||||
return qsTr("Enter email or username");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: passwordTextField.forceActiveFocus()
|
onAccepted: {
|
||||||
onTextChanged: {
|
|
||||||
// remove "invalid username / password error"
|
|
||||||
if (error || errorLabel.text.length > 0) {
|
|
||||||
errorLabel.text = "";
|
|
||||||
usernameTextField.error = false;
|
|
||||||
passwordTextField.error = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: passwordTextField
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 48
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
label: qsTr("Password")
|
|
||||||
validateOnEditingFinished: false
|
|
||||||
validator: function (str) {
|
|
||||||
if (str.length === 0) {
|
|
||||||
return qsTr("Enter password");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: signInButton.checkAndSignIn()
|
|
||||||
onTextChanged: {
|
|
||||||
// remove "invalid username / password error"
|
|
||||||
if (error || errorLabel.text.length > 0) {
|
|
||||||
errorLabel.text = "";
|
|
||||||
usernameTextField.error = false;
|
|
||||||
passwordTextField.error = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: signInButton
|
|
||||||
function checkAndSignIn() {
|
|
||||||
usernameTextField.validate();
|
|
||||||
passwordTextField.validate();
|
|
||||||
if (usernameTextField.error || passwordTextField.error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
usernameTextField.enabled = false;
|
|
||||||
passwordTextField.enabled = false;
|
|
||||||
loading = true;
|
|
||||||
Backend.login(usernameTextField.text, Qt.btoa(passwordTextField.text));
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 48
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !loading
|
|
||||||
text: loading ? qsTr("Signing in") : qsTr("Sign in")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
checkAndSignIn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 32
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !signInButton.loading
|
|
||||||
secondary: true
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
id: totpLayout
|
|
||||||
function reset() {
|
|
||||||
twoFAButton.loading = false;
|
|
||||||
twoFactorPasswordTextField.enabled = true;
|
|
||||||
twoFactorPasswordTextField.error = false;
|
|
||||||
twoFactorPasswordTextField.errorString = "";
|
|
||||||
twoFactorPasswordTextField.text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
text: qsTr("Two-factor authentication")
|
|
||||||
type: Label.LabelType.Heading
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: twoFactorUsernameLabel
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
Layout.topMargin: 8
|
|
||||||
color: wizard.colorScheme.text_weak
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
type: Label.LabelType.Lead
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: twoFactorPasswordTextField
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 32
|
|
||||||
assistiveText: qsTr("Enter the 6-digit code")
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
label: qsTr("Two-factor code")
|
|
||||||
validateOnEditingFinished: false
|
|
||||||
validator: function (str) {
|
|
||||||
if (str.length === 0) {
|
|
||||||
return qsTr("Enter the 6-digit code");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
twoFAButton.onClicked();
|
|
||||||
}
|
|
||||||
onTextChanged: {
|
|
||||||
if (text.length >= 6) {
|
|
||||||
twoFAButton.onClicked();
|
twoFAButton.onClicked();
|
||||||
}
|
}
|
||||||
}
|
onTextChanged: {
|
||||||
}
|
if (text.length >= 6) {
|
||||||
Button {
|
twoFAButton.onClicked();
|
||||||
id: twoFAButton
|
}
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 48
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !loading
|
|
||||||
text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
twoFactorPasswordTextField.validate();
|
|
||||||
if (twoFactorPasswordTextField.error) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
twoFactorPasswordTextField.enabled = false;
|
|
||||||
loading = true;
|
|
||||||
Backend.login2FA(usernameTextField.text, Qt.btoa(twoFactorPasswordTextField.text));
|
|
||||||
}
|
}
|
||||||
}
|
Button {
|
||||||
Button {
|
id: twoFAButton
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 32
|
colorScheme: wizard.colorScheme
|
||||||
colorScheme: wizard.colorScheme
|
enabled: !loading
|
||||||
enabled: !twoFAButton.loading
|
text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
|
||||||
secondary: true
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.abort();
|
twoFactorPasswordTextField.validate();
|
||||||
|
if (twoFactorPasswordTextField.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
twoFactorPasswordTextField.enabled = false;
|
||||||
|
loading = true;
|
||||||
|
Backend.login2FA(usernameTextField.text, Qt.btoa(twoFactorPasswordTextField.text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
enabled: !twoFAButton.loading
|
||||||
|
secondary: true
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ColumnLayout {
|
Item {
|
||||||
id: mailboxPasswordLayout
|
ColumnLayout {
|
||||||
function reset() {
|
id: mailboxPasswordLayout
|
||||||
secondPasswordButton.loading = false;
|
function reset() {
|
||||||
secondPasswordTextField.enabled = true;
|
secondPasswordButton.loading = false;
|
||||||
secondPasswordTextField.error = false;
|
secondPasswordTextField.enabled = true;
|
||||||
secondPasswordTextField.errorString = "";
|
secondPasswordTextField.error = false;
|
||||||
secondPasswordTextField.text = "";
|
secondPasswordTextField.errorString = "";
|
||||||
}
|
secondPasswordTextField.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
spacing: 0
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
Label {
|
ColumnLayout {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.fillWidth: true
|
||||||
colorScheme: wizard.colorScheme
|
spacing: 8
|
||||||
text: qsTr("Unlock your mailbox")
|
|
||||||
type: Label.LabelType.Heading
|
Label {
|
||||||
}
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Label {
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignCenter
|
colorScheme: wizard.colorScheme
|
||||||
Layout.topMargin: 8
|
horizontalAlignment: Text.AlignHCenter
|
||||||
color: wizard.colorScheme.text_weak
|
text: qsTr("Unlock your mailbox")
|
||||||
colorScheme: wizard.colorScheme
|
type: Label.LabelType.Title
|
||||||
type: Label.LabelType.Lead
|
}
|
||||||
}
|
Label {
|
||||||
TextField {
|
id: mailboxPasswordUsernameLabel
|
||||||
id: secondPasswordTextField
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 48
|
color: wizard.colorScheme.text_weak
|
||||||
colorScheme: wizard.colorScheme
|
colorScheme: wizard.colorScheme
|
||||||
echoMode: TextInput.Password
|
horizontalAlignment: Text.AlignHCenter
|
||||||
label: qsTr("Mailbox password")
|
text: ""
|
||||||
validateOnEditingFinished: false
|
type: Label.LabelType.Body
|
||||||
validator: function (str) {
|
|
||||||
if (str.length === 0) {
|
|
||||||
return qsTr("Enter password");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Label {
|
||||||
onAccepted: {
|
Layout.alignment: Qt.AlignHCenter
|
||||||
secondPasswordButton.onClicked();
|
Layout.fillWidth: true
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("You have secured your account with a separate mailbox password.")
|
||||||
|
type: Label.LabelType.Body
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
TextField {
|
||||||
Button {
|
id: secondPasswordTextField
|
||||||
id: secondPasswordButton
|
Layout.fillWidth: true
|
||||||
Layout.fillWidth: true
|
colorScheme: wizard.colorScheme
|
||||||
Layout.topMargin: 48
|
echoMode: TextInput.Password
|
||||||
colorScheme: wizard.colorScheme
|
label: qsTr("Mailbox password")
|
||||||
enabled: !loading
|
validateOnEditingFinished: false
|
||||||
text: loading ? qsTr("Unlocking") : qsTr("Unlock")
|
validator: function (str) {
|
||||||
|
if (str.length === 0) {
|
||||||
onClicked: {
|
return qsTr("Enter password");
|
||||||
secondPasswordTextField.validate();
|
}
|
||||||
if (secondPasswordTextField.error) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
secondPasswordTextField.enabled = false;
|
|
||||||
loading = true;
|
|
||||||
Backend.login2Password(usernameTextField.text, Qt.btoa(secondPasswordTextField.text));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 32
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !secondPasswordButton.loading
|
|
||||||
secondary: true
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
|
|
||||||
onClicked: {
|
onAccepted: {
|
||||||
root.abort();
|
secondPasswordButton.onClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: secondPasswordButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
enabled: !loading
|
||||||
|
text: loading ? qsTr("Unlocking") : qsTr("Unlock")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
secondPasswordTextField.validate();
|
||||||
|
if (secondPasswordTextField.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
secondPasswordTextField.enabled = false;
|
||||||
|
loading = true;
|
||||||
|
Backend.login2Password(usernameTextField.text, Qt.btoa(secondPasswordTextField.text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
colorScheme: wizard.colorScheme
|
||||||
|
enabled: !secondPasswordButton.loading
|
||||||
|
secondary: true
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -110,11 +110,6 @@ Item {
|
|||||||
leftContent.showOnboarding();
|
leftContent.showOnboarding();
|
||||||
rightContent.currentIndex = SetupWizard.ContentStack.Onboarding;
|
rightContent.currentIndex = SetupWizard.ContentStack.Onboarding;
|
||||||
}
|
}
|
||||||
function showOutlookSelector() {
|
|
||||||
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
|
|
||||||
leftContent.showOutlookSelector();
|
|
||||||
rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigOutlookSelector;
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onLoginFinished(userIndex, wasSignedOut) {
|
function onLoginFinished(userIndex, wasSignedOut) {
|
||||||
|
|||||||
@ -278,8 +278,9 @@ SPStreamEvent newLoginTfaRequestedEvent(QString const &username) {
|
|||||||
/// \param[in] username The username.
|
/// \param[in] username The username.
|
||||||
/// \return The event.
|
/// \return The event.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
SPStreamEvent newLoginTwoPasswordsRequestedEvent() {
|
SPStreamEvent newLoginTwoPasswordsRequestedEvent(QString const &username) {
|
||||||
auto event = new ::grpc::LoginTwoPasswordsRequestedEvent;
|
auto event = new ::grpc::LoginTwoPasswordsRequestedEvent;
|
||||||
|
event->set_username(username.toStdString());
|
||||||
auto loginEvent = new grpc::LoginEvent;
|
auto loginEvent = new grpc::LoginEvent;
|
||||||
loginEvent->set_allocated_twopasswordrequested(event);
|
loginEvent->set_allocated_twopasswordrequested(event);
|
||||||
return wrapLoginEvent(loginEvent);
|
return wrapLoginEvent(loginEvent);
|
||||||
|
|||||||
@ -42,7 +42,7 @@ SPStreamEvent newShowMainWindowEvent(); ///< Create a new ShowMainWindowEvent ev
|
|||||||
// Login events
|
// Login events
|
||||||
SPStreamEvent newLoginError(grpc::LoginErrorType error, QString const &message); ///< Create a new LoginError event.
|
SPStreamEvent newLoginError(grpc::LoginErrorType error, QString const &message); ///< Create a new LoginError event.
|
||||||
SPStreamEvent newLoginTfaRequestedEvent(QString const &username); ///< Create a new LoginTfaRequestedEvent event.
|
SPStreamEvent newLoginTfaRequestedEvent(QString const &username); ///< Create a new LoginTfaRequestedEvent event.
|
||||||
SPStreamEvent newLoginTwoPasswordsRequestedEvent(); ///< Create a new LoginTwoPasswordsRequestedEvent event.
|
SPStreamEvent newLoginTwoPasswordsRequestedEvent(QString const &username); ///< Create a new LoginTwoPasswordsRequestedEvent event.
|
||||||
SPStreamEvent newLoginFinishedEvent(QString const &userID, bool wasSignedOut); ///< Create a new LoginFinishedEvent event.
|
SPStreamEvent newLoginFinishedEvent(QString const &userID, bool wasSignedOut); ///< Create a new LoginFinishedEvent event.
|
||||||
SPStreamEvent newLoginAlreadyLoggedInEvent(QString const &userID); ///< Create a new LoginAlreadyLoggedInEvent event.
|
SPStreamEvent newLoginAlreadyLoggedInEvent(QString const &userID); ///< Create a new LoginAlreadyLoggedInEvent event.
|
||||||
|
|
||||||
|
|||||||
@ -1212,7 +1212,7 @@ void GRPCClient::processLoginEvent(LoginEvent const &event) {
|
|||||||
break;
|
break;
|
||||||
case LoginEvent::kTwoPasswordRequested:
|
case LoginEvent::kTwoPasswordRequested:
|
||||||
this->logTrace("Login event received: TwoPasswordRequested.");
|
this->logTrace("Login event received: TwoPasswordRequested.");
|
||||||
emit login2PasswordRequested();
|
emit login2PasswordRequested(QString::fromStdString(event.twopasswordrequested().username()));
|
||||||
break;
|
break;
|
||||||
case LoginEvent::kFinished: {
|
case LoginEvent::kFinished: {
|
||||||
this->logTrace("Login event received: Finished.");
|
this->logTrace("Login event received: Finished.");
|
||||||
|
|||||||
@ -146,10 +146,10 @@ signals:
|
|||||||
void loginUsernamePasswordError(QString const &errMsg);
|
void loginUsernamePasswordError(QString const &errMsg);
|
||||||
void loginFreeUserError();
|
void loginFreeUserError();
|
||||||
void loginConnectionError(QString const &errMsg);
|
void loginConnectionError(QString const &errMsg);
|
||||||
void login2FARequested(QString const &userName);
|
void login2FARequested(QString const &username);
|
||||||
void login2FAError(QString const &errMsg);
|
void login2FAError(QString const &errMsg);
|
||||||
void login2FAErrorAbort(QString const &errMsg);
|
void login2FAErrorAbort(QString const &errMsg);
|
||||||
void login2PasswordRequested();
|
void login2PasswordRequested(QString const &username);
|
||||||
void login2PasswordError(QString const &errMsg);
|
void login2PasswordError(QString const &errMsg);
|
||||||
void login2PasswordErrorAbort(QString const &errMsg);
|
void login2PasswordErrorAbort(QString const &errMsg);
|
||||||
void loginFinished(QString const &userID, bool wasSignedOut);
|
void loginFinished(QString const &userID, bool wasSignedOut);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -319,7 +319,9 @@ message LoginTfaRequestedEvent {
|
|||||||
string username = 1;
|
string username = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LoginTwoPasswordsRequestedEvent {}
|
message LoginTwoPasswordsRequestedEvent {
|
||||||
|
string username = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message LoginFinishedEvent {
|
message LoginFinishedEvent {
|
||||||
string userID = 1;
|
string userID = 1;
|
||||||
|
|||||||
@ -69,8 +69,8 @@ func NewLoginTfaRequestedEvent(username string) *StreamEvent {
|
|||||||
return loginEvent(&LoginEvent{Event: &LoginEvent_TfaRequested{TfaRequested: &LoginTfaRequestedEvent{Username: username}}})
|
return loginEvent(&LoginEvent{Event: &LoginEvent_TfaRequested{TfaRequested: &LoginTfaRequestedEvent{Username: username}}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLoginTwoPasswordsRequestedEvent() *StreamEvent {
|
func NewLoginTwoPasswordsRequestedEvent(username string) *StreamEvent {
|
||||||
return loginEvent(&LoginEvent{Event: &LoginEvent_TwoPasswordRequested{}})
|
return loginEvent(&LoginEvent{Event: &LoginEvent_TwoPasswordRequested{TwoPasswordRequested: &LoginTwoPasswordsRequestedEvent{Username: username}}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLoginFinishedEvent(userID string, wasSignedOut bool) *StreamEvent {
|
func NewLoginFinishedEvent(userID string, wasSignedOut bool) *StreamEvent {
|
||||||
|
|||||||
@ -424,7 +424,7 @@ func (s *Service) Login(_ context.Context, login *LoginRequest) (*emptypb.Empty,
|
|||||||
_ = s.SendEvent(NewLoginTfaRequestedEvent(login.Username))
|
_ = s.SendEvent(NewLoginTfaRequestedEvent(login.Username))
|
||||||
|
|
||||||
case auth.PasswordMode == proton.TwoPasswordMode:
|
case auth.PasswordMode == proton.TwoPasswordMode:
|
||||||
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent())
|
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent(login.Username))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
s.finishLogin()
|
s.finishLogin()
|
||||||
@ -469,7 +469,7 @@ func (s *Service) Login2FA(_ context.Context, login *LoginRequest) (*emptypb.Emp
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.auth.PasswordMode == proton.TwoPasswordMode {
|
if s.auth.PasswordMode == proton.TwoPasswordMode {
|
||||||
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent())
|
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent(login.Username))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -126,7 +126,7 @@ func (s *Service) StartEventTest() error {
|
|||||||
// login
|
// login
|
||||||
NewLoginError(LoginErrorType_FREE_USER, "error"),
|
NewLoginError(LoginErrorType_FREE_USER, "error"),
|
||||||
NewLoginTfaRequestedEvent(dummyAddress),
|
NewLoginTfaRequestedEvent(dummyAddress),
|
||||||
NewLoginTwoPasswordsRequestedEvent(),
|
NewLoginTwoPasswordsRequestedEvent(dummyAddress),
|
||||||
NewLoginFinishedEvent("userID", false),
|
NewLoginFinishedEvent("userID", false),
|
||||||
NewLoginAlreadyLoggedInEvent("userID"),
|
NewLoginAlreadyLoggedInEvent("userID"),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user