fix(@desktop/keycard): onboarding flow `I'm new to Status` -> `Generate keys for a new Keycard` updated

Onboarding flow updated according to the latest related
figma changes.
This commit is contained in:
Sale Djenic 2022-09-28 18:09:12 +02:00 committed by saledjenic
parent 6bf57d3bdd
commit 9be07efec3
19 changed files with 622 additions and 304 deletions

View File

@ -374,6 +374,9 @@ proc seedPhraseRefersToSelectedKeyPair*(self: Controller, seedPhrase: string): b
let accFromSP = self.accountsService.createAccountFromMnemonic(seedPhrase)
return selectedAccount.keyUid == accFromSP.keyUid
proc getLastReceivedKeycardData*(self: Controller): tuple[flowType: string, flowEvent: KeycardEvent] =
return self.keycardService.getLastReceivedKeycardData()
proc cancelCurrentFlow*(self: Controller) =
self.keycardService.cancelCurrentFlow()
# in most cases we're running another flow after canceling the current one,

View File

@ -13,11 +13,13 @@ proc delete*(self: KeycardCreatePinState) =
method executeBackCommand*(self: KeycardCreatePinState, controller: Controller) =
controller.setPin("")
controller.setPinMatch(false)
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.cancelCurrentFlow()
method getNextPrimaryState*(self: KeycardCreatePinState, controller: Controller): State =
if not self.pinValid:
return nil
return createState(StateType.KeycardRepeatPin, self.flowType, self.getBackState)
return createState(StateType.KeycardRepeatPin, self.flowType, self)
method executePrimaryCommand*(self: KeycardCreatePinState, controller: Controller) =
self.pinValid = controller.getPin().len == PINLengthForStatusApp

View File

