feat(GODT-2772): new login layout.

This commit is contained in:
Xavier Michelon
2023-08-21 13:34:16 +02:00
parent 81afc5fb1f
commit 6e86c95640
14 changed files with 920 additions and 882 deletions

View File

@ -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;
} }

View File

@ -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.

View File

@ -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() {

View File

@ -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,6 +122,7 @@ FocusScope {
target: Backend target: Backend
} }
Item {
ColumnLayout { ColumnLayout {
id: loginLayout id: loginLayout
function reset(clearUsername = false) { function reset(clearUsername = false) {
@ -142,26 +141,35 @@ FocusScope {
passwordTextField.text = ""; passwordTextField.text = "";
} }
spacing: 0 anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 16
ColumnLayout {
Layout.fillWidth: true
spacing: 8
Label { Label {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: qsTr("Sign in") text: qsTr("Sign in")
type: Label.LabelType.Heading type: Label.LabelType.Title
} }
Label { Label {
id: subTitle
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 8 Layout.fillWidth: true
color: wizard.colorScheme.text_weak color: wizard.colorScheme.text_weak
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: qsTr("Enter your Proton Account details.") text: qsTr("Enter your Proton Account details.")
type: Label.LabelType.Lead type: Label.LabelType.Body
}
} }
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 48
spacing: 0 spacing: 0
visible: errorLabel.text.length > 0 visible: errorLabel.text.length > 0
@ -184,7 +192,6 @@ FocusScope {
TextField { TextField {
id: usernameTextField id: usernameTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 48
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
focus: true focus: true
label: qsTr("Email or username") label: qsTr("Email or username")
@ -208,7 +215,6 @@ FocusScope {
TextField { TextField {
id: passwordTextField id: passwordTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 48
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
echoMode: TextInput.Password echoMode: TextInput.Password
label: qsTr("Password") label: qsTr("Password")
@ -244,7 +250,6 @@ FocusScope {
} }
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 48
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
enabled: !loading enabled: !loading
text: loading ? qsTr("Signing in") : qsTr("Sign in") text: loading ? qsTr("Signing in") : qsTr("Sign in")
@ -255,7 +260,6 @@ FocusScope {
} }
Button { Button {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
enabled: !signInButton.loading enabled: !signInButton.loading
secondary: true secondary: true
@ -265,7 +269,15 @@ FocusScope {
root.abort(); 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 { ColumnLayout {
id: totpLayout id: totpLayout
function reset() { function reset() {
@ -276,27 +288,47 @@ FocusScope {
twoFactorPasswordTextField.text = ""; twoFactorPasswordTextField.text = "";
} }
spacing: 0 anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 16
ColumnLayout {
Layout.fillWidth: true
spacing: 8
Label { Label {
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: qsTr("Two-factor authentication") text: qsTr("Two-factor authentication")
type: Label.LabelType.Heading type: Label.LabelType.Title
} }
Label { Label {
id: twoFactorUsernameLabel id: twoFactorUsernameLabel
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 8 Layout.fillWidth: true
color: wizard.colorScheme.text_weak color: wizard.colorScheme.text_weak
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
type: Label.LabelType.Lead horizontalAlignment: Text.AlignHCenter
text: ""
type: Label.LabelType.Body
}
}
Label {
id: descriptionLabel
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
colorScheme: wizard.colorScheme
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
} }
TextField { TextField {
id: twoFactorPasswordTextField id: twoFactorPasswordTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32
assistiveText: qsTr("Enter the 6-digit code")
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
label: qsTr("Two-factor code") label: qsTr("Two-factor code")
validateOnEditingFinished: false validateOnEditingFinished: false
@ -318,7 +350,6 @@ FocusScope {
Button { Button {
id: twoFAButton id: twoFAButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 48
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
enabled: !loading enabled: !loading
text: loading ? qsTr("Authenticating") : qsTr("Authenticate") text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
@ -335,7 +366,6 @@ FocusScope {
} }
Button { Button {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
enabled: !twoFAButton.loading enabled: !twoFAButton.loading
secondary: true secondary: true
@ -346,6 +376,8 @@ FocusScope {
} }
} }
} }
}
Item {
ColumnLayout { ColumnLayout {
id: mailboxPasswordLayout id: mailboxPasswordLayout
function reset() { function reset() {
@ -356,25 +388,46 @@ FocusScope {
secondPasswordTextField.text = ""; secondPasswordTextField.text = "";
} }
spacing: 0 anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 16
ColumnLayout {
Layout.fillWidth: true
spacing: 8
Label { Label {
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: qsTr("Unlock your mailbox") text: qsTr("Unlock your mailbox")
type: Label.LabelType.Heading type: Label.LabelType.Title
} }
Label { Label {
Layout.alignment: Qt.AlignCenter id: mailboxPasswordUsernameLabel
Layout.topMargin: 8 Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
color: wizard.colorScheme.text_weak color: wizard.colorScheme.text_weak
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
type: Label.LabelType.Lead horizontalAlignment: Text.AlignHCenter
text: ""
type: Label.LabelType.Body
}
}
Label {
Layout.alignment: Qt.AlignHCenter
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 { TextField {
id: secondPasswordTextField id: secondPasswordTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 48
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
echoMode: TextInput.Password echoMode: TextInput.Password
label: qsTr("Mailbox password") label: qsTr("Mailbox password")
@ -392,7 +445,6 @@ FocusScope {
Button { Button {
id: secondPasswordButton id: secondPasswordButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 48
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
enabled: !loading enabled: !loading
text: loading ? qsTr("Unlocking") : qsTr("Unlock") text: loading ? qsTr("Unlocking") : qsTr("Unlock")
@ -409,7 +461,6 @@ FocusScope {
} }
Button { Button {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32
colorScheme: wizard.colorScheme colorScheme: wizard.colorScheme
enabled: !secondPasswordButton.loading enabled: !secondPasswordButton.loading
secondary: true secondary: true
@ -421,4 +472,5 @@ FocusScope {
} }
} }
} }
}
} }

View File

@ -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) {

View File

@ -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);

View File

@ -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.

View File

@ -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.");

View File

@ -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

View File

@ -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;

View File

@ -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 {

View File

@ -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
} }

View File

@ -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"),