diff --git a/storybook/pages/KeycardEnterPinPagePage.qml b/storybook/pages/KeycardEnterPinPagePage.qml index fa2798b653..643f0b4dbd 100644 --- a/storybook/pages/KeycardEnterPinPagePage.qml +++ b/storybook/pages/KeycardEnterPinPagePage.qml @@ -63,7 +63,6 @@ Item { id: authorizationProgressSelector label: "Authorization progress" - } } } diff --git a/storybook/pages/UnblockWithPukFlowPage.qml b/storybook/pages/UnblockWithPukFlowPage.qml index 429985a4c1..559c73e013 100644 --- a/storybook/pages/UnblockWithPukFlowPage.qml +++ b/storybook/pages/UnblockWithPukFlowPage.qml @@ -79,9 +79,9 @@ SplitView { id: flow stackView: stackView keycardState: mockDriver.keycardState + pinSettingState: pinSettingStateSelector.value tryToSetPukFunction: mockDriver.setPuk remainingAttempts: mockDriver.keycardRemainingPukAttempts - keycardPinInfoPageDelay: 1000 onSetPinRequested: (pin) => { logs.logEvent("keycardPinCreated", ["pin"], arguments) console.warn("!!! PIN CREATED:", pin) @@ -175,22 +175,10 @@ SplitView { } Repeater { - model: [ - { value: Onboarding.KeycardState.NoPCSCService, text: "NoPCSCService" }, - { value: Onboarding.KeycardState.PluginReader, text: "PluginReader" }, - { value: Onboarding.KeycardState.InsertKeycard, text: "InsertKeycard" }, - { value: Onboarding.KeycardState.ReadingKeycard, text: "ReadingKeycard" }, - { value: Onboarding.KeycardState.WrongKeycard, text: "WrongKeycard" }, - { value: Onboarding.KeycardState.NotKeycard, text: "NotKeycard" }, - { value: Onboarding.KeycardState.MaxPairingSlotsReached, text: "MaxPairingSlotsReached" }, - { value: Onboarding.KeycardState.BlockedPIN, text: "BlockedPIN" }, - { value: Onboarding.KeycardState.BlockedPUK, text: "BlockedPUK" }, - { value: Onboarding.KeycardState.NotEmpty, text: "NotEmpty" }, - { value: Onboarding.KeycardState.Empty, text: "Empty" } - ] + model: Onboarding.getModelFromEnum("KeycardState") RoundButton { - text: modelData.text + text: modelData.name checkable: true checked: flow.keycardState === modelData.value @@ -201,6 +189,12 @@ SplitView { } } } + + ProgressSelector { + id: pinSettingStateSelector + + label: "Pin setting progress" + } } } } diff --git a/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml b/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml index 3a5ae47138..6dbd16c897 100644 --- a/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml +++ b/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml @@ -40,6 +40,8 @@ SQUtils.QObject { required property var validateConnectionString required property var tryToSetPukFunction + readonly property LoginScreen loginScreen: d.loginScreen + signal biometricsRequested(string profileId) signal dismissBiometricsRequested signal loginRequested(string keyUid, int method, var data) @@ -53,7 +55,6 @@ SQUtils.QObject { signal exportKeysRequested signal loadMnemonicRequested signal authorizationRequested(string pin) - signal performKeycardFactoryResetRequested signal linkActivated(string link) @@ -74,8 +75,6 @@ SQUtils.QObject { wrongFingerprint) } - readonly property LoginScreen loginScreen: d.loginScreen - QtObject { id: d @@ -99,6 +98,49 @@ SQUtils.QObject { function openTermsOfUsePopup() { termsOfUsePopup.createObject(root.stackView).open() } + + function openKeycardErrorPageIfStateFailed(state) { + if (state !== Onboarding.ProgressState.Failed) + return + + // find index of first page in the flow + let idx = 0 + const entryItem = stackView.find((item, index) => { + idx = index + // Loader is the type of first page (wrapping welcome page or login screen) + return item instanceof Loader + }) + + // when the initial page is not found, bacause e.g. the flow is not initialized + // or the stack was cleared + if (!entryItem) + return + + // replace the second page in the flow + stackView.replace(stackView.get(idx + 1), errorPage) + } + } + + Connections { + enabled: !(root.stackView.currentItem instanceof Loader) && + !(root.stackView.currentItem instanceof KeycardErrorPage) && + !(root.stackView.currentItem instanceof EnableBiometricsPage) + + function onPinSettingStateChanged() { + d.openKeycardErrorPageIfStateFailed(pinSettingState) + } + + function onAuthorizationStateChanged() { + d.openKeycardErrorPageIfStateFailed(authorizationState) + } + + function onRestoreKeysExportStateChanged() { + d.openKeycardErrorPageIfStateFailed(restoreKeysExportState) + } + + function onAddKeyPairStateChanged() { + d.openKeycardErrorPageIfStateFailed(addKeyPairState) + } } Component { @@ -110,6 +152,17 @@ SQUtils.QObject { } } + Component { + id: errorPage + + KeycardErrorPage { + readonly property bool backAvailableHint: false + + onTryAgainRequested: root.stackView.pop() + onFactoryResetRequested: keycardFactoryResetFlow.init() + } + } + Component { id: welcomePage @@ -337,15 +390,21 @@ SQUtils.QObject { UnblockWithSeedphraseFlow { id: unblockWithSeedphraseFlow + property string pin + stackView: root.stackView isSeedPhraseValid: root.isSeedPhraseValid - keycardPinInfoPageDelay: root.keycardPinInfoPageDelay + pinSettingState: root.pinSettingState onSeedphraseSubmitted: (seedphrase) => root.seedphraseSubmitted(seedphrase) - onSetPinRequested: (pin) => { - root.setPinRequested(pin) + onSetPinRequested: (pin) => { + unblockWithSeedphraseFlow.pin = pin + root.setPinRequested(pin) + } + + onFinished: { if (root.loginScreen) { root.loginRequested(root.loginScreen.selectedProfileKeyId, Onboarding.LoginMethod.Keycard, { pin }) @@ -363,11 +422,10 @@ SQUtils.QObject { stackView: root.stackView keycardState: root.keycardState + pinSettingState: root.pinSettingState tryToSetPukFunction: root.tryToSetPukFunction remainingAttempts: root.remainingPukAttempts - keycardPinInfoPageDelay: root.keycardPinInfoPageDelay - onSetPinRequested: (pin) => { unblockWithPukFlow.pin = pin root.setPinRequested(pin) @@ -422,8 +480,10 @@ SQUtils.QObject { KeycardFactoryResetFlow { id: keycardFactoryResetFlow + stackView: root.stackView keycardState: root.keycardState + onPerformKeycardFactoryResetRequested: root.performKeycardFactoryResetRequested() onFinished: { stackView.clear() diff --git a/ui/app/AppLayouts/Onboarding2/UnblockWithPukFlow.qml b/ui/app/AppLayouts/Onboarding2/UnblockWithPukFlow.qml index 5b984e9b1b..b168e1567c 100644 --- a/ui/app/AppLayouts/Onboarding2/UnblockWithPukFlow.qml +++ b/ui/app/AppLayouts/Onboarding2/UnblockWithPukFlow.qml @@ -4,7 +4,6 @@ import QtQuick.Controls 2.15 import StatusQ.Controls 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Core.Utils 0.1 as SQUtils -import StatusQ.Core.Backpressure 0.1 import AppLayouts.Onboarding2.pages 1.0 import AppLayouts.Onboarding.enums 1.0 @@ -15,11 +14,10 @@ SQUtils.QObject { required property StackView stackView required property int keycardState + required property int pinSettingState required property var tryToSetPukFunction required property int remainingAttempts - required property int keycardPinInfoPageDelay - signal setPinRequested(string pin) signal keycardFactoryResetRequested signal finished(bool success) @@ -34,7 +32,8 @@ SQUtils.QObject { function initialComponent() { if (root.keycardState === Onboarding.KeycardState.BlockedPIN) return keycardEnterPukPage - if (root.keycardState === Onboarding.KeycardState.Empty || root.keycardState === Onboarding.KeycardState.NotEmpty) + if (root.keycardState === Onboarding.KeycardState.Empty || + root.keycardState === Onboarding.KeycardState.NotEmpty) return keycardUnblockedPage return keycardIntroPage } @@ -74,13 +73,13 @@ SQUtils.QObject { Component { id: keycardCreatePinPage - KeycardCreatePinPage { - onSetPinRequested: (pin) => { - Backpressure.debounce(root, root.keycardPinInfoPageDelay, () => { - root.setPinRequested(pin) - root.stackView.replace(keycardUnblockedPage, {title: qsTr("Unblock successful")}) - })() - } + KeycardCreatePinDelayedPage { + pinSettingState: root.pinSettingState + authorizationState: Onboarding.ProgressState.Success // authorization not needed + + onSetPinRequested: root.setPinRequested(pin) + onFinished: root.stackView.replace(keycardUnblockedPage, + { title: qsTr("Unblock successful") }) } } diff --git a/ui/app/AppLayouts/Onboarding2/UnblockWithSeedphraseFlow.qml b/ui/app/AppLayouts/Onboarding2/UnblockWithSeedphraseFlow.qml index 6cd2d7eec4..3e2a0cd21d 100644 --- a/ui/app/AppLayouts/Onboarding2/UnblockWithSeedphraseFlow.qml +++ b/ui/app/AppLayouts/Onboarding2/UnblockWithSeedphraseFlow.qml @@ -1,10 +1,7 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 -import StatusQ.Controls 0.1 -import StatusQ.Core.Theme 0.1 import StatusQ.Core.Utils 0.1 as SQUtils -import StatusQ.Core.Backpressure 0.1 import AppLayouts.Onboarding2.pages 1.0 import AppLayouts.Onboarding.enums 1.0 @@ -15,10 +12,11 @@ SQUtils.QObject { required property StackView stackView required property var isSeedPhraseValid - required property int keycardPinInfoPageDelay + required property int pinSettingState signal seedphraseSubmitted(string seedphrase) signal setPinRequested(string pin) + signal finished function init() { root.stackView.push(seedphrasePage) @@ -42,12 +40,12 @@ SQUtils.QObject { Component { id: keycardCreatePinPage - KeycardCreatePinPage { - onSetPinRequested: (pin) => { - Backpressure.debounce(root, root.keycardPinInfoPageDelay, () => { - root.setPinRequested(pin) - })() - } + KeycardCreatePinDelayedPage { + pinSettingState: root.pinSettingState + authorizationState: Onboarding.ProgressState.Success // authorization not needed + + onSetPinRequested: root.setPinRequested(pin) + onFinished: root.finished() } } } diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardErrorPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardErrorPage.qml new file mode 100644 index 0000000000..2bf868bee5 --- /dev/null +++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardErrorPage.qml @@ -0,0 +1,29 @@ +import QtQuick 2.15 + +import StatusQ.Core.Theme 0.1 + +import AppLayouts.Onboarding2.controls 1.0 + +KeycardBasePage { + id: root + + signal tryAgainRequested() + signal factoryResetRequested() + + title: qsTr("Communication with Keycard lost") + subtitle: qsTr("There seems to be an issue communicating with your Keycard. Reinsert the card or reader and try again.") + image.source: Theme.png("onboarding/keycard/error") + + buttons: [ + MaybeOutlineButton { + text: qsTr("Try again") + anchors.horizontalCenter: parent.horizontalCenter + onClicked: root.tryAgainRequested() + }, + MaybeOutlineButton { + text: qsTr("Factory reset Keycard") + anchors.horizontalCenter: parent.horizontalCenter + onClicked: root.factoryResetRequested() + } + ] +} diff --git a/ui/app/AppLayouts/Onboarding2/pages/qmldir b/ui/app/AppLayouts/Onboarding2/pages/qmldir index 3b84b2f594..d2edecc499 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/qmldir +++ b/ui/app/AppLayouts/Onboarding2/pages/qmldir @@ -16,6 +16,7 @@ KeycardCreatePinPage 1.0 KeycardCreatePinPage.qml KeycardEmptyPage 1.0 KeycardEmptyPage.qml KeycardEnterPinPage 1.0 KeycardEnterPinPage.qml KeycardEnterPukPage 1.0 KeycardEnterPukPage.qml +KeycardErrorPage 1.0 KeycardErrorPage.qml KeycardExtractingKeysPage 1.0 KeycardExtractingKeysPage.qml KeycardIntroPage 1.0 KeycardIntroPage.qml KeycardLostPage 1.0 KeycardLostPage.qml