From ca5f7ce9f605644682b7de8a5470fa4c37011eee Mon Sep 17 00:00:00 2001 From: Xavier Michelon Date: Thu, 10 Aug 2023 15:01:32 +0200 Subject: [PATCH] feat(GODT-2767): connected existing entrypoints to wizard, and moved it to a stack layout. [skip-ci] --- .../bridge-gui/bridge-gui/Resources.qrc | 3 - .../bridge-gui/bridge-gui/qml/AccountView.qml | 10 +- .../bridge-gui/qml/ContentWrapper.qml | 57 +-- .../bridge-gui/bridge-gui/qml/MainWindow.qml | 116 +++-- .../bridge-gui/bridge-gui/qml/SetupGuide.qml | 293 ------------- .../qml/SetupWizard/SetupWizard.qml | 7 +- .../bridge-gui/bridge-gui/qml/SignIn.qml | 413 ------------------ .../bridge-gui/qml/WelcomeGuide.qml | 245 ----------- 8 files changed, 69 insertions(+), 1075 deletions(-) delete mode 100644 internal/frontend/bridge-gui/bridge-gui/qml/SetupGuide.qml delete mode 100644 internal/frontend/bridge-gui/bridge-gui/qml/SignIn.qml delete mode 100644 internal/frontend/bridge-gui/bridge-gui/qml/WelcomeGuide.qml diff --git a/internal/frontend/bridge-gui/bridge-gui/Resources.qrc b/internal/frontend/bridge-gui/bridge-gui/Resources.qrc index d3651c83..e2c6df5a 100644 --- a/internal/frontend/bridge-gui/bridge-gui/Resources.qrc +++ b/internal/frontend/bridge-gui/bridge-gui/Resources.qrc @@ -106,7 +106,6 @@ qml/Resources/bug_report_flow.json qml/SettingsItem.qml qml/SettingsView.qml - qml/SetupGuide.qml qml/SetupWizard/ClientListItem.qml qml/SetupWizard/LeftPane.qml qml/SetupWizard/ClientConfigOutlookSelector.qml @@ -117,11 +116,9 @@ qml/SetupWizard/Login.qml qml/SetupWizard/Onboarding.qml qml/SetupWizard/StepDescriptionBox.qml - qml/SignIn.qml qml/ConnectionModeSettings.qml qml/SplashScreen.qml qml/Status.qml - qml/WelcomeGuide.qml qml/WebViewWindow.qml diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/AccountView.qml b/internal/frontend/bridge-gui/bridge-gui/qml/AccountView.qml index ff196d43..f3a6b70a 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/AccountView.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/AccountView.qml @@ -29,7 +29,7 @@ Item { property var user signal showSetupGuide(var user, string address) - signal showSignIn + signal showSignIn(var username) Rectangle { anchors.fill: parent @@ -92,9 +92,9 @@ Item { visible: root.user ? (root.user.state === EUserState.SignedOut) : false onClicked: { - if (!root.user) - return; - root.showSignIn(); + if (user) { + root.showSignIn(user.primaryEmailOrUsername()); + } } } Button { @@ -124,7 +124,7 @@ Item { showSeparator: splitMode.visible text: qsTr("Email clients") type: SettingsItem.Button - visible: _connected && (!root.user.splitMode) || (root.user.addresses.length === 1) + visible: _connected && ((!root.user.splitMode) || (root.user.addresses.length === 1)) onClicked: { if (!root.user) diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/ContentWrapper.qml b/internal/frontend/bridge-gui/bridge-gui/qml/ContentWrapper.qml index 55e8ee51..ed24436e 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/ContentWrapper.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/ContentWrapper.qml @@ -25,7 +25,8 @@ Item { signal closeWindow signal quitBridge signal showSetupGuide(var user, string address) - signal showSetupWizard + signal showSignIn(var username) + signal showSetupWizard() function selectUser(userID) { const users = Backend.users; @@ -50,10 +51,6 @@ Item { function showSettings() { rightContent.showGeneralSettings(); } - function showSignIn(username) { - signIn.username = username; - rightContent.showSignIn(); - } RowLayout { anchors.fill: parent @@ -234,8 +231,7 @@ Item { if (user.state !== EUserState.SignedOut) { rightContent.showAccount(); } else { - signIn.username = user.primaryEmailOrUsername(); - rightContent.showSignIn(); + showSignIn(user.primaryEmailOrUsername()); } } } @@ -283,8 +279,7 @@ Item { width: 36 onClicked: { - signIn.username = ""; - root.showSetupWizard(); + root.showSignIn("") } } } @@ -324,10 +319,6 @@ Item { function showPortSettings() { rightContent.currentIndex = 4; } - function showSignIn() { - rightContent.currentIndex = 1; - signIn.focus = true; - } anchors.fill: parent @@ -346,42 +337,14 @@ Item { onShowSetupGuide: function (user, address) { root.showSetupGuide(user, address); } - onShowSignIn: { - const user = this.user; - signIn.username = user ? user.primaryEmailOrUsername() : ""; - rightContent.showSignIn(); + onShowSignIn: function (username) { + root.showSignIn(username) } } - GridLayout { - // 1 Sign In - columns: 2 - - Button { - id: backButton - Layout.alignment: Qt.AlignTop - Layout.leftMargin: 18 - Layout.topMargin: 10 - colorScheme: root.colorScheme - horizontalPadding: 8 - icon.source: "/qml/icons/ic-arrow-left.svg" - secondary: true - - onClicked: { - signIn.abort(); - rightContent.showAccount(); - } - } - SignIn { - id: signIn - Layout.bottomMargin: 68 - Layout.fillHeight: true - Layout.fillWidth: true - Layout.leftMargin: 80 - backButton.width - 18 - Layout.preferredWidth: 320 - Layout.rightMargin: 80 - Layout.topMargin: 68 - colorScheme: root.colorScheme - } + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: "#ff9900" } GeneralSettings { // 2 diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml b/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml index 7fb46a0e..309f6906 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml @@ -46,21 +46,38 @@ ApplicationWindow { contentWrapper.showSettings(); } function showSetup(user, address) { - setupGuide.user = user; - setupGuide.address = address; - setupGuide.reset(); - contentLayout._showSetup = !!setupGuide.user; + contentLayout.currentIndex = 1; + setupWizard.startClientCOnfig(user, address) } function showSignIn(username) { - if (contentLayout.currentIndex === 1) - return; - contentWrapper.showSignIn(username); + contentLayout.currentIndex = 1; + setupWizard.startLogin(username) } + function showWebViewOverlay(url) { webViewOverlay.visible = true; webViewOverlay.url = url; } + function layoutForUserCount(userCount) { + if (userCount === 0) { + showSignIn(""); + return; + } + + const u = Backend.users.get(0); + if (!u) { + console.trace(); + console.log("empty user"); + setupWizard.start(); + return; + } + + if ((userCount === 1) && (u.state === EUserState.SignedOut)) { + setupWizard.startLogin(u.primaryEmailOrUsername()); + } + } + colorScheme: ProtonStyle.currentStyle height: _defaultHeight minimumWidth: _defaultWidth @@ -72,10 +89,8 @@ ApplicationWindow { function onRowsAboutToBeRemoved(parent, first, last) { for (let i = first; i <= last; i++) { const user = Backend.users.get(i); - if (setupGuide.user === user) { - setupGuide.user = null; - contentLayout._showSetup = false; - return; + if (setupWizard.user === user) { + setupWizard.closeWizard(); } } } @@ -94,13 +109,6 @@ ApplicationWindow { target: Backend.users } Connections { - function onLoginFinished(index, wasSignedOut) { - // const user = Backend.users.get(index); - // if (user && !wasSignedOut) { - // root.showSetup(user, user.addresses[0]); - // } - // console.debug("Login finished", index); - } function onSelectUser(userID, forceShowWindow) { contentWrapper.selectUser(userID); if (forceShowWindow) { @@ -121,33 +129,19 @@ ApplicationWindow { target: Backend } + + Connections { + function onCountChanged(count) { + layoutForUserCount(count) + } + + target: Backend.users + } StackLayout { id: contentLayout - property bool _showSetup: false - anchors.fill: parent - currentIndex: { - // show welcome when there are no users - if (Backend.users.count === 0) { - setupWizard.start(); - return 0; - } - const u = Backend.users.get(0); - if (!u) { - console.trace(); - console.log("empty user"); - return 1; - } - if ((Backend.users.count === 1) && (u.state === EUserState.SignedOut)) { - showSignIn(u.primaryEmailOrUsername()); - return 0; - } - if (contentLayout._showSetup) { - return 2; - } - return 0; - } + currentIndex: 0 ContentWrapper { // 0 @@ -169,33 +163,23 @@ ApplicationWindow { onShowSetupGuide: function (user, address) { setupWizard.startClientConfig(user, address); } - onShowSetupWizard: { - setupWizard.start(); + onShowSignIn: function(username) { + root.showSignIn(username) } } - WelcomeGuide { - Layout.fillHeight: true - Layout.fillWidth: true // 1 - colorScheme: root.colorScheme - } - SetupGuide { - // 2 - id: setupGuide - Layout.fillHeight: true - Layout.fillWidth: true + + SetupWizard { + id: setupWizard + Layout.fillWidth: true; + Layout.fillHeight: true; colorScheme: root.colorScheme - onDismissed: { - root.showSetup(null, ""); - } - onFinished: { - // TODO: Do not close window. Trigger Backend to check that - // there is a successfully connected client. Then Backend - // should send another signal to close the setup guide. - root.showSetup(null, ""); + onWizardEnded: { + contentLayout.currentIndex = 0 } } } + WebView { id: webViewOverlay anchors.fill: parent @@ -204,12 +188,6 @@ ApplicationWindow { url: "" visible: false } - SetupWizard { - id: setupWizard - anchors.fill: parent - colorScheme: root.colorScheme - visible: false - } NotificationPopups { colorScheme: root.colorScheme mainWindow: root @@ -219,4 +197,8 @@ ApplicationWindow { id: splashScreen colorScheme: root.colorScheme } + + Component.onCompleted: { + layoutForUserCount(Backend.users.count) + } } diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SetupGuide.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SetupGuide.qml deleted file mode 100644 index 4cdfe43f..00000000 --- a/internal/frontend/bridge-gui/bridge-gui/qml/SetupGuide.qml +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright (c) 2023 Proton AG -// This file is part of Proton Mail Bridge. -// Proton Mail 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. -// Proton Mail 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 Proton Mail Bridge. If not, see . -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import QtQuick.Controls.impl -import Proton - -Item { - id: root - - property string address - property ColorScheme colorScheme - property var user - - signal dismissed - signal finished - - function reset() { - guidePages.currentIndex = 0; - clientList.currentIndex = -1; - actionList.currentIndex = -1; - } - function setupAction(actionID, clientID) { - if (user) { - user.setupGuideSeen = true; - } - switch (actionID) { - case -1: - root.dismissed(); - break; // dismiss - case 0 // automatic - : - if (user) { - switch (clientID) { - case 0: - root.user.configureAppleMail(root.address); - Backend.notifyAutoconfigClicked("AppleMail"); - break; - } - } - root.finished(); - break; - case 1 // manual - : - let clientObj = clients.get(clientID); - if (clientObj !== undefined && clientObj.link !== "") { - Qt.openUrlExternally(clientObj.link); - Backend.notifyKBArticleClicked(clientObj.link); - } else { - console.log("unexpected client index", actionID, clientID); - } - root.finished(); - break; - default: - console.log("unexpected client setup action", actionID, clientID); - } - } - - implicitHeight: children[0].implicitHeight - implicitWidth: children[0].implicitWidth - - ListModel { - id: clients - - property bool haveAutoSetup: true - property string iconSource: "/qml/icons/ic-apple-mail.svg" - property string link: "https://proton.me/support/protonmail-bridge-clients-apple-mail" - property string name: "Apple Mail" - - Component.onCompleted: { - if (Backend.goos === "darwin") { - append({ - "name": "Apple Mail", - "iconSource": "/qml/icons/ic-apple-mail.svg", - "haveAutoSetup": true, - "link": "https://proton.me/support/protonmail-bridge-clients-apple-mail" - }); - append({ - "name": "Microsoft Outlook", - "iconSource": "/qml/icons/ic-microsoft-outlook.svg", - "haveAutoSetup": false, - "link": "https://proton.me/support/protonmail-bridge-clients-macos-outlook-2019" - }); - } - if (Backend.goos === "windows") { - append({ - "name": "Microsoft Outlook", - "iconSource": "/qml/icons/ic-microsoft-outlook.svg", - "haveAutoSetup": false, - "link": "https://proton.me/support/protonmail-bridge-clients-windows-outlook-2019" - }); - } - append({ - "name": "Mozilla Thunderbird", - "iconSource": "/qml/icons/ic-mozilla-thunderbird.svg", - "haveAutoSetup": false, - "link": "https://proton.me/support/protonmail-bridge-clients-windows-thunderbird" - }); - append({ - "name": "Other", - "iconSource": "/qml/icons/ic-other-mail-clients.svg", - "haveAutoSetup": false, - "link": "https://proton.me/support/protonmail-bridge-configure-client" - }); - } - } - Rectangle { - anchors.fill: root - color: root.colorScheme.background_norm - } - StackLayout { - id: guidePages - anchors.bottomMargin: 70 - anchors.fill: parent - anchors.leftMargin: 80 - anchors.rightMargin: 80 - anchors.topMargin: 30 - - ColumnLayout { - // 0: Client selection - id: clientView - - property int columnWidth: 268 - - Layout.fillHeight: true - spacing: 8 - - Label { - colorScheme: root.colorScheme - text: qsTr("Setting up email client") - type: Label.LabelType.Heading - } - Label { - color: root.colorScheme.text_weak - colorScheme: root.colorScheme - text: address - type: Label.LabelType.Lead - } - RowLayout { - Layout.topMargin: 32 - clientView.spacing - spacing: 24 - - ColumnLayout { - id: clientColumn - Layout.alignment: Qt.AlignTop - - Label { - id: labelA - colorScheme: root.colorScheme - text: qsTr("Choose an email client") - type: Label.LabelType.Body_semibold - } - ListView { - id: clientList - Layout.fillHeight: true - model: clients - width: clientView.columnWidth - - delegate: Item { - implicitHeight: clientRow.height - implicitWidth: clientRow.width - - ColumnLayout { - id: clientRow - width: clientList.width - - RowLayout { - Layout.bottomMargin: 12 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - Layout.topMargin: 12 - - ColorImage { - height: 36 - source: model.iconSource - sourceSize.height: 36 - } - Label { - Layout.leftMargin: 12 - colorScheme: root.colorScheme - text: model.name - type: Label.LabelType.Body - } - } - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 1 - color: root.colorScheme.border_weak - } - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - - onClicked: { - clientList.currentIndex = index; - if (!model.haveAutoSetup) { - root.setupAction(1, index); - } - } - } - } - highlight: Rectangle { - color: root.colorScheme.interaction_default_active - radius: ProtonStyle.context_item_radius - } - } - } - ColumnLayout { - id: actionColumn - Layout.alignment: Qt.AlignTop - visible: clientList.currentIndex >= 0 && clients.get(clientList.currentIndex).haveAutoSetup - - Label { - colorScheme: root.colorScheme - text: qsTr("Choose configuration mode") - type: Label.LabelType.Body_semibold - } - ListView { - id: actionList - Layout.fillHeight: true - model: [qsTr("Configure automatically"), qsTr("Configure manually")] - width: clientView.columnWidth - - delegate: Item { - implicitHeight: children[0].height - implicitWidth: children[0].width - - ColumnLayout { - width: actionList.width - - Label { - Layout.bottomMargin: 20 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - Layout.topMargin: 20 - colorScheme: root.colorScheme - text: modelData - type: Label.LabelType.Body - } - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 1 - color: root.colorScheme.border_weak - } - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - - onClicked: { - actionList.currentIndex = index; - root.setupAction(index, clientList.currentIndex); - } - } - } - highlight: Rectangle { - color: root.colorScheme.interaction_default_active - radius: ProtonStyle.context_item_radius - } - } - } - } - Item { - Layout.fillHeight: true - } - Button { - colorScheme: root.colorScheme - flat: true - text: qsTr("Set up later") - - onClicked: { - root.setupAction(-1, -1); - if (user) { - user.setupGuideSeen = true; - } - root.dismissed(); - } - } - } - } -} diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/SetupWizard.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/SetupWizard.qml index 7d9bf352..36391627 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/SetupWizard.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/SetupWizard.qml @@ -33,6 +33,8 @@ Item { property var user property string address + signal wizardEnded() + function clientIconSource() { switch (client) { case SetupWizard.Client.AppleMail: @@ -66,7 +68,7 @@ Item { } function closeWizard() { - root.visible = false; + wizardEnded() } function showOutlookSelector() { @@ -92,13 +94,14 @@ Item { rightContent.currentIndex = 2; } - function startLogin() { + function startLogin(username = "") { root.visible = true; rootStackLayout.currentIndex = 0; root.address = ""; leftContent.showLogin(); rightContent.currentIndex = 1; login.reset(true); + login.username = username; } function showClientWarning() { diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SignIn.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SignIn.qml deleted file mode 100644 index 35278df0..00000000 --- a/internal/frontend/bridge-gui/bridge-gui/qml/SignIn.qml +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright (c) 2023 Proton AG -// This file is part of Proton Mail Bridge. -// Proton Mail 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. -// Proton Mail 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 Proton Mail Bridge. If not, see . -import QtQml -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import QtQuick.Controls.impl -import Proton - -FocusScope { - id: root - - property ColorScheme colorScheme - property alias currentIndex: stackLayout.currentIndex - property alias username: usernameTextField.text - - function abort() { - root.reset(); - Backend.loginAbort(usernameTextField.text); - } - function reset() { - stackLayout.currentIndex = 0; - loginNormalLayout.reset(); - login2FALayout.reset(); - login2PasswordLayout.reset(); - } - - implicitHeight: children[0].implicitHeight - implicitWidth: children[0].implicitWidth - state: "Page 1" - - states: [ - State { - name: "Page 1" - - PropertyChanges { - currentIndex: 0 - target: stackLayout - } - }, - State { - name: "Page 2" - - PropertyChanges { - currentIndex: 1 - target: stackLayout - } - }, - State { - name: "Page 3" - - PropertyChanges { - currentIndex: 2 - target: stackLayout - } - } - ] - - StackLayout { - id: stackLayout - function loginFailed() { - signInButton.loading = false; - usernameTextField.enabled = true; - usernameTextField.error = true; - passwordTextField.enabled = true; - passwordTextField.error = true; - } - - anchors.fill: parent - - Connections { - function onLogin2FAError(_) { - console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAError"); - twoFAButton.loading = false; - twoFactorPasswordTextField.enabled = true; - twoFactorPasswordTextField.error = true; - twoFactorPasswordTextField.errorString = qsTr("Your code is incorrect"); - twoFactorPasswordTextField.focus = true; - } - function onLogin2FAErrorAbort(_) { - console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAErrorAbort"); - root.reset(); - errorLabel.text = qsTr("Incorrect login credentials. Please try again."); - } - function onLogin2FARequested(username) { - console.assert(stackLayout.currentIndex === 0, "Unexpected login2FARequested"); - twoFactorUsernameLabel.text = username; - stackLayout.currentIndex = 1; - twoFactorPasswordTextField.focus = true; - } - function onLogin2PasswordError(_) { - console.assert(stackLayout.currentIndex === 2, "Unexpected login2PasswordError"); - secondPasswordButton.loading = false; - secondPasswordTextField.enabled = true; - secondPasswordTextField.error = true; - secondPasswordTextField.errorString = qsTr("Your mailbox password is incorrect"); - secondPasswordTextField.focus = true; - } - function onLogin2PasswordErrorAbort(_) { - console.assert(stackLayout.currentIndex === 2, "Unexpected login2PasswordErrorAbort"); - root.reset(); - errorLabel.text = qsTr("Incorrect login credentials. Please try again."); - } - function onLogin2PasswordRequested() { - console.assert(stackLayout.currentIndex === 0 || stackLayout.currentIndex === 1, "Unexpected login2PasswordRequested"); - stackLayout.currentIndex = 2; - secondPasswordTextField.focus = true; - } - function onLoginAlreadyLoggedIn(_) { - stackLayout.currentIndex = 0; - root.reset(); - } - function onLoginConnectionError(_) { - if (stackLayout.currentIndex === 0) { - stackLayout.loginFailed(); - } - } - function onLoginFinished(_) { - stackLayout.currentIndex = 0; - root.reset(); - } - function onLoginFreeUserError() { - console.assert(stackLayout.currentIndex === 0, "Unexpected loginFreeUserError"); - stackLayout.loginFailed(); - } - function onLoginUsernamePasswordError(errorMsg) { - console.assert(stackLayout.currentIndex === 0, "Unexpected loginUsernamePasswordError"); - stackLayout.loginFailed(); - if (errorMsg !== "") - errorLabel.text = errorMsg; - else - errorLabel.text = qsTr("Incorrect login credentials"); - } - - target: Backend - } - ColumnLayout { - id: loginNormalLayout - function reset() { - signInButton.loading = false; - errorLabel.text = ""; - usernameTextField.enabled = true; - usernameTextField.error = false; - usernameTextField.errorString = ""; - usernameTextField.focus = true; - passwordTextField.enabled = true; - passwordTextField.error = false; - passwordTextField.errorString = ""; - passwordTextField.text = ""; - } - - spacing: 0 - - Label { - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: 16 - colorScheme: root.colorScheme - text: qsTr("Sign in") - type: Label.LabelType.Title - } - Label { - id: subTitle - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: 8 - color: root.colorScheme.text_weak - colorScheme: root.colorScheme - text: qsTr("Enter your Proton Account details.") - type: Label.LabelType.Body - } - RowLayout { - Layout.fillWidth: true - Layout.topMargin: 36 - spacing: 0 - visible: errorLabel.text.length > 0 - - ColorImage { - color: root.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: root.colorScheme.signal_danger - colorScheme: root.colorScheme - type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption - wrapMode: Text.WordWrap - } - } - TextField { - id: usernameTextField - Layout.fillWidth: true - Layout.topMargin: 24 - colorScheme: root.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 - Layout.topMargin: 8 - colorScheme: root.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: 24 - colorScheme: root.colorScheme - enabled: !loading - text: loading ? qsTr("Signing in") : qsTr("Sign in") - - onClicked: { - checkAndSignIn(); - } - } - Label { - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: 24 - colorScheme: root.colorScheme - text: link("https://proton.me/mail/pricing", qsTr("Create or upgrade your account")) - textFormat: Text.StyledText - type: Label.LabelType.Body - - onLinkActivated: { - Qt.openUrlExternally(link); - } - } - } - ColumnLayout { - id: login2FALayout - function reset() { - twoFAButton.loading = false; - twoFactorPasswordTextField.enabled = true; - twoFactorPasswordTextField.error = false; - twoFactorPasswordTextField.errorString = ""; - twoFactorPasswordTextField.text = ""; - } - - spacing: 0 - - Label { - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 16 - colorScheme: root.colorScheme - text: qsTr("Two-factor authentication") - type: Label.LabelType.Heading - } - Label { - id: twoFactorUsernameLabel - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 8 - color: root.colorScheme.text_weak - colorScheme: root.colorScheme - type: Label.LabelType.Lead - } - TextField { - id: twoFactorPasswordTextField - Layout.fillWidth: true - Layout.topMargin: 32 - assistiveText: qsTr("Enter the 6-digit code") - colorScheme: root.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(); - } - } - } - Button { - id: twoFAButton - Layout.fillWidth: true - Layout.topMargin: 24 - colorScheme: root.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)); - } - } - } - ColumnLayout { - id: login2PasswordLayout - function reset() { - secondPasswordButton.loading = false; - secondPasswordTextField.enabled = true; - secondPasswordTextField.error = false; - secondPasswordTextField.errorString = ""; - secondPasswordTextField.text = ""; - } - - spacing: 0 - - Label { - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 16 - colorScheme: root.colorScheme - text: qsTr("Unlock your mailbox") - type: Label.LabelType.Heading - } - TextField { - id: secondPasswordTextField - Layout.fillWidth: true - Layout.topMargin: 8 + implicitHeight + 24 + subTitle.implicitHeight - colorScheme: root.colorScheme - echoMode: TextInput.Password - label: qsTr("Mailbox password") - validateOnEditingFinished: false - validator: function (str) { - if (str.length === 0) { - return qsTr("Enter password"); - } - } - - onAccepted: { - secondPasswordButton.onClicked(); - } - } - Button { - id: secondPasswordButton - Layout.fillWidth: true - Layout.topMargin: 24 - colorScheme: root.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)); - } - } - } - } -} diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/WelcomeGuide.qml b/internal/frontend/bridge-gui/bridge-gui/qml/WelcomeGuide.qml deleted file mode 100644 index 94df5dec..00000000 --- a/internal/frontend/bridge-gui/bridge-gui/qml/WelcomeGuide.qml +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright (c) 2023 Proton AG -// This file is part of Proton Mail Bridge. -// Proton Mail 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. -// Proton Mail 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 Proton Mail Bridge. If not, see . -import QtQml -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import Proton - -Item { - id: root - - property ColorScheme colorScheme - - implicitHeight: children[0].implicitHeight - implicitWidth: children[0].implicitWidth - - RowLayout { - anchors.fill: parent - spacing: 0 - - states: [ - State { - name: "Page 1" - - PropertyChanges { - currentIndex: 0 - target: signInItem - } - }, - State { - name: "Page 2" - - PropertyChanges { - currentIndex: 1 - target: signInItem - } - }, - State { - name: "Page 3" - - PropertyChanges { - currentIndex: 2 - target: signInItem - } - } - ] - - Rectangle { - Layout.fillHeight: true - Layout.fillWidth: true - color: root.colorScheme.background_norm - implicitHeight: children[0].implicitHeight - implicitWidth: children[0].implicitWidth - visible: signInItem.currentIndex === 0 - - GridLayout { - anchors.fill: parent - columnSpacing: 0 - columns: 3 - rowSpacing: 0 - - // top margin - Item { - Layout.columnSpan: 3 - Layout.fillWidth: true - - // Using binding component here instead of direct binding to avoid binding loop during construction of element - Binding on Layout.preferredHeight { - value: (parent.height - welcomeContentItem.height) / 4 - } - } - - // left margin - Item { - Layout.fillWidth: true - Layout.maximumWidth: 80 - Layout.minimumWidth: 48 - Layout.preferredHeight: welcomeContentItem.height - } - ColumnLayout { - id: welcomeContentItem - Layout.fillWidth: true - spacing: 0 - - Image { - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: 16 - source: "/qml/icons/img-welcome.svg" - sourceSize.height: 148 - sourceSize.width: 264 - } - Label { - Layout.alignment: Qt.AlignHCenter - Layout.fillWidth: true - Layout.topMargin: 16 - colorScheme: root.colorScheme - horizontalAlignment: Text.AlignHCenter - text: qsTr("Welcome to\nProton Mail Bridge") - type: Label.LabelType.Heading - } - Label { - id: longTextLabel - Layout.alignment: Qt.AlignHCenter - Layout.fillWidth: true - Layout.preferredWidth: 320 - Layout.topMargin: 16 - colorScheme: root.colorScheme - horizontalAlignment: Text.AlignHCenter - text: qsTr("Add your Proton Mail account to securely access and manage your messages in your favorite email client. Bridge runs in the background and encrypts and decrypts your messages seamlessly.") - type: Label.LabelType.Body - wrapMode: Text.WordWrap - } - } - - // Right margin - Item { - Layout.fillWidth: true - Layout.maximumWidth: 80 - Layout.minimumWidth: 48 - Layout.preferredHeight: welcomeContentItem.height - } - - // bottom margin - Item { - Layout.columnSpan: 3 - Layout.fillHeight: true - Layout.fillWidth: true - implicitHeight: children[0].implicitHeight + children[0].anchors.bottomMargin + children[0].anchors.topMargin - implicitWidth: children[0].implicitWidth - - Image { - id: logoImage - anchors.bottom: parent.bottom - anchors.bottomMargin: 48 - anchors.horizontalCenter: parent.horizontalCenter - anchors.topMargin: 48 - source: colorScheme.logo_img - sourceSize.height: 25 - sourceSize.width: 200 - } - } - } - } - Rectangle { - Layout.fillHeight: true - Layout.fillWidth: true - color: (signInItem.currentIndex == 0) ? root.colorScheme.background_weak : root.colorScheme.background_norm - implicitHeight: children[0].implicitHeight - implicitWidth: children[0].implicitWidth - - RowLayout { - anchors.fill: parent - spacing: 0 - - Item { - Layout.fillHeight: true - Layout.fillWidth: true - Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4 - implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin - implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin - - Button { - anchors.bottom: parent.bottom - anchors.bottomMargin: 80 - anchors.left: parent.left - anchors.leftMargin: 80 - anchors.rightMargin: 80 - anchors.topMargin: 80 - colorScheme: root.colorScheme - secondary: true - text: qsTr("Back") - visible: signInItem.currentIndex != 0 - - onClicked: { - signInItem.abort(); - } - } - } - GridLayout { - Layout.fillHeight: true - Layout.fillWidth: true - columnSpacing: 0 - columns: 3 - rowSpacing: 0 - - // top margin - Item { - Layout.columnSpan: 3 - Layout.fillWidth: true - - // Using binding component here instead of direct binding to avoid binding loop during construction of element - Binding on Layout.preferredHeight { - value: (parent.height - signInItem.height) / 4 - } - } - - // left margin - Item { - Layout.fillWidth: true - Layout.maximumWidth: 80 - Layout.minimumWidth: 48 - Layout.preferredHeight: signInItem.height - } - SignIn { - id: signInItem - Layout.fillWidth: true - Layout.preferredWidth: 320 - colorScheme: root.colorScheme - focus: true - username: Backend.users.count === 1 && Backend.users.get(0) && (Backend.users.get(0).state === EUserState.SignedOut) ? Backend.users.get(0).username : "" - } - - // Right margin - Item { - Layout.fillWidth: true - Layout.maximumWidth: 80 - Layout.minimumWidth: 48 - Layout.preferredHeight: signInItem.height - } - - // bottom margin - Item { - Layout.columnSpan: 3 - Layout.fillHeight: true - Layout.fillWidth: true - } - } - Item { - Layout.fillHeight: true - Layout.preferredWidth: signInItem.currentIndex === 0 ? 0 : parent.width / 4 - } - } - } - } -}