From 71ca20a9f35f03b6507933a2e15f6fda514386ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tinkl?= Date: Tue, 19 Nov 2024 12:14:14 +0100 Subject: [PATCH] feat(Onboarding): Login flows - implement the Login flows (seed, sync, keycard) - amend the keycard flow sequences with separate (non) empty page --- storybook/pages/OnboardingLayoutPage.qml | 38 +++- storybook/pages/SyncingEnterCodePage.qml | 13 +- ui/StatusQ/src/StatusQ/Components/qmldir | 3 - ui/StatusQ/src/assets.qrc | 1 + .../src/assets/png/onboarding/status_sync.png | Bin 0 -> 77257 bytes .../Onboarding/views/SyncCodeView.qml | 2 +- .../Onboarding2/OnboardingLayout.qml | 142 ++++++++++---- .../components/SeedphraseVerifyInput.qml | 11 +- .../Onboarding2/controls/ListItemButton.qml | 1 - .../controls/MaybeOutlineButton.qml | 29 +++ ui/app/AppLayouts/Onboarding2/controls/qmldir | 1 + .../pages/BackupSeedphraseAcks.qml | 2 + .../pages/BackupSeedphraseIntro.qml | 2 + .../pages/BackupSeedphraseOutro.qml | 2 + .../pages/BackupSeedphraseReveal.qml | 16 +- .../pages/BackupSeedphraseVerify.qml | 12 +- .../pages/CreateKeycardProfilePage.qml | 2 + .../Onboarding2/pages/CreatePasswordPage.qml | 2 + .../Onboarding2/pages/CreateProfilePage.qml | 3 +- .../pages/EnableBiometricsPage.qml | 2 + .../pages/HelpUsImproveStatusPage.qml | 2 + .../Onboarding2/pages/KeycardBasePage.qml | 2 - .../pages/KeycardCreatePinPage.qml | 1 + .../Onboarding2/pages/KeycardEmptyPage.qml | 25 +++ .../Onboarding2/pages/KeycardEnterPinPage.qml | 6 +- .../Onboarding2/pages/KeycardIntroPage.qml | 142 +++++++------- .../Onboarding2/pages/KeycardNotEmptyPage.qml | 40 ++++ .../Onboarding2/pages/LoginBySyncingPage.qml | 68 +++++++ .../Onboarding2/pages/LoginPage.qml | 174 ++++++++++++++++++ .../Onboarding2/pages/OnboardingPage.qml | 2 + .../Onboarding2/pages/SeedphrasePage.qml | 2 + .../Onboarding2/pages/WelcomePage.qml | 1 + ui/app/AppLayouts/Onboarding2/pages/qmldir | 28 +-- .../GetSyncCodeDesktopInstructions.qml | 4 +- .../shared/controls/StatusSyncCodeScan.qml | 2 +- .../popups/GetSyncCodeInstructionsPopup.qml | 4 +- ui/imports/shared/views/SyncingEnterCode.qml | 83 +++++---- 37 files changed, 672 insertions(+), 198 deletions(-) create mode 100644 ui/StatusQ/src/assets/png/onboarding/status_sync.png create mode 100644 ui/app/AppLayouts/Onboarding2/controls/MaybeOutlineButton.qml create mode 100644 ui/app/AppLayouts/Onboarding2/pages/KeycardEmptyPage.qml create mode 100644 ui/app/AppLayouts/Onboarding2/pages/KeycardNotEmptyPage.qml create mode 100644 ui/app/AppLayouts/Onboarding2/pages/LoginBySyncingPage.qml create mode 100644 ui/app/AppLayouts/Onboarding2/pages/LoginPage.qml diff --git a/storybook/pages/OnboardingLayoutPage.qml b/storybook/pages/OnboardingLayoutPage.qml index 93fc82aee9..369117c67a 100644 --- a/storybook/pages/OnboardingLayoutPage.qml +++ b/storybook/pages/OnboardingLayoutPage.qml @@ -47,6 +47,8 @@ SplitView { // create keycard profile Constants.startupState.keycardEmpty ] + + readonly property string mnemonic: "dog dog dog dog dog dog dog dog dog dog dog dog" } OnboardingLayout { @@ -59,14 +61,30 @@ SplitView { } function getPasswordStrengthScore(password) { + logs.logEvent("StartupStore.getPasswordStrengthScore", ["password"], arguments) return Math.min(password.length-1, 4) } function validMnemonic(mnemonic) { - return true + logs.logEvent("StartupStore.validMnemonic", ["mnemonic"], arguments) + return mnemonic === keycardMock.mnemonic } function getPin() { + logs.logEvent("StartupStore.getPin()") return ctrlPin.text } + function getSeedPhrase() { + logs.logEvent("StartupStore.getSeedPhrase()") + // FIXME needed? cf getMnemonic() + } + + function validateLocalPairingConnectionString(connectionString) { + logs.logEvent("StartupStore.validateLocalPairingConnectionString", ["connectionString"], arguments) + return !Number.isNaN(parseInt(connectionString)) + } + function setConnectionString(connectionString) { + logs.logEvent("StartupStore.setConnectionString", ["connectionString"], arguments) + } + readonly property var startupModuleInst: QtObject { property int remainingAttempts: 5 } @@ -89,12 +107,18 @@ SplitView { readonly property var words: ["apple", "banana", "cat", "cow", "catalog", "catch", "category", "cattle", "dog", "elephant", "fish", "grape"] function getMnemonic() { + logs.logEvent("PrivacyStore.getMnemonic()") return words.join(" ") } function mnemonicWasShown() { console.warn("!!! MNEMONIC SHOWN") - logs.logEvent("mnemonicWasShown") + logs.logEvent("PrivacyStore.mnemonicWasShown()") + } + + function removeMnemonic() { + console.warn("!!! REMOVE MNEMONIC") + logs.logEvent("PrivacyStore.removeMnemonic()") } } @@ -105,9 +129,9 @@ SplitView { property bool metricsPopupSeen } - onFinished: (success, primaryPath, secondaryPath) => { - console.warn("!!! ONBOARDING FINISHED; success:", success, "; primary path:", primaryPath, "; secondary:", secondaryPath) - logs.logEvent("onFinished", ["success", "primaryPath", "secondaryPath"], arguments) + onFinished: (primaryPath, secondaryPath, data) => { + console.warn("!!! ONBOARDING FINISHED; primary path:", primaryPath, "; secondary:", secondaryPath, "; data:", JSON.stringify(data)) + logs.logEvent("onFinished", ["primaryPath", "secondaryPath", "data"], arguments) console.warn("!!! RESTARTING FLOW") restartFlow() @@ -151,7 +175,7 @@ SplitView { ColumnLayout { Layout.fillWidth: true Label { - text: "Current page: %1".arg(onboarding.stack.currentItem ? onboarding.stack.currentItem.title : "") + text: "Current page: %1".arg(onboarding.stack.currentItem ? onboarding.stack.currentItem.pageClassName : "") } Label { text: `Current path: ${onboarding.primaryPath} -> ${onboarding.secondaryPath}` @@ -177,7 +201,7 @@ SplitView { Button { text: "Copy seedphrase" focusPolicy: Qt.NoFocus - onClicked: ClipboardUtils.setText("dog dog dog dog dog dog dog dog dog dog dog dog") + onClicked: ClipboardUtils.setText(keycardMock.mnemonic) } Button { text: "Copy PIN (\"%1\")".arg(ctrlPin.text) diff --git a/storybook/pages/SyncingEnterCodePage.qml b/storybook/pages/SyncingEnterCodePage.qml index d423f036d7..0b762994b7 100644 --- a/storybook/pages/SyncingEnterCodePage.qml +++ b/storybook/pages/SyncingEnterCodePage.qml @@ -7,6 +7,7 @@ import Storybook 1.0 import mainui 1.0 import shared.views 1.0 import shared.stores 1.0 as SharedStores +import shared.popups 1.0 import AppLayouts.stores 1.0 as AppLayoutStores @@ -33,9 +34,19 @@ SplitView { anchors.horizontalCenter: parent.horizontalCenter validateConnectionString: (stringValue) => !Number.isNaN(parseInt(stringValue)) - onDisplayInstructions: logs.logEvent("SyncingEnterCode::displayInstructions") + onDisplayInstructions: { + logs.logEvent("SyncingEnterCode::displayInstructions") + instructionsPopup.createObject(root).open() + } onProceed: (connectionString) => logs.logEvent("SyncingEnterCode::proceed", ["connectionString"], arguments) } + + Component { + id: instructionsPopup + GetSyncCodeInstructionsPopup { + destroyOnClose: true + } + } } LogsAndControlsPanel { diff --git a/ui/StatusQ/src/StatusQ/Components/qmldir b/ui/StatusQ/src/StatusQ/Components/qmldir index 28cd64f3bb..1aac49fb44 100644 --- a/ui/StatusQ/src/StatusQ/Components/qmldir +++ b/ui/StatusQ/src/StatusQ/Components/qmldir @@ -8,12 +8,10 @@ StatusAnimatedImage 0.1 StatusAnimatedImage.qml StatusBadge 0.1 StatusBadge.qml StatusBetaTag 0.1 StatusBetaTag.qml StatusCard 0.1 StatusCard.qml -StatusChart 0.1 StatusChart.qml StatusChartPanel 0.1 StatusChartPanel.qml StatusChatInfoToolBar 0.1 StatusChatInfoToolBar.qml StatusChatList 0.1 StatusChatList.qml StatusChatListAndCategories 0.1 StatusChatListAndCategories.qml -StatusChatListCategory 0.1 StatusChatListCategory.qml StatusChatListCategoryItem 0.1 StatusChatListCategoryItem.qml StatusChatListItem 0.1 StatusChatListItem.qml StatusColorSpace 0.0 StatusColorSpace.qml @@ -23,7 +21,6 @@ StatusContactRequestsIndicatorListItem 0.1 StatusContactRequestsIndicatorListIte StatusContactVerificationIcons 0.1 StatusContactVerificationIcons.qml StatusCursorDelegate 0.1 StatusCursorDelegate.qml StatusDateGroupLabel 0.1 StatusDateGroupLabel.qml -StatusDateInput 0.1 StatusDateInput.qml StatusDatePicker 0.1 StatusDatePicker.qml StatusDescriptionListItem 0.1 StatusDescriptionListItem.qml StatusDotsLoadingIndicator 0.1 StatusDotsLoadingIndicator.qml diff --git a/ui/StatusQ/src/assets.qrc b/ui/StatusQ/src/assets.qrc index d6610e3a52..00b9aa9541 100644 --- a/ui/StatusQ/src/assets.qrc +++ b/ui/StatusQ/src/assets.qrc @@ -8349,6 +8349,7 @@ assets/png/onboarding/status_keycard.png assets/png/onboarding/status_keycard_multiple.png assets/png/onboarding/status_seedphrase.png + assets/png/onboarding/status_sync.png assets/png/onboarding/enable_biometrics.png assets/png/onboarding/keycard/empty.png assets/png/onboarding/keycard/insert.png diff --git a/ui/StatusQ/src/assets/png/onboarding/status_sync.png b/ui/StatusQ/src/assets/png/onboarding/status_sync.png new file mode 100644 index 0000000000000000000000000000000000000000..d1034b3082312712c336e7867d19f77fa1fbdb3e GIT binary patch literal 77257 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 1 && !stack.busy onClicked: stack.pop() } @@ -175,6 +174,7 @@ Page { function onLoginRequested() { console.warn("!!! PRIMARY: LOG IN") d.primaryPath = OnboardingLayout.PrimaryPath.Login + stack.push(helpUsImproveStatusPage) } // help us improve page @@ -187,7 +187,7 @@ Page { if (d.primaryPath === OnboardingLayout.PrimaryPath.CreateProfile) stack.push(createProfilePage) else if (d.primaryPath === OnboardingLayout.PrimaryPath.Login) - ; // TODO Login path + stack.push(loginPage) } // create profile page @@ -199,7 +199,7 @@ Page { function onCreateProfileWithSeedphraseRequested() { console.warn("!!! SECONDARY: CREATE PROFILE WITH SEEDPHRASE") d.secondaryPath = OnboardingLayout.SecondaryPath.CreateProfileWithSeedphrase - stack.push(seedphrasePage, { title: qsTr("Create profile with a recovery phrase"), subtitle: qsTr("Enter your 12, 18 or 24 word recovery phrase")}) + stack.push(seedphrasePage, { title: qsTr("Create profile using a recovery phrase"), subtitle: qsTr("Enter your 12, 18 or 24 word recovery phrase")}) } function onCreateProfileWithEmptyKeycardRequested() { console.warn("!!! SECONDARY: CREATE PROFILE WITH KEYCARD") @@ -207,10 +207,28 @@ Page { stack.push(keycardIntroPage) } + // login page + function onLoginWithSeedphraseRequested() { + console.warn("!!! SECONDARY: LOGIN WITH SEEDPHRASE") + d.secondaryPath = OnboardingLayout.SecondaryPath.LoginWithSeedphrase + stack.push(seedphrasePage, { title: qsTr("Sign in with your Status recovery phrase"), subtitle: qsTr("Enter your 12, 18 or 24 word recovery phrase")}) + } + function onLoginWithSyncingRequested() { + console.warn("!!! SECONDARY: LOGIN WITH SYNCING") + d.secondaryPath = OnboardingLayout.SecondaryPath.LoginWithSyncing + stack.push(loginBySyncPage) + } + function onLoginWithKeycardRequested() { + console.warn("!!! SECONDARY: LOGIN WITH KEYCARD") + d.secondaryPath = OnboardingLayout.SecondaryPath.LoginWithKeycard + stack.push(keycardIntroPage) + } + // create password page function onSetPasswordRequested(password: string) { console.warn("!!! SET PASSWORD REQUESTED") d.password = password + // TODO set the password immediately? stack.clear() stack.push(enableBiometricsPage, {subtitle: qsTr("Use biometrics to fill in your password?")}) // FIXME make optional on unsupported platforms } @@ -218,7 +236,7 @@ Page { // seedphrase page function onSeedphraseValidated() { console.warn("!!! SEEDPHRASE VALIDATED") - if (d.secondaryPath === OnboardingLayout.SecondaryPath.CreateProfileWithSeedphrase) { + if (d.secondaryPath === OnboardingLayout.SecondaryPath.CreateProfileWithSeedphrase || d.secondaryPath === OnboardingLayout.SecondaryPath.LoginWithSeedphrase) { console.warn("!!! AFTER SEEDPHRASE -> PASSWORD PAGE") stack.push(createPasswordPage) } else if (d.secondaryPath === OnboardingLayout.SecondaryPath.CreateProfileWithKeycardExistingSeedphrase) { @@ -238,15 +256,31 @@ Page { } function onKeycardFactoryResetRequested() { console.warn("!!! KEYCARD FACTORY RESET REQUESTED") + // TODO start keycard factory reset in a popup here root.keycardFactoryResetRequested() } - function onLoginWithKeycardRequested() { - console.warn("!!! LOGIN WITH KEYCARD REQUESTED") - stack.push(keycardEnterPinPage) + function onLoginWithThisKeycardRequested() { + console.warn("!!! LOGIN WITH THIS KEYCARD REQUESTED") + d.primaryPath = OnboardingLayout.PrimaryPath.Login + d.secondaryPath = OnboardingLayout.SecondaryPath.LoginWithKeycard + if (root.startupStore.getPin() !== "") + stack.push(keycardEnterPinPage) + else + stack.push(keycardCreatePinPage) } function onEmptyKeycardDetected() { console.warn("!!! EMPTY KEYCARD DETECTED") - stack.replace(createKeycardProfilePage) // NB: replacing the keycardIntroPage + if (d.secondaryPath === OnboardingLayout.SecondaryPath.LoginWithKeycard) + stack.replace(keycardEmptyPage) // NB: replacing the loginPage + else + stack.replace(createKeycardProfilePage) // NB: replacing the keycardIntroPage + } + function onNotEmptyKeycardDetected() { + console.warn("!!! NOT EMPTY KEYCARD DETECTED") + if (d.secondaryPath === OnboardingLayout.SecondaryPath.LoginWithKeycard) + stack.push(keycardEnterPinPage) + else + stack.push(keycardNotEmptyPage) } function onCreateKeycardProfileWithNewSeedphrase() { @@ -267,6 +301,7 @@ Page { function onKeycardPinCreated(pin) { console.warn("!!! KEYCARD PIN CREATED:", pin) d.keycardPin = pin + // TODO set the PIN immediately? Backpressure.debounce(root, 2000, function() { stack.clear() stack.push(enableBiometricsPage, // FIXME make optional on unsupported platforms @@ -277,6 +312,7 @@ Page { function onKeycardPinEntered(pin) { console.warn("!!! KEYCARD PIN ENTERED:", pin) d.keycardPin = pin + // TODO set the PIN immediately? stack.clear() stack.push(enableBiometricsPage, // FIXME make optional on unsupported platforms {subtitle: qsTr("Would you like to enable biometrics to fill in your password? You will use biometrics for signing in to Status and for signing transactions.")}) @@ -295,7 +331,6 @@ Page { function onBackupSeedphraseConfirmed() { console.warn("!!! BACKUP SEED CONFIRMED") - d.settings.seedphraseRevealed = true root.privacyStore.mnemonicWasShown() stack.push(backupSeedVerifyPage) } @@ -311,6 +346,19 @@ Page { stack.replace(splashScreen, { runningProgressAnimation: true }) } + // login with sync pages + function onSyncProceedWithConnectionString(connectionString) { + console.warn("!!! SYNC PROCEED WITH CONNECTION STRING:", connectionString) + d.syncConnectionString = connectionString + root.startupStore.setConnectionString(connectionString) + // TODO backend: start the sync + Backpressure.debounce(root, 1000, function() { + stack.clear() + // TODO show the sync in progress screen instead of the final splash page? + stack.replace(splashScreen, { runningProgressAnimation: true }) + })() + } + // enable biometrics page function onEnableBiometricsRequested(enabled: bool) { console.warn("!!! ENABLE BIOMETRICS:", enabled) @@ -346,30 +394,26 @@ Page { id: createPasswordPage CreatePasswordPage { passwordStrengthScoreFunction: root.startupStore.getPasswordStrengthScore - StackView.onRemoved: { - d.password = "" - } } } Component { id: enableBiometricsPage - EnableBiometricsPage { - StackView.onRemoved: d.enableBiometrics = false - } + EnableBiometricsPage {} } Component { id: splashScreen DidYouKnowSplashScreen { - readonly property string title: "Splash" + readonly property string pageClassName: "Splash" property bool runningProgressAnimation NumberAnimation on progress { from: 0.0 to: 1 duration: root.splashScreenDurationMs running: runningProgressAnimation - onStopped: root.finished(true, d.primaryPath, d.secondaryPath) + onStopped: root.finished(d.primaryPath, d.secondaryPath, + {"password": d.password, "keycardPin": d.keycardPin, "enableBiometrics": d.enableBiometrics, "syncConnectionString": d.syncConnectionString}) } } } @@ -384,7 +428,10 @@ Page { Component { id: createKeycardProfilePage CreateKeycardProfilePage { - StackView.onActivated: d.secondaryPath = OnboardingLayout.SecondaryPath.CreateProfileWithKeycard + StackView.onActivated: { + d.primaryPath = OnboardingLayout.PrimaryPath.CreateProfile + d.secondaryPath = OnboardingLayout.SecondaryPath.CreateProfileWithKeycard + } } } @@ -397,10 +444,22 @@ Page { // NB just to make sure we don't miss the signal when we (re)load the page in the final state already if (keycardState === Constants.startupState.keycardEmpty) emptyKeycardDetected() + else if (keycardState === Constants.startupState.keycardNotEmpty) + notEmptyKeycardDetected() } } } + Component { + id: keycardEmptyPage + KeycardEmptyPage {} + } + + Component { + id: keycardNotEmptyPage + KeycardNotEmptyPage {} + } + Component { id: keycardCreatePinPage KeycardCreatePinPage {} @@ -427,7 +486,6 @@ Page { Component { id: backupSeedRevealPage BackupSeedphraseReveal { - seedphraseRevealed: d.settings.seedphraseRevealed seedWords: d.seedWords } } @@ -451,6 +509,20 @@ Page { BackupSeedphraseOutro {} } + Component { + id: loginPage + LoginPage { + StackView.onActivated: d.secondaryPath = OnboardingLayout.SecondaryPath.Unknown // reset when we get back here + } + } + + Component { + id: loginBySyncPage + LoginBySyncingPage { + validateConnectionString: root.startupStore.validateLocalPairingConnectionString + } + } + // common popups Component { id: privacyPolicyPopup diff --git a/ui/app/AppLayouts/Onboarding2/components/SeedphraseVerifyInput.qml b/ui/app/AppLayouts/Onboarding2/components/SeedphraseVerifyInput.qml index 0e08511d77..0b042fc9c9 100644 --- a/ui/app/AppLayouts/Onboarding2/components/SeedphraseVerifyInput.qml +++ b/ui/app/AppLayouts/Onboarding2/components/SeedphraseVerifyInput.qml @@ -46,25 +46,18 @@ StatusTextField { switch (event.key) { case Qt.Key_Return: case Qt.Key_Enter: { - if (!!text && filteredModel.count > 0) { + if (filteredModel.count > 0) { root.text = filteredModel.get(suggestionsList.currentIndex).seedWord } break } - case Qt.Key_Down: { - suggestionsList.incrementCurrentIndex() - break - } - case Qt.Key_Up: { - suggestionsList.decrementCurrentIndex() - break - } case Qt.Key_Space: { event.accepted = !event.text.match(/^[a-zA-Z]$/) break } } } + Keys.forwardTo: [suggestionsList] StatusDropdown { x: 0 diff --git a/ui/app/AppLayouts/Onboarding2/controls/ListItemButton.qml b/ui/app/AppLayouts/Onboarding2/controls/ListItemButton.qml index c28b16b703..5cfae0d7a3 100644 --- a/ui/app/AppLayouts/Onboarding2/controls/ListItemButton.qml +++ b/ui/app/AppLayouts/Onboarding2/controls/ListItemButton.qml @@ -1,5 +1,4 @@ import QtQuick 2.15 -import QtQuick.Controls 2.15 import StatusQ.Core 0.1 import StatusQ.Components 0.1 diff --git a/ui/app/AppLayouts/Onboarding2/controls/MaybeOutlineButton.qml b/ui/app/AppLayouts/Onboarding2/controls/MaybeOutlineButton.qml new file mode 100644 index 0000000000..4527718177 --- /dev/null +++ b/ui/app/AppLayouts/Onboarding2/controls/MaybeOutlineButton.qml @@ -0,0 +1,29 @@ +import QtQuick 2.15 +import QtQml 2.15 + +import StatusQ.Core 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Core.Theme 0.1 + +StatusButton { + id: root + + implicitWidth: 320 + + // inside a Column (or another Positioner), make all but the first button outline + Binding on normalColor { + value: "transparent" + when: !root.Positioner.isFirstItem + restoreMode: Binding.RestoreBindingOrValue + } + Binding on borderWidth { + value: 1 + when: !root.Positioner.isFirstItem + restoreMode: Binding.RestoreBindingOrValue + } + Binding on borderColor { + value: Theme.palette.baseColor2 + when: !root.Positioner.isFirstItem + restoreMode: Binding.RestoreBindingOrValue + } +} diff --git a/ui/app/AppLayouts/Onboarding2/controls/qmldir b/ui/app/AppLayouts/Onboarding2/controls/qmldir index b513ee4081..9607ac395e 100644 --- a/ui/app/AppLayouts/Onboarding2/controls/qmldir +++ b/ui/app/AppLayouts/Onboarding2/controls/qmldir @@ -1,2 +1,3 @@ OnboardingFrame 1.0 OnboardingFrame.qml ListItemButton 1.0 ListItemButton.qml +MaybeOutlineButton 1.0 MaybeOutlineButton.qml diff --git a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseAcks.qml b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseAcks.qml index 9597d83dc1..6f475458e0 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseAcks.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseAcks.qml @@ -12,6 +12,8 @@ OnboardingPage { signal backupSeedphraseContinue() + pageClassName: "BackupSeedphraseAcks" + contentItem: Item { ColumnLayout { anchors.centerIn: parent diff --git a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseIntro.qml b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseIntro.qml index 94fdc77c4d..f14cbeaa99 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseIntro.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseIntro.qml @@ -12,6 +12,8 @@ OnboardingPage { signal backupSeedphraseRequested() + pageClassName: "BackupSeedphraseIntro" + contentItem: Item { ColumnLayout { anchors.centerIn: parent diff --git a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseOutro.qml b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseOutro.qml index 11c6c359d3..307de382a3 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseOutro.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseOutro.qml @@ -14,6 +14,8 @@ OnboardingPage { signal backupSeedphraseRemovalConfirmed() + pageClassName: "BackupSeedphraseOutro" + contentItem: Item { ColumnLayout { anchors.centerIn: parent diff --git a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseReveal.qml b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseReveal.qml index 414506344a..f174aa44c8 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseReveal.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseReveal.qml @@ -14,10 +14,16 @@ OnboardingPage { id: root required property var seedWords - property bool seedphraseRevealed signal backupSeedphraseConfirmed() + pageClassName: "BackupSeedphraseReveal" + + QtObject { + id: d + property bool seedphraseRevealed + } + contentItem: Item { ColumnLayout { anchors.centerIn: parent @@ -78,7 +84,7 @@ OnboardingPage { } } } - layer.enabled: !root.seedphraseRevealed + layer.enabled: !d.seedphraseRevealed layer.effect: GaussianBlur { radius: 16 samples: 33 @@ -91,8 +97,8 @@ OnboardingPage { text: qsTr("Reveal recovery phrase") icon.name: "show" type: StatusBaseButton.Type.Primary - visible: !root.seedphraseRevealed - onClicked: root.seedphraseRevealed = true + visible: !d.seedphraseRevealed + onClicked: d.seedphraseRevealed = true } } @@ -107,7 +113,7 @@ OnboardingPage { StatusButton { Layout.alignment: Qt.AlignHCenter text: qsTr("Confirm recovery phrase") - enabled: root.seedphraseRevealed + enabled: d.seedphraseRevealed onClicked: root.backupSeedphraseConfirmed() } } diff --git a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseVerify.qml b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseVerify.qml index 9a7f53ab78..e19302200b 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseVerify.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseVerify.qml @@ -21,6 +21,8 @@ OnboardingPage { signal backupSeedphraseVerified() + pageClassName: "BackupSeedphraseVerify" + QtObject { id: d readonly property var seedSuggestions: BIP39_en {} // [{seedWord:string}, ...] @@ -87,9 +89,13 @@ OnboardingPage { seedSuggestions: d.seedSuggestions Component.onCompleted: if (index === 0) forceActiveFocus() onAccepted: { - const nextItem = seedRepeater.itemAt(index + 1) ?? seedRepeater.itemAt(0) - if (!!nextItem) { - nextItem.input.forceActiveFocus() + if (seedRepeater.allValid) { /// move to next page + root.backupSeedphraseVerified() + } else { // move to next field + const nextItem = seedRepeater.itemAt(index + 1) ?? seedRepeater.itemAt(0) + if (!!nextItem) { + nextItem.input.forceActiveFocus() + } } } } diff --git a/ui/app/AppLayouts/Onboarding2/pages/CreateKeycardProfilePage.qml b/ui/app/AppLayouts/Onboarding2/pages/CreateKeycardProfilePage.qml index 94750192bc..92ee60c573 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/CreateKeycardProfilePage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/CreateKeycardProfilePage.qml @@ -19,6 +19,8 @@ OnboardingPage { signal createKeycardProfileWithNewSeedphrase() signal createKeycardProfileWithExistingSeedphrase() + pageClassName: "CreateKeycardProfilePage" + contentItem: Item { ColumnLayout { width: parent.width diff --git a/ui/app/AppLayouts/Onboarding2/pages/CreatePasswordPage.qml b/ui/app/AppLayouts/Onboarding2/pages/CreatePasswordPage.qml index 1041eea266..3eaec9eabd 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/CreatePasswordPage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/CreatePasswordPage.qml @@ -19,6 +19,8 @@ OnboardingPage { title: qsTr("Create profile password") + pageClassName: "CreatePasswordPage" + QtObject { id: d diff --git a/ui/app/AppLayouts/Onboarding2/pages/CreateProfilePage.qml b/ui/app/AppLayouts/Onboarding2/pages/CreateProfilePage.qml index 879f2023a0..70fd3ecd30 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/CreateProfilePage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/CreateProfilePage.qml @@ -1,7 +1,6 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 -import QtGraphicalEffects 1.15 import StatusQ.Core 0.1 import StatusQ.Components 0.1 @@ -22,6 +21,8 @@ OnboardingPage { signal createProfileWithSeedphraseRequested() signal createProfileWithEmptyKeycardRequested() + pageClassName: "CreateProfilePage" + contentItem: Item { ColumnLayout { anchors.centerIn: parent diff --git a/ui/app/AppLayouts/Onboarding2/pages/EnableBiometricsPage.qml b/ui/app/AppLayouts/Onboarding2/pages/EnableBiometricsPage.qml index 5a21d96a0c..e0cba2efd7 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/EnableBiometricsPage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/EnableBiometricsPage.qml @@ -16,6 +16,8 @@ OnboardingPage { signal enableBiometricsRequested(bool enable) + pageClassName: "EnableBiometricsPage" + contentItem: Item { ColumnLayout { anchors.centerIn: parent diff --git a/ui/app/AppLayouts/Onboarding2/pages/HelpUsImproveStatusPage.qml b/ui/app/AppLayouts/Onboarding2/pages/HelpUsImproveStatusPage.qml index 4adb4d4a18..cb356ffdf9 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/HelpUsImproveStatusPage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/HelpUsImproveStatusPage.qml @@ -20,6 +20,8 @@ OnboardingPage { signal shareUsageDataRequested(bool enabled) signal privacyPolicyRequested() + pageClassName: "HelpUsImproveStatusPage" + contentItem: Item { ColumnLayout { anchors.centerIn: parent diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardBasePage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardBasePage.qml index 866f1367ba..2ffd2b72b0 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/KeycardBasePage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardBasePage.qml @@ -8,8 +8,6 @@ import StatusQ.Controls 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Core.Utils 0.1 as SQUtils -import utils 1.0 - OnboardingPage { id: root diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardCreatePinPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardCreatePinPage.qml index ad13826e07..ce3481e00d 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/KeycardCreatePinPage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardCreatePinPage.qml @@ -17,6 +17,7 @@ KeycardBasePage { signal keycardPinCreated(string pin) + pageClassName: "KeycardCreatePinPage" image.source: Theme.png("onboarding/keycard/reading") QtObject { diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardEmptyPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardEmptyPage.qml new file mode 100644 index 0000000000..f4233370c5 --- /dev/null +++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardEmptyPage.qml @@ -0,0 +1,25 @@ +import QtQuick 2.15 + +import StatusQ.Core.Theme 0.1 + +import AppLayouts.Onboarding2.controls 1.0 + +KeycardBasePage { + id: root + + signal createProfileWithEmptyKeycardRequested() + + title: qsTr("Keycard is empty") + subtitle: qsTr("There is no profile key pair on this Keycard") + image.source: Theme.png("onboarding/keycard/error") + + pageClassName: "KeycardEmptyPage" + + buttons: [ + MaybeOutlineButton { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Create new profile on this Keycard") + onClicked: root.createProfileWithEmptyKeycardRequested() + } + ] +} diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardEnterPinPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardEnterPinPage.qml index 1d1c417ac2..260e82f326 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/KeycardEnterPinPage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardEnterPinPage.qml @@ -7,6 +7,7 @@ import StatusQ.Components 0.1 import StatusQ.Controls 0.1 import StatusQ.Controls.Validators 0.1 import StatusQ.Core.Theme 0.1 +import StatusQ.Core.Backpressure 0.1 import AppLayouts.Onboarding2.controls 1.0 @@ -23,6 +24,7 @@ KeycardBasePage { signal keycardFactoryResetRequested() signal keycardLocked() + pageClassName: "KeycardEnterPinPage" image.source: Theme.png("onboarding/keycard/reading") QtObject { @@ -134,7 +136,9 @@ KeycardBasePage { } StateChangeScript { script: { - root.keycardPinEntered(pinInput.pinInput) + Backpressure.debounce(root, 2000, function() { + root.keycardPinEntered(pinInput.pinInput) + })() } } }, diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardIntroPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardIntroPage.qml index a2a74b2084..d9922241e3 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/KeycardIntroPage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardIntroPage.qml @@ -17,11 +17,12 @@ KeycardBasePage { required property string keycardState // Constants.startupState.keycardXXX property bool displayPromoBanner - signal reloadKeycardRequested() signal keycardFactoryResetRequested() - signal loginWithKeycardRequested() - + signal reloadKeycardRequested() signal emptyKeycardDetected() + signal notEmptyKeycardDetected() + + pageClassName: "KeycardIntroPage" OnboardingFrame { id: promoBanner @@ -77,46 +78,22 @@ KeycardBasePage { } buttons: [ - MaybeOutlineButton { - id: btnLogin - text: qsTr("Log in with this Keycard") - onClicked: root.loginWithKeycardRequested() - }, MaybeOutlineButton { id: btnFactoryReset + visible: false text: qsTr("Factory reset Keycard") + anchors.horizontalCenter: parent.horizontalCenter onClicked: root.keycardFactoryResetRequested() }, MaybeOutlineButton { id: btnReload - text: qsTr("I’ve inserted a Keycard") + visible: false + text: qsTr("I’ve inserted a different Keycard") + anchors.horizontalCenter: parent.horizontalCenter onClicked: root.reloadKeycardRequested() } ] - // inside a Column (or another Positioner), make all but the first button outline - component MaybeOutlineButton: StatusButton { - id: maybeOutlineButton - width: 320 - anchors.horizontalCenter: parent.horizontalCenter - visible: false - Binding on normalColor { - value: "transparent" - when: !maybeOutlineButton.Positioner.isFirstItem - restoreMode: Binding.RestoreBindingOrValue - } - Binding on borderWidth { - value: 1 - when: !maybeOutlineButton.Positioner.isFirstItem - restoreMode: Binding.RestoreBindingOrValue - } - Binding on borderColor { - value: Theme.palette.baseColor2 - when: !maybeOutlineButton.Positioner.isFirstItem - restoreMode: Binding.RestoreBindingOrValue - } - } - states: [ // normal/intro states State { @@ -139,15 +116,18 @@ KeycardBasePage { PropertyChanges { target: root title: qsTr("Insert your Keycard") - infoText.text: qsTr("Need a little %1?").arg(Utils.getStyledLink(qsTr("help"), "https://keycard.tech/docs/", infoText.hoveredLink, - Theme.palette.baseColor1, Theme.palette.primaryColor1)) + infoText.text: qsTr("Need a little %1?").arg(Utils.getStyledLink(qsTr("help"), "https://keycard.tech/docs/", + infoText.hoveredLink, + Theme.palette.baseColor1, + Theme.palette.primaryColor1)) image.source: Theme.png("onboarding/keycard/insert") } }, State { name: "reading" when: root.keycardState === Constants.startupState.keycardReadingKeycard || - root.keycardState === Constants.startupState.keycardInsertedKeycard + root.keycardState === Constants.startupState.keycardInsertedKeycard || + root.keycardState === Constants.startupState.keycardRecognizedKeycard PropertyChanges { target: root title: qsTr("Reading Keycard...") @@ -156,9 +136,42 @@ KeycardBasePage { }, // error states State { - name: "error" + name: "notKeycard" + when: root.keycardState === Constants.startupState.keycardWrongKeycard || + root.keycardState === Constants.startupState.keycardNotKeycard PropertyChanges { target: root + title: qsTr("Oops this isn’t a Keycard") + subtitle: qsTr("Remove card and insert a Keycard") + image.source: Theme.png("onboarding/keycard/invalid") + } + PropertyChanges { + target: btnReload + visible: true + } + }, + State { + name: "noService" + when: root.keycardState === Constants.startupState.keycardNoPCSCService + PropertyChanges { + target: root + title: qsTr("Smartcard reader service unavailable") + subtitle: qsTr("The Smartcard reader service (PCSC service), required for using Keycard, is not currently working. Ensure PCSC is installed and running and try again.") + image.source: Theme.png("onboarding/keycard/error") + } + PropertyChanges { + target: btnReload + visible: true + text: qsTr("Retry") + } + }, + State { + name: "occupied" + when: root.keycardState === Constants.startupState.keycardMaxPairingSlotsReached + PropertyChanges { + target: root + title: qsTr("All pairing slots occupied") + subtitle: qsTr("Factory reset this Keycard or insert a different one") image.source: Theme.png("onboarding/keycard/error") } PropertyChanges { @@ -170,63 +183,38 @@ KeycardBasePage { visible: true } }, - State { - name: "notKeycard" - extend: "error" - when: root.keycardState === Constants.startupState.keycardWrongKeycard || - root.keycardState === Constants.startupState.keycardNotKeycard - PropertyChanges { - target: root - title: qsTr("Oops this isn’t a Keycard") - subtitle: qsTr("Remove card and insert a Keycard") - image.source: Theme.png("onboarding/keycard/invalid") - } - PropertyChanges { - target: btnFactoryReset - visible: false - } - }, - State { - name: "occupied" - extend: "error" - when: root.keycardState === Constants.startupState.keycardMaxPairingSlotsReached - PropertyChanges { - target: root - title: qsTr("All pairing slots occupied") - subtitle: qsTr("Factory reset this Keycard or insert a different one") - } - }, State { name: "locked" - extend: "error" when: root.keycardState === Constants.startupState.keycardLocked PropertyChanges { target: root - title: qsTr("Keycard locked") + title: "".arg(Theme.palette.dangerColor1) + qsTr("Keycard locked") + "" subtitle: qsTr("The Keycard you have inserted is locked, you will need to factory reset it or insert a different one") - } - }, - State { - name: "notEmpty" - extend: "error" - when: root.keycardState === Constants.startupState.keycardNotEmpty - PropertyChanges { - target: root - title: qsTr("Keycard is not empty") - subtitle: qsTr("You can’t use it to store new keys right now") + image.source: Theme.png("onboarding/keycard/error") } PropertyChanges { - target: btnLogin + target: btnFactoryReset + visible: true + } + PropertyChanges { + target: btnReload visible: true } }, - // success/exit state + // exit states State { - name: "emptyDetected" + name: "empty" when: root.keycardState === Constants.startupState.keycardEmpty StateChangeScript { script: root.emptyKeycardDetected() } + }, + State { + name: "notEmpty" + when: root.keycardState === Constants.startupState.keycardNotEmpty + StateChangeScript { + script: root.notEmptyKeycardDetected() + } } ] } diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardNotEmptyPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardNotEmptyPage.qml new file mode 100644 index 0000000000..8e0f800c89 --- /dev/null +++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardNotEmptyPage.qml @@ -0,0 +1,40 @@ +import QtQuick 2.15 + +import StatusQ.Core.Theme 0.1 + +import AppLayouts.Onboarding2.controls 1.0 + +KeycardBasePage { + id: root + + signal reloadKeycardRequested() + signal loginWithThisKeycardRequested() + signal keycardFactoryResetRequested() + + title: qsTr("Keycard is not empty") + subtitle: qsTr("You can’t use it to store new keys right now") + image.source: Theme.png("onboarding/keycard/error") + + pageClassName: "KeycardNotEmptyPage" + + buttons: [ + MaybeOutlineButton { + id: btnReload + text: qsTr("I’ve inserted a Keycard") + anchors.horizontalCenter: parent.horizontalCenter + onClicked: root.reloadKeycardRequested() + }, + MaybeOutlineButton { + id: btnLogin + text: qsTr("Log in with this Keycard") + anchors.horizontalCenter: parent.horizontalCenter + onClicked: root.loginWithThisKeycardRequested() + }, + MaybeOutlineButton { + id: btnFactoryReset + text: qsTr("Factory reset Keycard") + anchors.horizontalCenter: parent.horizontalCenter + onClicked: root.keycardFactoryResetRequested() + } + ] +} diff --git a/ui/app/AppLayouts/Onboarding2/pages/LoginBySyncingPage.qml b/ui/app/AppLayouts/Onboarding2/pages/LoginBySyncingPage.qml new file mode 100644 index 0000000000..166f98f8aa --- /dev/null +++ b/ui/app/AppLayouts/Onboarding2/pages/LoginBySyncingPage.qml @@ -0,0 +1,68 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import StatusQ.Core 0.1 +import StatusQ.Components 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Core.Theme 0.1 + +import shared.views 1.0 +import shared.popups 1.0 + +OnboardingPage { + id: root + + property var validateConnectionString: (stringValue) => { console.error("validateConnectionString IMPLEMENT ME"); return false } + + signal syncProceedWithConnectionString(string connectionString) + + title: qsTr("Log in by syncing") + + pageClassName: "LoginBySyncingPage" + + contentItem: Item { + ColumnLayout { + anchors.centerIn: parent + width: Math.min(440, root.availableWidth) + spacing: Theme.xlPadding + + StatusBaseText { + Layout.fillWidth: true + text: root.title + font.pixelSize: 22 + font.bold: true + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + StatusBaseText { + Layout.fillWidth: true + Layout.topMargin: -12 + text: qsTr("If you have Status on another device") + color: Theme.palette.baseColor1 + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + + SyncingEnterCode { + id: syncView + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + validateConnectionString: root.validateConnectionString + + secondTabName: qsTr("Enter code") + showBetaTag: false + + onDisplayInstructions: instructionsPopup.createObject(root).open() + onProceed: (connectionString) => root.syncProceedWithConnectionString(connectionString) + } + } + } + + Component { + id: instructionsPopup + GetSyncCodeInstructionsPopup { + destroyOnClose: true + } + } +} diff --git a/ui/app/AppLayouts/Onboarding2/pages/LoginPage.qml b/ui/app/AppLayouts/Onboarding2/pages/LoginPage.qml new file mode 100644 index 0000000000..916a7469b8 --- /dev/null +++ b/ui/app/AppLayouts/Onboarding2/pages/LoginPage.qml @@ -0,0 +1,174 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQml.Models 2.15 + +import StatusQ.Core 0.1 +import StatusQ.Components 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Popups 0.1 +import StatusQ.Popups.Dialog 0.1 + +import AppLayouts.Onboarding2.controls 1.0 + +import utils 1.0 + +OnboardingPage { + id: root + + title: qsTr("Log in") + + signal loginWithSeedphraseRequested() + signal loginWithSyncingRequested() + signal loginWithKeycardRequested() + + pageClassName: "LoginPage" + + contentItem: Item { + ColumnLayout { + anchors.centerIn: parent + width: Math.min(380, root.availableWidth) + spacing: 20 + + StatusBaseText { + Layout.fillWidth: true + text: root.title + font.pixelSize: 22 + font.bold: true + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + StatusBaseText { + Layout.fillWidth: true + Layout.topMargin: -12 + text: qsTr("How would you like to log in to Status?") + color: Theme.palette.baseColor1 + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + + OnboardingFrame { + Layout.fillWidth: true + contentItem: ColumnLayout { + spacing: 20 + StatusImage { + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: Math.min(268, parent.width) + Layout.preferredHeight: Math.min(164, height) + source: Theme.png("onboarding/status_seedphrase") + mipmap: true + } + StatusBaseText { + Layout.fillWidth: true + text: qsTr("Log in with recovery phrase") + font.pixelSize: Theme.secondaryAdditionalTextSize + font.bold: true + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + StatusBaseText { + Layout.fillWidth: true + Layout.topMargin: -Theme.padding + text: qsTr("If you have your Status recovery phrase") + font.pixelSize: Theme.additionalTextSize + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + color: Theme.palette.baseColor1 + } + StatusButton { + Layout.fillWidth: true + text: qsTr("Enter recovery phrase") + font.pixelSize: Theme.additionalTextSize + onClicked: root.loginWithSeedphraseRequested() + } + } + } + + OnboardingFrame { + id: buttonFrame + Layout.fillWidth: true + padding: 1 + dropShadow: false + contentItem: ColumnLayout { + spacing: 0 + ListItemButton { + Layout.fillWidth: true + title: qsTr("Log in by syncing") + subTitle: qsTr("If you have Status on another device") + asset.name: Theme.svg("mobile-sync") // FIXME correct icon + onClicked: loginWithSyncAck.createObject(root).open() + } + Rectangle { + Layout.fillWidth: true + Layout.leftMargin: -buttonFrame.padding + Layout.rightMargin: -buttonFrame.padding + Layout.preferredHeight: 1 + color: Theme.palette.statusMenu.separatorColor + } + ListItemButton { + Layout.fillWidth: true + title: qsTr("Log in with Keycard") + subTitle: qsTr("If your profile keys are stored on a Keycard") + asset.name: Theme.png("onboarding/create_profile_keycard") + onClicked: root.loginWithKeycardRequested() + } + } + } + } + } + + Component { + id: loginWithSyncAck + StatusDialog { + title: qsTr("Log in by syncing") + width: 480 + padding: 20 + destroyOnClose: true + contentItem: ColumnLayout { + spacing: 20 + StatusBaseText { + Layout.fillWidth: true + wrapMode: Text.Wrap + text: qsTr("To pair your devices and sync your profile, make sure to check and complete the following steps:") + } + ColumnLayout { + Layout.fillWidth: true + spacing: Theme.padding + StatusCheckBox { + Layout.fillWidth: true + id: ack1 + text: qsTr("Connect both devices to the same network") + } + StatusCheckBox { + Layout.fillWidth: true + id: ack2 + text: qsTr("Make sure you are logged in on the other device") + } + StatusCheckBox { + Layout.fillWidth: true + id: ack3 + text: qsTr("Disable the firewall and VPN on both devices") + } + } + } + footer: StatusDialogFooter { + spacing: Theme.padding + rightButtons: ObjectModel { + StatusFlatButton { + text: qsTr("Cancel") + onClicked: close() + } + StatusButton { + text: qsTr("Continue") + enabled: ack1.checked && ack2.checked && ack3.checked + onClicked: { + root.loginWithSyncingRequested() + close() + } + } + } + } + } + } +} diff --git a/ui/app/AppLayouts/Onboarding2/pages/OnboardingPage.qml b/ui/app/AppLayouts/Onboarding2/pages/OnboardingPage.qml index 5a6a81f04f..6a9efcbc0f 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/OnboardingPage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/OnboardingPage.qml @@ -4,6 +4,8 @@ import QtQuick.Controls 2.15 import StatusQ.Core.Theme 0.1 Page { + required property string pageClassName + signal openLink(string link) signal openLinkWithConfirmation(string link, string domain) diff --git a/ui/app/AppLayouts/Onboarding2/pages/SeedphrasePage.qml b/ui/app/AppLayouts/Onboarding2/pages/SeedphrasePage.qml index 13b7923b5a..ba9d9eeb24 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/SeedphrasePage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/SeedphrasePage.qml @@ -18,6 +18,8 @@ OnboardingPage { signal seedphraseValidated() + pageClassName: "SeedphrasePage" + contentItem: Item { ColumnLayout { anchors.centerIn: parent diff --git a/ui/app/AppLayouts/Onboarding2/pages/WelcomePage.qml b/ui/app/AppLayouts/Onboarding2/pages/WelcomePage.qml index 740394e393..7c58b71c5f 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/WelcomePage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/WelcomePage.qml @@ -15,6 +15,7 @@ import utils 1.0 OnboardingPage { id: root + pageClassName: "WelcomePage" title: qsTr("Welcome to Status") signal createProfileRequested() diff --git a/ui/app/AppLayouts/Onboarding2/pages/qmldir b/ui/app/AppLayouts/Onboarding2/pages/qmldir index 5cc71f3440..58f98e41dc 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/qmldir +++ b/ui/app/AppLayouts/Onboarding2/pages/qmldir @@ -1,15 +1,19 @@ -WelcomePage 1.0 WelcomePage.qml -HelpUsImproveStatusPage 1.0 HelpUsImproveStatusPage.qml -CreateProfilePage 1.0 CreateProfilePage.qml -CreatePasswordPage 1.0 CreatePasswordPage.qml -EnableBiometricsPage 1.0 EnableBiometricsPage.qml -SeedphrasePage 1.0 SeedphrasePage.qml -KeycardIntroPage 1.0 KeycardIntroPage.qml -CreateKeycardProfilePage 1.0 CreateKeycardProfilePage.qml -KeycardCreatePinPage 1.0 KeycardCreatePinPage.qml -KeycardEnterPinPage 1.0 KeycardEnterPinPage.qml -BackupSeedphraseIntro 1.0 BackupSeedphraseIntro.qml BackupSeedphraseAcks 1.0 BackupSeedphraseAcks.qml +BackupSeedphraseIntro 1.0 BackupSeedphraseIntro.qml +BackupSeedphraseOutro 1.0 BackupSeedphraseOutro.qml BackupSeedphraseReveal 1.0 BackupSeedphraseReveal.qml BackupSeedphraseVerify 1.0 BackupSeedphraseVerify.qml -BackupSeedphraseOutro 1.0 BackupSeedphraseOutro.qml +CreateKeycardProfilePage 1.0 CreateKeycardProfilePage.qml +CreatePasswordPage 1.0 CreatePasswordPage.qml +CreateProfilePage 1.0 CreateProfilePage.qml +EnableBiometricsPage 1.0 EnableBiometricsPage.qml +HelpUsImproveStatusPage 1.0 HelpUsImproveStatusPage.qml +KeycardCreatePinPage 1.0 KeycardCreatePinPage.qml +KeycardEmptyPage 1.0 KeycardEmptyPage.qml +KeycardEnterPinPage 1.0 KeycardEnterPinPage.qml +KeycardIntroPage 1.0 KeycardIntroPage.qml +KeycardNotEmptyPage 1.0 KeycardNotEmptyPage.qml +LoginPage 1.0 LoginPage.qml +LoginBySyncingPage 1.0 LoginBySyncingPage.qml +SeedphrasePage 1.0 SeedphrasePage.qml +WelcomePage 1.0 WelcomePage.qml diff --git a/ui/imports/shared/controls/GetSyncCodeDesktopInstructions.qml b/ui/imports/shared/controls/GetSyncCodeDesktopInstructions.qml index 3284528764..60ab2dfb77 100644 --- a/ui/imports/shared/controls/GetSyncCodeDesktopInstructions.qml +++ b/ui/imports/shared/controls/GetSyncCodeDesktopInstructions.qml @@ -71,7 +71,7 @@ Column { if (root.type === SyncingCodeInstructions.Type.EncryptedKey) { return qsTr("Copy the") } - return qsTr("Enable camera") + return qsTr("Enable camera access") } return qsTr("Click") } @@ -122,7 +122,7 @@ Column { } return "" } - return qsTr("Enable camera") + return qsTr("Enable camera access") } text2Color: Theme.palette.directColor1 text3: { diff --git a/ui/imports/shared/controls/StatusSyncCodeScan.qml b/ui/imports/shared/controls/StatusSyncCodeScan.qml index 2266a29d7e..4bdfdc4c96 100644 --- a/ui/imports/shared/controls/StatusSyncCodeScan.qml +++ b/ui/imports/shared/controls/StatusSyncCodeScan.qml @@ -166,7 +166,7 @@ Column { color: Theme.palette.baseColor1 font.pixelSize: Theme.tertiaryTextFontSize horizontalAlignment: Text.AlignHCenter - text: qsTr("Ensure both devices are on the same network") + text: qsTr("Ensure both devices are on the same local network") } StatusBaseText { diff --git a/ui/imports/shared/popups/GetSyncCodeInstructionsPopup.qml b/ui/imports/shared/popups/GetSyncCodeInstructionsPopup.qml index 80e5e95c8e..1807f3f6bc 100644 --- a/ui/imports/shared/popups/GetSyncCodeInstructionsPopup.qml +++ b/ui/imports/shared/popups/GetSyncCodeInstructionsPopup.qml @@ -1,4 +1,4 @@ -import QtQuick 2.14 +import QtQuick 2.15 import StatusQ.Popups.Dialog 0.1 @@ -7,7 +7,7 @@ import shared.views 1.0 StatusDialog { id: root - title: qsTr("How to get a sync code on...") + title: qsTr("How to get a pairing code on...") horizontalPadding: 24 verticalPadding: 32 footer: null diff --git a/ui/imports/shared/views/SyncingEnterCode.qml b/ui/imports/shared/views/SyncingEnterCode.qml index aae94bf460..71e15e11b9 100644 --- a/ui/imports/shared/views/SyncingEnterCode.qml +++ b/ui/imports/shared/views/SyncingEnterCode.qml @@ -1,5 +1,5 @@ -import QtQuick 2.14 -import QtQuick.Layouts 1.14 +import QtQuick 2.15 +import QtQuick.Layouts 1.15 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 @@ -13,48 +13,54 @@ ColumnLayout { id: root property string firstTabName: qsTr("Scan QR code") - property string secondTabName: qsTr("Enter sync code") - property string firstInstructionButtonName: qsTr("How to get a sync code") - property string secondInstructionButtonName: qsTr("How to get a sync code") - property string syncQrErrorMessage: qsTr("This does not look like a sync QR code") - property string syncCodeErrorMessage: qsTr("This does not look like a sync code") - property string syncCodeLabel: qsTr("Paste sync code") + property string secondTabName: qsTr("Enter code") + property string firstInstructionButtonName: qsTr("How to get a pairing code") + property string secondInstructionButtonName: qsTr("How to get a pairing code") + property string syncQrErrorMessage: qsTr("This does not look like a pairing QR code") + property string syncCodeErrorMessage: qsTr("This does not look like a pairing code") + property string syncCodeLabel: qsTr("Type or paste pairing code") + property alias showBetaTag: betaTag.visible property var validateConnectionString: function(stringValue) { return true } - readonly property bool syncViaQr: !switchTabBar.currentIndex + readonly property bool syncViaQr: !switchTabBar.currentIndex signal displayInstructions() signal proceed(string connectionString) - spacing: 8 + spacing: Theme.halfPadding - StatusSwitchTabBar { - id: switchTabBar + RowLayout { + spacing: root.spacing Layout.fillWidth: true - Layout.leftMargin: 16 - Layout.rightMargin: 16 - currentIndex: 0 + Layout.leftMargin: Theme.bigPadding + Layout.rightMargin: Theme.bigPadding - StatusSwitchTabButton { - text: root.firstTabName + StatusSwitchTabBar { + Layout.fillWidth: true + Layout.leftMargin: betaTag.visible ? betaTag.width : 0 + id: switchTabBar + + currentIndex: 0 + + StatusSwitchTabButton { + text: root.firstTabName + } + + StatusSwitchTabButton { + text: root.secondTabName + } } - StatusSwitchTabButton { - text: root.secondTabName + StatusBetaTag { + id: betaTag } } - StatusBetaTag { - anchors.left: switchTabBar.right - anchors.leftMargin: 8 - anchors.verticalCenter: switchTabBar.verticalCenter - } - StackLayout { Layout.fillWidth: true Layout.preferredHeight: Math.max(syncQr.implicitHeight, syncCode.implicitHeight) - Layout.topMargin: 24 + Layout.topMargin: Theme.bigPadding currentIndex: switchTabBar.currentIndex // StackLayout doesn't support alignment, so we create an `Item` wrappers @@ -80,11 +86,12 @@ ColumnLayout { } ColumnLayout { - spacing: 20 + Layout.topMargin: Theme.padding + spacing: Theme.padding StatusSyncCodeInput { id: syncCode Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: 424 + Layout.preferredWidth: 440 mode: StatusSyncCodeInput.Mode.WriteMode label: root.syncCodeLabel @@ -97,30 +104,36 @@ ColumnLayout { validate: root.validateConnectionString } ] - input.onValidChanged: { - if (!input.valid) - return - root.proceed(syncCode.text) - } } StatusBaseText { Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter horizontalAlignment: Text.AlignHCenter color: Theme.palette.baseColor1 font.pixelSize: Theme.tertiaryTextFontSize - text: qsTr("Ensure both devices are on the same network") + text: qsTr("Ensure both devices are on the same local network") + } + StatusButton { + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: Theme.padding + text: qsTr("Continue") + enabled: syncCode.input.valid + onClicked: root.proceed(syncCode.text) } } } StatusFlatButton { + Layout.topMargin: Theme.xlPadding Layout.alignment: Qt.AlignHCenter visible: switchTabBar.currentIndex == 0 && !!root.firstInstructionButtonName || switchTabBar.currentIndex == 1 && !!root.secondInstructionButtonName text: switchTabBar.currentIndex == 0? root.firstInstructionButtonName : root.secondInstructionButtonName + font.pixelSize: Theme.additionalTextSize + normalColor: "transparent" + borderWidth: 1 + borderColor: Theme.palette.baseColor2 onClicked: { root.displayInstructions() }