feat(GODT-2769): Setup Wizard QML foundations.

This commit is contained in:
Xavier Michelon
2023-08-11 11:28:54 +02:00
parent 7355c7dfd6
commit 0a51c7a6b0
10 changed files with 158 additions and 183 deletions

View File

@ -28,8 +28,8 @@ Item {
property var notifications
property var user
signal showSetupGuide(var user, string address)
signal showSignIn(var username)
signal showClientConfigurator(var user, string address)
signal showLogin(var username)
Rectangle {
anchors.fill: parent
@ -93,7 +93,7 @@ Item {
onClicked: {
if (user) {
root.showSignIn(user.primaryEmailOrUsername());
root.showLogin(user.primaryEmailOrUsername());
}
}
}
@ -129,7 +129,7 @@ Item {
onClicked: {
if (!root.user)
return;
root.showSetupGuide(root.user, user.addresses[0]);
root.showClientConfigurator(root.user, user.addresses[0]);
}
}
SettingsItem {
@ -171,7 +171,7 @@ Item {
onClicked: {
if (!root.user)
return;
root.showSetupGuide(root.user, addressSelector.displayText);
root.showClientConfigurator(root.user, addressSelector.displayText);
}
}
}

View File

@ -24,9 +24,8 @@ Item {
signal closeWindow
signal quitBridge
signal showSetupGuide(var user, string address)
signal showSignIn(var username)
signal showSetupWizard()
signal showClientConfigurator(var user, string address)
signal showLogin(var username)
function selectUser(userID) {
const users = Backend.users;
@ -37,7 +36,7 @@ Item {
}
accounts.currentIndex = i;
if (user.state === EUserState.SignedOut)
showSignIn(user.primaryEmailOrUsername());
showLogin(user.primaryEmailOrUsername());
return;
}
console.error("User with ID ", userID, " was not found in the account list");
@ -55,7 +54,6 @@ Item {
rightContent.showGeneralSettings();
}
RowLayout {
anchors.fill: parent
spacing: 0
@ -235,7 +233,7 @@ Item {
if (user.state !== EUserState.SignedOut) {
rightContent.showAccount();
} else {
showSignIn(user.primaryEmailOrUsername());
showLogin(user.primaryEmailOrUsername());
}
}
}
@ -283,7 +281,7 @@ Item {
width: 36
onClicked: {
root.showSignIn("")
root.showLogin("");
}
}
}
@ -338,16 +336,16 @@ Item {
return Backend.users.get(accounts.currentIndex);
}
onShowSetupGuide: function (user, address) {
root.showSetupGuide(user, address);
onShowClientConfigurator: function (user, address) {
root.showClientConfigurator(user, address);
}
onShowSignIn: function (username) {
root.showSignIn(username)
onShowLogin: function (username) {
root.showLogin(username);
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.fillWidth: true
color: "#ff9900"
}
GeneralSettings {

View File

@ -26,6 +26,22 @@ ApplicationWindow {
property int _defaultWidth: 1080
property var notifications
function layoutForUserCount(userCount) {
if (userCount === 0) {
showLogin();
return;
}
const u = Backend.users.get(0);
if (!u) {
console.trace();
console.log("empty user");
setupWizard.showOnboarding();
return;
}
if ((userCount === 1) && (u.state === EUserState.SignedOut)) {
setupWizard.showLogin(u.primaryEmailOrUsername());
}
}
function selectUser(userID) {
contentWrapper.selectUser(userID);
}
@ -36,54 +52,38 @@ ApplicationWindow {
root.requestActivate();
}
}
function showClientConfigurator(user, address) {
contentLayout.currentIndex = 1;
setupWizard.showClientConfig(user, address);
}
function showHelp() {
showWebViewOverlay("https://proton.me/support/bridge");
}
function showLocalCacheSettings() {
contentWrapper.showLocalCacheSettings();
}
function showLogin(username = "") {
contentLayout.currentIndex = 1;
setupWizard.showLogin(username);
}
function showSettings() {
contentWrapper.showSettings();
}
function showSetup(user, address) {
contentLayout.currentIndex = 1;
setupWizard.showClientConfig(user, address)
}
function showSignIn(username) {
contentLayout.currentIndex = 1;
setupWizard.showLogin(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.showOnboarding();
return;
}
if ((userCount === 1) && (u.state === EUserState.SignedOut)) {
setupWizard.showLogin(u.primaryEmailOrUsername());
}
}
colorScheme: ProtonStyle.currentStyle
height: _defaultHeight
minimumWidth: _defaultWidth
visible: true
width: _defaultWidth
Component.onCompleted: {
layoutForUserCount(Backend.users.count);
}
// show Setup Guide on every new user
Connections {
function onRowsAboutToBeRemoved(parent, first, last) {
@ -103,7 +103,7 @@ ApplicationWindow {
if (user.setupGuideSeen) {
return;
}
root.showSetup(user, user.addresses[0]);
root.showClientConfigurator(user, user.addresses[0]);
}
target: Backend.users
@ -129,17 +129,15 @@ ApplicationWindow {
target: Backend
}
Connections {
function onCountChanged(count) {
layoutForUserCount(count)
layoutForUserCount(count);
}
target: Backend.users
}
StackLayout {
id: contentLayout
anchors.fill: parent
currentIndex: 0
@ -160,29 +158,27 @@ ApplicationWindow {
root.close();
Backend.quit();
}
onShowSetupGuide: function (user, address) {
root.showSetup(user, address);
onShowClientConfigurator: function (user, address) {
root.showClientConfigurator(user, address);
}
onShowSignIn: function(username) {
root.showSignIn(username)
onShowLogin: function (username) {
root.showLogin(username);
}
}
SetupWizard {
id: setupWizard
Layout.fillWidth: true;
Layout.fillHeight: true;
Layout.fillHeight: true
Layout.fillWidth: true
colorScheme: root.colorScheme
onWizardEnded: {
contentLayout.currentIndex = 0
}
onShowBugReport: {
contentWrapper.showBugReport();
}
onWizardEnded: {
contentLayout.currentIndex = 0;
}
}
}
WebView {
id: webViewOverlay
anchors.fill: parent
@ -200,8 +196,4 @@ ApplicationWindow {
id: splashScreen
colorScheme: root.colorScheme
}
Component.onCompleted: {
layoutForUserCount(Backend.users.count)
}
}

View File

@ -238,12 +238,12 @@ FocusScope {
bottomPadding: 8
color: {
if (!control.enabled) {
return root.colorScheme.text_disabled
return root.colorScheme.text_disabled;
}
if (control.readOnly) {
return root.colorScheme.text_hint
return root.colorScheme.text_hint;
}
return root.colorScheme.text_norm
return root.colorScheme.text_norm;
}
// enforcing default focus here within component

View File

@ -20,24 +20,27 @@ import ".."
Rectangle {
id: root
property var wizard
color: colorScheme.background_weak
readonly property bool genericClient: SetupWizard.Client.Generic === wizard.client
property var wizard
color: colorScheme.background_weak
Item {
id: centeredContainer
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 800
ColumnLayout {
anchors.bottomMargin: 96
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottomMargin: 96
anchors.topMargin: 32
spacing: 0
Label {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
@ -55,18 +58,17 @@ Rectangle {
color: colorScheme.text_weak
colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: genericClient ? qsTr("Here are the IMAP and SMTP configuration parameters for your email client") :
qsTr("Here are your email configuration parameters for %1. \nWe have prepared an easy to follow configuration guide to help you setup your account in %1.").arg(wizard.clientName())
text: genericClient ? qsTr("Here are the IMAP and SMTP configuration parameters for your email client") : qsTr("Here are your email configuration parameters for %1. \nWe have prepared an easy to follow configuration guide to help you setup your account in %1.").arg(wizard.clientName())
type: Label.LabelType.Body
wrapMode: Text.WordWrap
}
RowLayout {
id: configuration
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: 32
spacing: 64
Configuration {
Layout.fillWidth: true
colorScheme: wizard.colorScheme
@ -88,7 +90,6 @@ Rectangle {
username: wizard.address
}
}
Button {
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 444
@ -97,17 +98,16 @@ Rectangle {
text: qsTr("Open configuration guide")
visible: !genericClient
}
Button {
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 444
Layout.topMargin: 32
colorScheme: wizard.colorScheme
text: qsTr("Done")
onClicked: wizard.closeWizard()
}
}
LinkLabel {
id: reportProblemLink
anchors.bottom: parent.bottom

View File

@ -61,8 +61,8 @@ Item {
Label {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: qsTr("Do not enter your Proton account password in you email application.")
type: Label.LabelType.Body
wrapMode: Text.WordWrap

View File

@ -20,15 +20,8 @@ import ".."
Item {
id: root
property var wizard
function showClientSelector() {
titleLabel.text = qsTr("Configure your email client");
descriptionLabel.text = qsTr("Bridge is now connected to Proton, and has already started downloading your messages. Lets now connect your email client to Bridge.");
linkLabel1.clear();
linkLabel2.clear();
icon.source = "/qml/icons/img-mail-clients.svg";
}
property var wizard
function showClientConfigCommon() {
const clientName = wizard.clientName();
@ -40,26 +33,29 @@ Item {
Layout.preferredHeight = 72;
Layout.preferredWidth = 72;
}
function showClientConfigWarning() {
showClientConfigCommon();
linkLabel1.setLink("https://proton.me/support/bridge", qsTr("Why can't I use my Proton password in my email client?"));
}
function showClientSelector() {
titleLabel.text = qsTr("Configure your email client");
descriptionLabel.text = qsTr("Bridge is now connected to Proton, and has already started downloading your messages. Lets now connect your email client to Bridge.");
linkLabel1.clear();
linkLabel2.clear();
icon.source = "/qml/icons/img-mail-clients.svg";
}
function showLogin() {
descriptionLabel.text = qsTr("Let's start by signing in to your Proton account.");
linkLabel1.setLink("https://proton.me/mail/pricing", qsTr("Create or upgrade your account"));
linkLabel2.clear();
showLoginCommon();
}
function showLogin2FA() {
descriptionLabel.text = qsTr("You have enabled two-factor authentication. Please enter the 6-digit code provided by your authenticator application.");
linkLabel1.clear();
linkLabel2.clear();
showLoginCommon();
}
function showLoginCommon() {
titleLabel.text = qsTr("Sign in to your Proton Account");
icon.Layout.preferredHeight = 72;
@ -68,14 +64,12 @@ Item {
icon.sourceSize.height = 128;
icon.sourceSize.width = 128;
}
function showLoginMailboxPassword() {
descriptionLabel.text = qsTr("You have secured your account with a separate mailbox password.");
linkLabel1.clear();
linkLabel2.clear();
showLoginCommon();
}
function showOnboarding() {
titleLabel.text = qsTr("Welcome to\nProton Mail Bridge");
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. ");
@ -87,7 +81,6 @@ Item {
icon.sourceSize.height = 148;
icon.sourceSize.width = 265;
}
function showOutlookSelector() {
showClientConfigCommon();
linkLabel1.setLink("https://proton.me/support/bridge", qsTr("My version of Outlook is not listed"));
@ -98,7 +91,6 @@ Item {
function onLogin2FARequested() {
showLogin2FA();
}
function onLogin2PasswordRequested() {
showLoginMailboxPassword();
}

View File

@ -19,10 +19,15 @@ import Proton
FocusScope {
id: root
enum RootStack {
Login,
TOTP,
MailboxPassword
}
property var wizard
property alias currentIndex: stackLayout.currentIndex
property alias username: usernameTextField.text
property var wizard
signal loginAbort(string username, bool wasSignedOut)
@ -32,10 +37,10 @@ FocusScope {
Backend.loginAbort(usernameTextField.text);
}
function reset(clearUsername = false) {
stackLayout.currentIndex = 0;
loginNormalLayout.reset(clearUsername);
login2FALayout.reset();
login2PasswordLayout.reset();
stackLayout.currentIndex = Login.RootStack.Login;
loginLayout.reset(clearUsername);
totpLayout.reset();
mailboxPasswordLayout.reset();
}
implicitHeight: children[0].implicitHeight
@ -55,7 +60,7 @@ FocusScope {
Connections {
function onLogin2FAError(_) {
console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAError");
console.assert(stackLayout.currentIndex === Login.RootStack.TOTP, "Unexpected login2FAError");
twoFAButton.loading = false;
twoFactorPasswordTextField.enabled = true;
twoFactorPasswordTextField.error = true;
@ -63,18 +68,18 @@ FocusScope {
twoFactorPasswordTextField.focus = true;
}
function onLogin2FAErrorAbort(_) {
console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAErrorAbort");
console.assert(stackLayout.currentIndex === Login.RootStack.TOTP, "Unexpected login2FAErrorAbort");
root.reset();
errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
}
function onLogin2FARequested(username) {
console.assert(stackLayout.currentIndex === 0, "Unexpected login2FARequested");
console.assert(stackLayout.currentIndex === Login.RootStack.Login, "Unexpected login2FARequested");
twoFactorUsernameLabel.text = username;
stackLayout.currentIndex = 1;
stackLayout.currentIndex = Login.RootStack.TOTP;
twoFactorPasswordTextField.focus = true;
}
function onLogin2PasswordError(_) {
console.assert(stackLayout.currentIndex === 2, "Unexpected login2PasswordError");
console.assert(stackLayout.currentIndex === Login.RootStack.MailboxPassword, "Unexpected login2PasswordError");
secondPasswordButton.loading = false;
secondPasswordTextField.enabled = true;
secondPasswordTextField.error = true;
@ -82,34 +87,34 @@ FocusScope {
secondPasswordTextField.focus = true;
}
function onLogin2PasswordErrorAbort(_) {
console.assert(stackLayout.currentIndex === 2, "Unexpected login2PasswordErrorAbort");
console.assert(stackLayout.currentIndex === Login.RootStack.MailboxPassword, "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;
console.assert(stackLayout.currentIndex === Login.RootStack.Login || stackLayout.currentIndex === Login.RootStack.TOTP, "Unexpected login2PasswordRequested");
stackLayout.currentIndex = Login.RootStack.MailboxPassword;
secondPasswordTextField.focus = true;
}
function onLoginAlreadyLoggedIn(_) {
stackLayout.currentIndex = 0;
stackLayout.currentIndex = Login.RootStack.Login;
root.reset();
}
function onLoginConnectionError(_) {
if (stackLayout.currentIndex === 0) {
if (stackLayout.currentIndex === Login.RootStack.Login) {
stackLayout.loginFailed();
}
}
function onLoginFinished(_) {
stackLayout.currentIndex = 0;
stackLayout.currentIndex = Login.RootStack.Login;
root.reset();
}
function onLoginFreeUserError() {
console.assert(stackLayout.currentIndex === 0, "Unexpected loginFreeUserError");
console.assert(stackLayout.currentIndex === Login.RootStack.Login, "Unexpected loginFreeUserError");
stackLayout.loginFailed();
}
function onLoginUsernamePasswordError(errorMsg) {
console.assert(stackLayout.currentIndex === 0, "Unexpected loginUsernamePasswordError");
console.assert(stackLayout.currentIndex === Login.RootStack.Login, "Unexpected loginUsernamePasswordError");
stackLayout.loginFailed();
if (errorMsg !== "")
errorLabel.text = errorMsg;
@ -120,7 +125,7 @@ FocusScope {
target: Backend
}
ColumnLayout {
id: loginNormalLayout
id: loginLayout
function reset(clearUsername = false) {
signInButton.loading = false;
errorLabel.text = "";
@ -262,7 +267,7 @@ FocusScope {
}
}
ColumnLayout {
id: login2FALayout
id: totpLayout
function reset() {
twoFAButton.loading = false;
twoFactorPasswordTextField.enabled = true;
@ -342,7 +347,7 @@ FocusScope {
}
}
ColumnLayout {
id: login2PasswordLayout
id: mailboxPasswordLayout
function reset() {
secondPasswordButton.loading = false;
secondPasswordTextField.enabled = true;

View File

@ -19,6 +19,7 @@ import "." as Proton
Item {
id: root
property var wizard
ColumnLayout {
@ -55,7 +56,7 @@ Item {
colorScheme: wizard.colorScheme
text: qsTr("Let's start")
onClicked: wizard.showLogin();
onClicked: wizard.showLogin()
}
}
}

View File

@ -26,85 +26,75 @@ Item {
MozillaThunderbird,
Generic
}
enum RootStack {
TwoPanesView = 0,
ClientConfigParameters = 1
}
enum ContentStack {
Onboarding = 0,
Login = 1,
ClientConfigSelector = 2,
ClientConfigOutlookSelector = 3,
ClientConfigWarning = 4
Onboarding,
Login,
ClientConfigSelector,
ClientConfigOutlookSelector,
ClientConfigWarning
}
enum RootStack {
TwoPanesView,
ClientConfigParameters
}
property string address
property int client
property string clientVersion
property ColorScheme colorScheme
property var user
property string address
signal wizardEnded()
signal showBugReport()
signal showBugReport
signal wizardEnded
function clientIconSource() {
switch (client) {
case SetupWizard.Client.AppleMail:
return "/qml/icons/ic-apple-mail.svg";
case SetupWizard.Client.MicrosoftOutlook:
return "/qml/icons/ic-microsoft-outlook.svg";
case SetupWizard.Client.MozillaThunderbird:
return "/qml/icons/ic-mozilla-thunderbird.svg";
case SetupWizard.Client.Generic:
return "/qml/icons/ic-other-mail-clients.svg";
default:
console.error("Unknown mail client " + client)
return "/qml/icons/ic-other-mail-clients.svg";
case SetupWizard.Client.AppleMail:
return "/qml/icons/ic-apple-mail.svg";
case SetupWizard.Client.MicrosoftOutlook:
return "/qml/icons/ic-microsoft-outlook.svg";
case SetupWizard.Client.MozillaThunderbird:
return "/qml/icons/ic-mozilla-thunderbird.svg";
case SetupWizard.Client.Generic:
return "/qml/icons/ic-other-mail-clients.svg";
default:
console.error("Unknown mail client " + client);
return "/qml/icons/ic-other-mail-clients.svg";
}
}
function clientName() {
switch (client) {
case SetupWizard.Client.AppleMail:
return "Apple Mail";
case SetupWizard.Client.MicrosoftOutlook:
return "Outlook";
case SetupWizard.Client.MozillaThunderbird:
return "Thunderbird";
case SetupWizard.Client.Generic:
return "your email client";
default:
console.error("Unknown mail client " + client)
return "your email client";
case SetupWizard.Client.AppleMail:
return "Apple Mail";
case SetupWizard.Client.MicrosoftOutlook:
return "Outlook";
case SetupWizard.Client.MozillaThunderbird:
return "Thunderbird";
case SetupWizard.Client.Generic:
return "your email client";
default:
console.error("Unknown mail client " + client);
return "your email client";
}
}
function closeWizard() {
wizardEnded()
wizardEnded();
}
function showOutlookSelector() {
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
leftContent.showOutlookSelector();
rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigOutlookSelector;
}
function showOnboarding() {
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
leftContent.showOnboarding();
rightContent.currentIndex = SetupWizard.ContentStack.Onboarding;
}
function showClientConfig(user, address) {
root.user = user
root.address = address
root.user = user;
root.address = address;
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
leftContent.showClientSelector();
rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigSelector;
}
function showClientParams() {
rootStackLayout.currentIndex = SetupWizard.RootStack.ClientConfigParameters;
}
function showClientWarning() {
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
leftContent.showClientConfigWarning();
rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigWarning;
}
function showLogin(username = "") {
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
root.address = "";
@ -113,16 +103,15 @@ Item {
login.reset(true);
login.username = username;
}
function showClientWarning() {
function showOnboarding() {
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
leftContent.showClientConfigWarning();
rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigWarning
leftContent.showOnboarding();
rightContent.currentIndex = SetupWizard.ContentStack.Onboarding;
}
function showClientParams() {
rootStackLayout.currentIndex = SetupWizard.RootStack.ClientConfigParameters;
function showOutlookSelector() {
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
leftContent.showOutlookSelector();
rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigOutlookSelector;
}
Connections {
@ -131,14 +120,13 @@ Item {
closeWizard();
return;
}
let user = Backend.users.get(userIndex)
let address = user ? user.addresses[0] : ""
let user = Backend.users.get(userIndex);
let address = user ? user.addresses[0] : "";
showClientConfig(user, address);
}
target: Backend
}
StackLayout {
id: rootStackLayout
anchors.fill: parent
@ -157,7 +145,6 @@ Item {
LeftPane {
id: leftContent
anchors.bottom: parent.bottom
anchors.bottomMargin: 96
anchors.horizontalCenter: parent.horizontalCenter