Onboarding: generic keycard error page

Closes: #17232
This commit is contained in:
Michał Cieślak 2025-02-12 23:34:03 +01:00 committed by Lukáš Tinkl
parent 2f899a87e3
commit 3921460133
7 changed files with 125 additions and 45 deletions

View File

@ -63,7 +63,6 @@ Item {
id: authorizationProgressSelector
label: "Authorization progress"
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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()
}
]
}

View File

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