@ -8,9 +8,13 @@ proc newKeycardInsertKeycardState*(flowType: FlowType, backState: State): Keycar
proc delete*(self: KeycardInsertKeycardState) =
self.State.delete
method executeBackCommand*(self: KeycardInsertKeycardState, controller: Controller) =
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.cancelCurrentFlow()
method resolveKeycardNextState*(self: KeycardInsertKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
let state = ensureReaderAndCardPresenceOnboarding(self, keycardFlowType, keycardEvent, controller)
let state = ensureReaderAndCardPresenceAndResolveNextOnboardingState(self, keycardFlowType, keycardEvent, controller)
if not state.isNil:
return state
if keycardFlowType == ResponseTypeValueInsertCard and
@ -20,5 +24,5 @@ method resolveKeycardNextState*(self: KeycardInsertKeycardState, keycardFlowType
return nil
if keycardFlowType == ResponseTypeValueCardInserted:
controller.setKeycardData("")
return createState(StateType.KeycardReadingKeycard, self.flowType, self.getBackState)
return createState(StateType.KeycardInsertedKeycard, self.flowType, self.getBackState)
return nil

View File

@ -0,0 +1,16 @@
type
KeycardInsertedKeycardState* = ref object of State
proc newKeycardInsertedKeycardState*(flowType: FlowType, backState: State): KeycardInsertedKeycardState =
result = KeycardInsertedKeycardState()
result.setup(flowType, StateType.KeycardInsertedKeycard, backState)
proc delete*(self: KeycardInsertedKeycardState) =
self.State.delete
method executeBackCommand*(self: KeycardInsertedKeycardState, controller: Controller) =
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.cancelCurrentFlow()
method getNextPrimaryState*(self: KeycardInsertedKeycardState, controller: Controller): State =
return createState(StateType.KeycardReadingKeycard, self.flowType, self.getBackState)

View File

@ -1,12 +1,9 @@
type
KeycardLockedState* = ref object of State
KeycardNotKeycardState* = ref object of State
proc newKeycardLockedState*(flowType: FlowType, backState: State): KeycardLockedState =
result = KeycardLockedState()
result.setup(flowType, StateType.KeycardLocked, backState)
proc newKeycardNotKeycardState*(flowType: FlowType, backState: State): KeycardNotKeycardState =
result = KeycardNotKeycardState()
result.setup(flowType, StateType.KeycardNotKeycard, backState)
proc delete*(self: KeycardLockedState) =
self.State.delete
method getNextPrimaryState*(self: KeycardLockedState, controller: Controller): State =
return createState(StateType.KeycardEnterSeedPhraseWords, self.flowType, self)
proc delete*(self: KeycardNotKeycardState) =
self.State.delete

View File

@ -0,0 +1,13 @@
type
KeycardLockedState* = ref object of State
proc newKeycardLockedState*(flowType: FlowType, backState: State): KeycardLockedState =
result = KeycardLockedState()
result.setup(flowType, StateType.KeycardLocked, backState)
proc delete*(self: KeycardLockedState) =
self.State.delete
method executePrimaryCommand*(self: KeycardLockedState, controller: Controller) =
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.runFactoryResetPopup()

View File

@ -8,6 +8,10 @@ proc newKeycardPluginReaderState*(flowType: FlowType, backState: State): Keycard
proc delete*(self: KeycardPluginReaderState) =
self.State.delete
method executeBackCommand*(self: KeycardPluginReaderState, controller: Controller) =
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.cancelCurrentFlow()
method resolveKeycardNextState*(self: KeycardPluginReaderState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextOnboardingState(self, keycardFlowType, keycardEvent, controller)

View File

@ -8,6 +8,16 @@ proc newKeycardReadingKeycardState*(flowType: FlowType, backState: State): Keyca
proc delete*(self: KeycardReadingKeycardState) =
self.State.delete
method executeBackCommand*(self: KeycardReadingKeycardState, controller: Controller) =
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.cancelCurrentFlow()
method getNextPrimaryState*(self: KeycardReadingKeycardState, controller: Controller): State =
let (flowType, flowEvent) = controller.getLastReceivedKeycardData()
# this is used in case a keycard is not inserted in the moment when flow is run (we're animating an insertion)
return ensureReaderAndCardPresenceAndResolveNextOnboardingState(self, flowType, flowEvent, controller)
method resolveKeycardNextState*(self: KeycardReadingKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
# this is used in case a keycard is inserted and we jump to the first meaningful screen
return ensureReaderAndCardPresenceAndResolveNextOnboardingState(self, keycardFlowType, keycardEvent, controller)

View File

@ -0,0 +1,17 @@
type
KeycardRecognizedKeycardState* = ref object of State
proc newKeycardRecognizedKeycardState*(flowType: FlowType, backState: State): KeycardRecognizedKeycardState =
result = KeycardRecognizedKeycardState()
result.setup(flowType, StateType.KeycardRecognizedKeycard, backState)
proc delete*(self: KeycardRecognizedKeycardState) =
self.State.delete
method executeBackCommand*(self: KeycardRecognizedKeycardState, controller: Controller) =
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.cancelCurrentFlow()
method getNextPrimaryState*(self: KeycardRecognizedKeycardState, controller: Controller): State =
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
return createState(StateType.KeycardCreatePin, self.flowType, self.getBackState)

View File

@ -29,7 +29,9 @@ type StateType* {.pure.} = enum
Biometrics = "Biometrics"
KeycardPluginReader = "KeycardPluginReader"
KeycardInsertKeycard = "KeycardInsertKeycard"
KeycardInsertedKeycard = "KeycardInsertedKeycard"
KeycardReadingKeycard = "KeycardReadingKeycard"
KeycardRecognizedKeycard = "KeycardRecognizedKeycard"
KeycardCreatePin = "KeycardCreatePin"
KeycardRepeatPin = "KeycardRepeatPin"
KeycardPinSet = "KeycardPinSet"
@ -41,6 +43,7 @@ type StateType* {.pure.} = enum
KeycardEnterSeedPhraseWords = "KeycardEnterSeedPhraseWords"
KeycardNotEmpty = "KeycardNotEmpty"
KeycardEmpty = "KeycardEmpty"
KeycardNotKeycard = "KeycardNotKeycard"
KeycardLocked = "KeycardLocked"
KeycardRecover = "KeycardRecover"
KeycardMaxPairingSlotsReached = "KeycardMaxPairingSlotsReached"

View File

@ -24,14 +24,17 @@ include keycard_enter_pin_state
include keycard_enter_puk_state
include keycard_enter_seed_phrase_words_state
include keycard_insert_keycard_state
include keycard_inserted_keycard_state
include keycard_locked_state
include keycard_max_pairing_slots_reached_state
include keycard_max_pin_retries_reached_state
include keycard_max_puk_retries_reached_state
include keycard_not_empty_state
include keycard_not_keycard_state
include keycard_pin_set_state
include keycard_plugin_reader_state
include keycard_reading_keycard_state
include keycard_recognized_keycard_state
include keycard_recover_state
include keycard_repeat_pin_state
include keycard_wrong_pin_state
@ -85,8 +88,12 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newKeycardPluginReaderState(flowType, backState)
if stateToBeCreated == StateType.KeycardInsertKeycard:
return newKeycardInsertKeycardState(flowType, backState)
if stateToBeCreated == StateType.KeycardInsertedKeycard:
return newKeycardInsertedKeycardState(flowType, backState)
if stateToBeCreated == StateType.KeycardReadingKeycard:
return newKeycardReadingKeycardState(flowType, backState)
if stateToBeCreated == StateType.KeycardRecognizedKeycard:
return newKeycardRecognizedKeycardState(flowType, backState)
if stateToBeCreated == StateType.KeycardCreatePin:
return newKeycardCreatePinState(flowType, backState)
if stateToBeCreated == StateType.KeycardRepeatPin:
@ -107,6 +114,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newKeycardEnterSeedPhraseWordsState(flowType, backState)
if stateToBeCreated == StateType.KeycardNotEmpty:
return newKeycardNotEmptyState(flowType, backState)
if stateToBeCreated == StateType.KeycardNotKeycard:
return newKeycardNotKeycardState(flowType, backState)
if stateToBeCreated == StateType.KeycardEmpty:
return newKeycardEmptyState(flowType, backState)
if stateToBeCreated == StateType.KeycardLocked:
@ -160,7 +169,7 @@ proc ensureReaderAndCardPresenceOnboarding*(state: State, keycardFlowType: strin
return createState(StateType.KeycardInsertKeycard, state.flowType, state.getBackState)
if keycardFlowType == ResponseTypeValueCardInserted:
controller.setKeycardData("")
return createState(StateType.KeycardReadingKeycard, state.flowType, state.getBackState)
return createState(StateType.KeycardInsertedKeycard, state.flowType, state.getBackState)
proc ensureReaderAndCardPresenceLogin*(state: State, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State =
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
@ -189,15 +198,24 @@ proc ensureReaderAndCardPresenceAndResolveNextOnboardingState*(state: State, key
if keycardFlowType == ResponseTypeValueEnterNewPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorRequireInit:
return createState(StateType.KeycardCreatePin, state.flowType, state.getBackState)
return createState(StateType.KeycardRecognizedKeycard, state.flowType, state.getBackState)
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len == 0:
return createState(StateType.KeycardNotEmpty, state.flowType, state.getBackState)
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len == 0:
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
return createState(StateType.KeycardLocked, state.flowType, state.getBackState)
if keycardFlowType == ResponseTypeValueSwapCard and
keycardEvent.error.len > 0 and
(keycardEvent.error == ErrorHasKeys or
keycardEvent.error == RequestParamPUKRetries):
return createState(StateType.KeycardNotEmpty, state.flowType, state.getBackState)
keycardEvent.error.len > 0:
if keycardEvent.error == ErrorNotAKeycard:
return createState(StateType.KeycardNotKeycard, state.flowType, state.getBackState)
if keycardEvent.error == RequestParamFreeSlots:
return createState(StateType.KeycardLocked, state.flowType, state.getBackState)
if keycardEvent.error == RequestParamPUKRetries:
return createState(StateType.KeycardLocked, state.flowType, state.getBackState)
if keycardEvent.error == ErrorHasKeys:
return createState(StateType.KeycardNotEmpty, state.flowType, state.getBackState)
if keycardFlowType == ResponseTypeValueEnterMnemonic and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorLoadingKeys:

View File

@ -79,7 +79,9 @@ OnboardingBasePage {
}
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPluginReader ||
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardInsertKeycard ||
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardReadingKeycard)
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardInsertedKeycard ||
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardReadingKeycard ||
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRecognizedKeycard)
{
return keycardInitViewComponent
}
@ -100,6 +102,7 @@ OnboardingBasePage {
return seedphraseWordsInputViewComponent
}
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardNotEmpty ||
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardNotKeycard ||
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEmpty ||
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardLocked ||
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRecover ||

View File

@ -4,6 +4,9 @@ import QtQuick.Controls 2.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import shared.popups.keycard.helpers 1.0
import utils 1.0
@ -14,46 +17,55 @@ Item {
property StartupStore startupStore
QtObject {
id: d
property int index: 0
property variant images : [
Style.svg("keycard/card0@2x"),
Style.svg("keycard/card1@2x"),
Style.svg("keycard/card2@2x"),
Style.svg("keycard/card3@2x")
]
}
Timer {
interval: 400
running: true
repeat: true
id: timer
interval: 1000
running: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRecognizedKeycard
onTriggered: {
d.index++
root.startupStore.currentStartupState.doPrimaryAction()
}
}
ColumnLayout {
anchors.centerIn: parent
height: Constants.keycard.general.onboardingHeight
spacing: Style.current.padding
Image {
KeycardImage {
id: image
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
fillMode: Image.PreserveAspectFit
antialiasing: true
source: d.images[d.index % d.images.length]
mipmap: true
Layout.preferredHeight: Constants.keycard.general.imageHeight
Layout.preferredWidth: Constants.keycard.general.imageWidth
onAnimationCompleted: {
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardInsertedKeycard ||
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardReadingKeycard) {
root.startupStore.currentStartupState.doPrimaryAction()
}
}
}
StatusBaseText {
id: title
Layout.alignment: Qt.AlignHCenter
font.weight: Font.Bold
wrapMode: Text.WordWrap
Row {
spacing: Style.current.halfPadding
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: Constants.keycard.general.titleHeight
StatusIcon {
id: icon
visible: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRecognizedKeycard
width: Style.current.padding
height: Style.current.padding
icon: "checkmark"
color: Theme.palette.baseColor1
}
StatusLoadingIndicator {
id: loading
visible: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardReadingKeycard
}
StatusBaseText {
id: title
wrapMode: Text.WordWrap
}
}
StatusBaseText {
@ -61,6 +73,11 @@ Item {
Layout.alignment: Qt.AlignHCenter
wrapMode: Text.WordWrap
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
states: [
@ -72,6 +89,12 @@ Item {
text: qsTr("Plug in Keycard reader...")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
font.weight: Font.Bold
}
PropertyChanges {
target: image
source: Style.png("keycard/empty-reader")
pattern: ""
}
PropertyChanges {
target: info
@ -86,6 +109,17 @@ Item {
text: qsTr("Insert your Keycard...")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
font.weight: Font.Bold
}
PropertyChanges {
target: image
pattern: "keycard/card_insert/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 0
endImgIndex: 16
duration: 1000
loops: 1
}
PropertyChanges {
target: info
@ -95,6 +129,31 @@ Item {
color: Theme.palette.baseColor1
}
},
State {
name: Constants.startupState.keycardInsertedKeycard
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardInsertedKeycard
PropertyChanges {
target: title
text: qsTr("Keycard inserted...")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
font.weight: Font.Bold
}
PropertyChanges {
target: image
pattern: "keycard/card_inserted/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 0
endImgIndex: 29
duration: 1000
loops: 1
}
PropertyChanges {
target: info
visible: false
}
},
State {
name: Constants.startupState.keycardReadingKeycard
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardReadingKeycard
@ -103,6 +162,42 @@ Item {
text: qsTr("Reading Keycard...")
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.baseColor1
font.weight: Font.Bold
}
PropertyChanges {
target: image
pattern: "keycard/warning/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 0
endImgIndex: 55
duration: 3000
loops: 1
}
PropertyChanges {
target: info
visible: false
}
},
State {
name: Constants.startupState.keycardRecognizedKeycard
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRecognizedKeycard
PropertyChanges {
target: title
text: qsTr("Keycard recognized")
font.pixelSize: Constants.keycard.general.fontSize2
font.weight: Font.Normal
color: Theme.palette.baseColor1
}
PropertyChanges {
target: image
pattern: "keycard/success/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 0
endImgIndex: 29
duration: 1300
loops: 1
}
PropertyChanges {
target: info

View File

@ -7,6 +7,8 @@ import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import shared.popups.keycard.helpers 1.0
import utils 1.0
import "../stores"
@ -45,18 +47,14 @@ Item {
ColumnLayout {
anchors.centerIn: parent
height: Constants.keycard.general.onboardingHeight
spacing: Style.current.padding
Image {
KeycardImage {
id: image
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
fillMode: Image.PreserveAspectFit
antialiasing: true
source: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPinSet?
Style.svg("keycard/card-success3@2x") :
Style.svg("keycard/card3@2x")
mipmap: true
Layout.preferredHeight: Constants.keycard.general.imageHeight
Layout.preferredWidth: Constants.keycard.general.imageWidth
}
StatusBaseText {
@ -75,6 +73,9 @@ Item {
enabled: root.startupStore.currentStartupState.stateType !== Constants.startupState.keycardPinSet
onPinInputChanged: {
if (root.state !== Constants.startupState.keycardWrongPin) {
image.source = Style.png("keycard/enter-pin-%1".arg(pinInput.length))
}
if(pinInput.length == 0)
return
if(root.state === Constants.startupState.keycardCreatePin ||
@ -90,6 +91,7 @@ Item {
root.startupStore.doPrimaryAction()
} else {
info.text = qsTr("PINs don't match")
image.source = Style.png("keycard/plain-error")
}
}
}
@ -108,12 +110,22 @@ Item {
font.pixelSize: Constants.keycard.general.fontSize3
wrapMode: Text.WordWrap
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
states: [
State {
name: Constants.startupState.keycardCreatePin
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardCreatePin
PropertyChanges {
target: image
source: Style.png("keycard/enter-pin-0")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Create new Keycard PIN")
@ -130,6 +142,11 @@ Item {
State {
name: Constants.startupState.keycardRepeatPin
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRepeatPin
PropertyChanges {
target: image
source: Style.png("keycard/enter-pin-0")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Repeat Keycard PIN")
@ -147,6 +164,16 @@ Item {
State {
name: Constants.startupState.keycardPinSet
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPinSet
PropertyChanges {
target: image
pattern: "keycard/strong_success/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 0
endImgIndex: 20
duration: 1300
loops: 1
}
PropertyChanges {
target: title
text: qsTr("Keycard PIN set")
@ -163,6 +190,11 @@ Item {
State {
name: Constants.startupState.keycardEnterPin
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEnterPin
PropertyChanges {
target: image
source: Style.png("keycard/card-empty")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Enter Keycard PIN")
@ -179,6 +211,11 @@ Item {
State {
name: Constants.startupState.keycardWrongPin
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardWrongPin
PropertyChanges {
target: image
source: Style.png("keycard/plain-error")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Enter Keycard PIN")

View File

@ -8,6 +8,8 @@ import StatusQ.Controls 0.1
import utils 1.0
import shared.popups.keycard.helpers 1.0
import "../stores"
Item {
@ -15,83 +17,70 @@ Item {
property StartupStore startupStore
Item {
anchors.top: parent.top
anchors.bottom: footerWrapper.top
anchors.left: parent.left
anchors.right: parent.right
ColumnLayout {
anchors.centerIn: parent
height: Constants.keycard.general.onboardingHeight
spacing: Style.current.padding
ColumnLayout {
anchors.centerIn: parent
spacing: Style.current.padding
KeycardImage {
id: image
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: Constants.keycard.general.imageHeight
Layout.preferredWidth: Constants.keycard.general.imageWidth
}
Image {
id: image
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
fillMode: Image.PreserveAspectFit
antialiasing: true
mipmap: true
}
StatusBaseText {
id: title
Layout.alignment: Qt.AlignHCenter
font.weight: Font.Bold
}
StatusBaseText {
id: title
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
}
StatusBaseText {
id: info
Layout.alignment: Qt.AlignHCenter
wrapMode: Text.WordWrap
}
StatusBaseText {
id: info
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize3
wrapMode: Text.WordWrap
StatusButton {
id: button
visible: text.length > 0
Layout.alignment: Qt.AlignHCenter
focus: true
onClicked: {
root.startupStore.doPrimaryAction()
}
}
}
Item {
id: footerWrapper
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: Constants.keycard.general.footerWrapperHeight
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Style.current.bigPadding
StatusButton {
id: button
visible: text.length > 0
Layout.alignment: Qt.AlignHCenter
focus: true
StatusBaseText {
id: link
visible: text.length > 0
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.buttonFontSize
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
parent.font.underline = true
}
onExited: {
parent.font.underline = false
}
onClicked: {
root.startupStore.doPrimaryAction()
root.startupStore.doSecondaryAction()
}
}
}
StatusBaseText {
id: link
visible: text.length > 0
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.buttonFontSize
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
parent.font.underline = true
}
onExited: {
parent.font.underline = false
}
onClicked: {
root.startupStore.doSecondaryAction()
}
}
}
StatusBaseText {
id: message
Layout.alignment: Qt.AlignHCenter
wrapMode: Text.WordWrap
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
@ -101,16 +90,20 @@ Item {
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardNotEmpty
PropertyChanges {
target: image
source: Style.svg("keycard/card3@2x")
source: Style.png("keycard/card-inserted")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("This Keycard already stores keys")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize1
}
PropertyChanges {
target: info
text: qsTr("To generate new keys, you will need to perform a factory reset first")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize2
}
PropertyChanges {
target: button
@ -119,8 +112,13 @@ Item {
}
PropertyChanges {
target: link
text: qsTr("Insert another Keycard")
color: Theme.palette.primaryColor1
text: ""
}
PropertyChanges {
target: message
text: qsTr("Or remove Keycard and insert another Keycard and try again")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize2
}
},
State {
@ -128,7 +126,8 @@ Item {
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEmpty
PropertyChanges {
target: image
source: Style.svg("keycard/card-error3@2x")
source: Style.png("keycard/card-empty")
pattern: ""
}
PropertyChanges {
target: title
@ -138,6 +137,7 @@ Item {
target: info
text: qsTr("The keycard is empty")
color: Theme.palette.dangerColor1
font.pixelSize: Constants.keycard.general.fontSize2
}
PropertyChanges {
target: button
@ -148,22 +148,35 @@ Item {
target: link
text: ""
}
PropertyChanges {
target: message
text: ""
}
},
State {
name: Constants.startupState.keycardLocked
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardLocked
PropertyChanges {
target: image
source: Style.svg("keycard/card-error3@2x")
pattern: "keycard/strong_error/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 18
endImgIndex: 29
duration: 1300
loops: -1
}
PropertyChanges {
target: title
text: qsTr("Keycard locked and already stores keys")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize1
}
PropertyChanges {
target: info
text: qsTr("The Keycard you have inserted is locked, you will need to factory reset it before proceeding")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize2
}
PropertyChanges {
target: button
@ -172,8 +185,50 @@ Item {
}
PropertyChanges {
target: link
text: qsTr("Insert another Keycard")
color: Theme.palette.primaryColor1
text: ""
}
PropertyChanges {
target: message
text: qsTr("Or remove Keycard and insert another Keycard and try again")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize2
}
},
State {
name: Constants.startupState.keycardNotKeycard
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardNotKeycard
PropertyChanges {
target: image
pattern: "keycard/strong_error/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 18
endImgIndex: 29
duration: 1300
loops: -1
}
PropertyChanges {
target: title
text: qsTr("This is not a Keycard")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize1
}
PropertyChanges {
target: info
text: qsTr("The card inserted is not a recognised Keycard, please remove and try and again")
color: Theme.palette.directColor1
}
PropertyChanges {
target: button
text: ""
}
PropertyChanges {
target: link
text: ""
}
PropertyChanges {
target: message
text: ""
}
},
State {
@ -181,17 +236,25 @@ Item {
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardMaxPairingSlotsReached
PropertyChanges {
target: image
source: Style.svg("keycard/card-error3@2x")
pattern: "keycard/strong_error/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 18
endImgIndex: 29
duration: 1300
loops: -1
}
PropertyChanges {
target: title
text: qsTr("Keycard locked")
color: Theme.palette.dangerColor1
font.pixelSize: Constants.keycard.general.fontSize1
}
PropertyChanges {
target: info
text: qsTr("Max pairing slots reached for this keycard")
color: Theme.palette.dangerColor1
font.pixelSize: Constants.keycard.general.fontSize2
}
PropertyChanges {
target: button
@ -203,23 +266,35 @@ Item {
text: qsTr("Insert another Keycard")
color: Theme.palette.primaryColor1
}
PropertyChanges {
target: message
text: ""
}
},
State {
name: Constants.startupState.keycardMaxPukRetriesReached
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardMaxPukRetriesReached
PropertyChanges {
target: image
source: Style.svg("keycard/card-error3@2x")
pattern: "keycard/strong_error/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 18
endImgIndex: 29
duration: 1300
loops: -1
}
PropertyChanges {
target: title
text: qsTr("Keycard locked")
color: Theme.palette.dangerColor1
font.pixelSize: Constants.keycard.general.fontSize1
}
PropertyChanges {
target: info
text: qsTr("Max PUK retries reached for this keycard")
color: Theme.palette.dangerColor1
font.pixelSize: Constants.keycard.general.fontSize2
}
PropertyChanges {
target: button
@ -231,13 +306,23 @@ Item {
text: qsTr("Insert another Keycard")
color: Theme.palette.primaryColor1
}
PropertyChanges {
target: message
text: ""
}
},
State {
name: Constants.startupState.keycardMaxPinRetriesReached
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardMaxPinRetriesReached
PropertyChanges {
target: image
source: Style.svg("keycard/card-error3@2x")
pattern: "keycard/strong_error/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 18
endImgIndex: 29
duration: 1300
loops: -1
}
PropertyChanges {
target: title
@ -257,17 +342,29 @@ Item {
target: link
text: ""
}
PropertyChanges {
target: message
text: ""
}
},
State {
name: Constants.startupState.keycardRecover
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRecover
PropertyChanges {
target: image
source: Style.svg("keycard/card-error3@2x")
pattern: "keycard/strong_error/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 18
endImgIndex: 29
duration: 1300
loops: -1
}
PropertyChanges {
target: title
text: qsTr("Recover your Keycard")
color: Theme.palette.directColor1
font.pixelSize: Constants.keycard.general.fontSize1
}
PropertyChanges {
target: info
@ -283,6 +380,10 @@ Item {
text: qsTr("Recover with PUK")
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: message
text: ""
}
}
]
}

View File

@ -39,85 +39,76 @@ Item {
}
}
Item {
anchors.top: parent.top
anchors.bottom: footerWrapper.top
anchors.left: parent.left
anchors.right: parent.right
ColumnLayout {
anchors.centerIn: parent
height: Constants.keycard.general.onboardingHeight
spacing: Style.current.padding
ColumnLayout {
anchors.centerIn: parent
spacing: Style.current.padding
StatusBaseText {
id: title
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
text: qsTr("Write down your seed phrase")
}
StatusBaseText {
id: title
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
text: qsTr("Write down your seed phrase")
}
StatusBaseText {
id: info
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
horizontalAlignment: Qt.AlignHCenter
text: qsTr("You will need this to recover your Keycard if you loose\nyour PIN of if the wrong PIN is entered five times in a row.")
}
StatusBaseText {
id: info
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize3
color: Theme.palette.dangerColor1
horizontalAlignment: Qt.AlignHCenter
text: qsTr("You will need this to recover your Keycard if you loose\nyour PIN of if the wrong PIN is entered five times in a row.")
}
GridLayout {
id: grid
Layout.alignment: Qt.AlignHCenter
columns: d.numOfColumns
rowSpacing: d.rowSpacing
columnSpacing: d.columnSpacing
height: Constants.keycard.general.seedPhraseHeight
width: Constants.keycard.general.seedPhraseWidth
GridLayout {
id: grid
Layout.alignment: Qt.AlignHCenter
columns: d.numOfColumns
rowSpacing: d.rowSpacing
columnSpacing: d.columnSpacing
Repeater {
model: d.seedPhraseModel
delegate: Item {
Layout.preferredWidth: Constants.keycard.general.seedPhraseCellWidth
Layout.preferredHeight: Constants.keycard.general.seedPhraseCellHeight
StatusBaseText {
id: wordNumber
width: Constants.keycard.general.seedPhraseCellNumberWidth
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Qt.AlignRight
font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
color: Theme.palette.directColor1
text: "%1.".arg(model.index + 1)
}
Repeater {
model: d.seedPhraseModel
delegate: Item {
Layout.preferredWidth: Constants.keycard.general.seedPhraseCellWidth
Layout.preferredHeight: Constants.keycard.general.seedPhraseCellHeight
StatusBaseText {
id: wordNumber
width: Constants.keycard.general.seedPhraseCellNumberWidth
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Qt.AlignRight
font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
color: Theme.palette.directColor1
text: "%1.".arg(model.index + 1)
}
StatusBaseText {
id: word
anchors.left: wordNumber.right
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Style.current.xlPadding
horizontalAlignment: Qt.AlignLeft
font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
color: Theme.palette.directColor1
text: model.modelData
}
StatusBaseText {
id: word
anchors.left: wordNumber.right
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Style.current.xlPadding
horizontalAlignment: Qt.AlignLeft
font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
color: Theme.palette.directColor1
text: model.modelData
}
}
}
}
}
Item {
id: footerWrapper
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: Constants.keycard.general.footerWrapperHeight
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
StatusButton {
anchors.top: parent.top
anchors.topMargin: Style.current.padding
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
text: qsTr("Next")
focus: true
onClicked: {

View File

@ -64,121 +64,117 @@ Item {
}
}
Item {
anchors.top: parent.top
anchors.bottom: footerWrapper.top
anchors.left: parent.left
anchors.right: parent.right
ColumnLayout {
anchors.centerIn: parent
height: Constants.keycard.general.onboardingHeight
spacing: Style.current.padding
ColumnLayout {
anchors.centerIn: parent
spacing: Style.current.padding
StatusBaseText {
id: title
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
text: qsTr("Enter seed phrase words")
}
StatusBaseText {
id: title
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
text: qsTr("Enter seed phrase words")
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
GridLayout {
id: grid
Layout.alignment: Qt.AlignHCenter
columns: d.numOfColumns
rowSpacing: d.rowSpacing
columnSpacing: d.columnSpacing
GridLayout {
id: grid
Layout.alignment: Qt.AlignHCenter
columns: d.numOfColumns
rowSpacing: d.rowSpacing
columnSpacing: d.columnSpacing
height: Constants.keycard.general.enterSeedPhraseWordsHeight
width: Constants.keycard.general.enterSeedPhraseWordsWidth
Component.onCompleted: {
for (var i = 0; i < children.length - 1; ++i) {
if(children[i].inputField && children[i+1].inputField){
children[i].inputField.input.tabNavItem = children[i+1].inputField.input.edit
}
}
}
Repeater {
model: d.wordNumbers
delegate: Item {
Layout.preferredWidth: Constants.keycard.general.seedPhraseCellWidth
Layout.preferredHeight: Constants.keycard.general.seedPhraseCellHeight
property alias inputField: word
property alias wN: wordNumber
StatusBaseText {
id: wordNumber
width: Constants.keycard.general.seedPhraseCellNumberWidth
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Qt.AlignRight
font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
color: Theme.palette.directColor1
text: "%1.".arg(model.modelData + 1)
}
StatusInput {
id: word
anchors.left: wordNumber.right
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Style.current.xlPadding
input.edit.font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
input.acceptReturn: true
onTextChanged: {
if(text.length == 0)
return
if(/(^\s|^\r|^\n)|(\s$|^\r$|^\n$)/.test(text)) {
text = text.trim()
return
}
else if(/\s|\r|\n/.test(text)) {
text = ""
return
}
valid = d.seedPhrases[model.modelData] === text
d.updateValidity(index, valid, text !== "")
}
onKeyPressed: {
if (d.allEntriesValid &&
(input.edit.keyEvent === Qt.Key_Return ||
input.edit.keyEvent === Qt.Key_Enter)) {
event.accepted = true
root.startupStore.doPrimaryAction()
}
}
}
Component.onCompleted: {
for (var i = 0; i < children.length - 1; ++i) {
if(children[i].inputField && children[i+1].inputField){
children[i].inputField.input.tabNavItem = children[i+1].inputField.input.edit
}
}
}
StatusBaseText {
id: info
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize3
color: Theme.palette.dangerColor1
horizontalAlignment: Qt.AlignHCenter
text: d.anyInputDirty && !d.allEntriesValid? qsTr("Invalid word") : ""
Repeater {
model: d.wordNumbers
delegate: Item {
Layout.preferredWidth: Constants.keycard.general.seedPhraseCellWidth
Layout.preferredHeight: Constants.keycard.general.seedPhraseCellHeight
property alias inputField: word
property alias wN: wordNumber
StatusBaseText {
id: wordNumber
width: Constants.keycard.general.seedPhraseCellNumberWidth
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Qt.AlignRight
font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
color: Theme.palette.directColor1
text: "%1.".arg(model.modelData + 1)
}
StatusInput {
id: word
anchors.left: wordNumber.right
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Style.current.xlPadding
input.edit.font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
input.acceptReturn: true
onTextChanged: {
if(text.length == 0)
return
if(/(^\s|^\r|^\n)|(\s$|^\r$|^\n$)/.test(text)) {
text = text.trim()
return
}
else if(/\s|\r|\n/.test(text)) {
text = ""
return
}
valid = d.seedPhrases[model.modelData] === text
d.updateValidity(index, valid, text !== "")
}
onKeyPressed: {
if (d.allEntriesValid &&
(input.edit.keyEvent === Qt.Key_Return ||
input.edit.keyEvent === Qt.Key_Enter)) {
event.accepted = true
root.startupStore.doPrimaryAction()
}
}
}
}
}
}
}
Item {
id: footerWrapper
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: Constants.keycard.general.footerWrapperHeight
StatusBaseText {
id: info
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize3
color: Theme.palette.dangerColor1
horizontalAlignment: Qt.AlignHCenter
text: d.anyInputDirty && !d.allEntriesValid? qsTr("Invalid word") : ""
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
StatusButton {
anchors.top: parent.top
anchors.topMargin: Style.current.padding
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
enabled: d.allEntriesValid
text: qsTr("Next")
text: qsTr("Finish")
onClicked: {
root.startupStore.doPrimaryAction()
}

View File

@ -0,0 +1 @@
KeycardImage 1.0 KeycardImage.qml

View File

@ -39,7 +39,9 @@ QtObject {
readonly property string biometrics: "Biometrics"
readonly property string keycardPluginReader: "KeycardPluginReader"
readonly property string keycardInsertKeycard: "KeycardInsertKeycard"
readonly property string keycardInsertedKeycard: "KeycardInsertedKeycard"
readonly property string keycardReadingKeycard: "KeycardReadingKeycard"
readonly property string keycardRecognizedKeycard: "KeycardRecognizedKeycard"
readonly property string keycardCreatePin: "KeycardCreatePin"
readonly property string keycardRepeatPin: "KeycardRepeatPin"
readonly property string keycardPinSet: "KeycardPinSet"
@ -50,6 +52,7 @@ QtObject {
readonly property string keycardDisplaySeedPhrase: "KeycardDisplaySeedPhrase"
readonly property string keycardEnterSeedPhraseWords: "KeycardEnterSeedPhraseWords"
readonly property string keycardNotEmpty: "KeycardNotEmpty"
readonly property string keycardNotKeycard: "KeycardNotKeycard"
readonly property string keycardEmpty: "KeycardEmpty"
readonly property string keycardLocked: "KeycardLocked"
readonly property string keycardRecover: "KeycardRecover"
@ -382,7 +385,13 @@ QtObject {
readonly property QtObject keycard: QtObject {
readonly property QtObject general: QtObject {
readonly property int footerWrapperHeight: 125
readonly property int onboardingHeight: 460
readonly property int imageWidth: 240
readonly property int imageHeight: 240
readonly property int seedPhraseWidth: 816
readonly property int seedPhraseHeight: 228
readonly property int enterSeedPhraseWordsWidth: 868
readonly property int enterSeedPhraseWordsHeight: 60
readonly property int keycardPinLength: 6
readonly property int keycardPukLength: 12
readonly property int keycardPukAdditionalSpacingOnEvery4Items: 4
@ -397,8 +406,6 @@ QtObject {
readonly property int buttonFontSize: 15
readonly property int pukCellWidth: 50
readonly property int pukCellHeight: 60
readonly property int sharedFlowImageWidth: 240
readonly property int sharedFlowImageHeight: 240
readonly property int popupWidth: 640
readonly property int popupHeight: 640
readonly property int popupBiggerHeight: 766