Skip to content

Commit

Permalink
feat(OnboardingLayout): Decompose into smaller, pure ui sub-flows
Browse files Browse the repository at this point in the history
Closes: #16947
  • Loading branch information
micieslak authored and caybro committed Jan 2, 2025
1 parent 733685f commit 970c7a1
Show file tree
Hide file tree
Showing 13 changed files with 856 additions and 496 deletions.
12 changes: 6 additions & 6 deletions storybook/pages/OnboardingLayoutPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ SplitView {

OnboardingLayout {
id: onboarding

SplitView.fillWidth: true
SplitView.fillHeight: true

networkChecksEnabled: true

onboardingStore: OnboardingStore {
readonly property int keycardState: ctrlKeycardState.currentValue // enum Onboarding.KeycardState
property int keycardRemainingPinAttempts: 5
Expand Down Expand Up @@ -118,9 +121,9 @@ SplitView {
property bool metricsPopupSeen
}

onFinished: (primaryFlow, secondaryFlow, data) => {
console.warn("!!! ONBOARDING FINISHED; primary flow:", primaryFlow, "; secondary:", secondaryFlow, "; data:", JSON.stringify(data))
logs.logEvent("onFinished", ["primaryFlow", "secondaryFlow", "data"], arguments)
onFinished: (flow, data) => {
console.warn("!!! ONBOARDING FINISHED; flow:", flow, "; data:", JSON.stringify(data))
logs.logEvent("onFinished", ["flow", "data"], arguments)

console.warn("!!! SIMULATION: SHOWING SPLASH")
stack.clear()
Expand Down Expand Up @@ -186,9 +189,6 @@ SplitView {
Label {
text: "Current page: %1".arg(onboarding.stack.currentItem ? onboarding.stack.currentItem.pageClassName : "")
}
Label {
text: `Current flow: ${onboarding.primaryFlow} -> ${onboarding.secondaryFlow}`
}
Label {
text: "Stack depth: %1".arg(onboarding.stack.depth)
}
Expand Down
22 changes: 7 additions & 15 deletions storybook/qmlTests/tests/tst_OnboardingLayout.qml
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,7 @@ Item {

// FINISH
tryCompare(finishedSpy, "count", 1)
compare(finishedSpy.signalArguments[0][0], Onboarding.PrimaryFlow.CreateProfile)
compare(finishedSpy.signalArguments[0][1], Onboarding.SecondaryFlow.CreateProfileWithPassword)
compare(finishedSpy.signalArguments[0][0], Onboarding.SecondaryFlow.CreateProfileWithPassword)
}

// FLOW: Create Profile -> Use a recovery phrase (create profile with seedphrase)
Expand Down Expand Up @@ -372,8 +371,7 @@ Item {

// FINISH
tryCompare(finishedSpy, "count", 1)
compare(finishedSpy.signalArguments[0][0], Onboarding.PrimaryFlow.CreateProfile)
compare(finishedSpy.signalArguments[0][1], Onboarding.SecondaryFlow.CreateProfileWithSeedphrase)
compare(finishedSpy.signalArguments[0][0], Onboarding.SecondaryFlow.CreateProfileWithSeedphrase)
}

function test_flow_createProfile_withKeycardAndNewSeedphrase_data() {
Expand Down Expand Up @@ -536,8 +534,7 @@ Item {

// FINISH
tryCompare(finishedSpy, "count", 1)
compare(finishedSpy.signalArguments[0][0], Onboarding.PrimaryFlow.CreateProfile)
compare(finishedSpy.signalArguments[0][1], Onboarding.SecondaryFlow.CreateProfileWithKeycardNewSeedphrase)
compare(finishedSpy.signalArguments[0][0], Onboarding.SecondaryFlow.CreateProfileWithKeycardNewSeedphrase)
}

function test_flow_createProfile_withKeycardAndExistingSeedphrase_data() {
Expand Down Expand Up @@ -642,8 +639,7 @@ Item {

// FINISH
tryCompare(finishedSpy, "count", 1)
compare(finishedSpy.signalArguments[0][0], Onboarding.PrimaryFlow.CreateProfile)
compare(finishedSpy.signalArguments[0][1], Onboarding.SecondaryFlow.CreateProfileWithKeycardExistingSeedphrase)
compare(finishedSpy.signalArguments[0][0], Onboarding.SecondaryFlow.CreateProfileWithKeycardExistingSeedphrase)
}

// FLOW: Log in -> Log in with recovery phrase
Expand Down Expand Up @@ -729,10 +725,8 @@ Item {
compare(dynamicSpy.signalArguments[0][0], data.bioEnabled)
}

// FINISH
tryCompare(finishedSpy, "count", 1)
compare(finishedSpy.signalArguments[0][0], Onboarding.PrimaryFlow.Login)
compare(finishedSpy.signalArguments[0][1], Onboarding.SecondaryFlow.LoginWithSeedphrase)
compare(finishedSpy.signalArguments[0][0], Onboarding.SecondaryFlow.LoginWithSeedphrase)
}

// FLOW: Log in -> Log in by syncing
Expand Down Expand Up @@ -818,8 +812,7 @@ Item {

// FINISH
tryCompare(finishedSpy, "count", 1)
compare(finishedSpy.signalArguments[0][0], Onboarding.PrimaryFlow.Login)
compare(finishedSpy.signalArguments[0][1], Onboarding.SecondaryFlow.LoginWithSyncing)
compare(finishedSpy.signalArguments[0][0], Onboarding.SecondaryFlow.LoginWithSyncing)
}

// FLOW: Log in -> Log in with Keycard
Expand Down Expand Up @@ -878,8 +871,7 @@ Item {

// FINISH
tryCompare(finishedSpy, "count", 1)
compare(finishedSpy.signalArguments[0][0], Onboarding.PrimaryFlow.Login)
compare(finishedSpy.signalArguments[0][1], Onboarding.SecondaryFlow.LoginWithKeycard)
compare(finishedSpy.signalArguments[0][0], Onboarding.SecondaryFlow.LoginWithKeycard)
}
}
}
30 changes: 30 additions & 0 deletions ui/app/AppLayouts/Onboarding2/CreateNewProfileFlow.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import QtQuick 2.15
import QtQuick.Controls 2.15

import StatusQ.Core.Utils 0.1 as SQUtils

import AppLayouts.Onboarding2.pages 1.0


SQUtils.QObject {
id: root

required property StackView stackView
required property var passwordStrengthScoreFunction

signal finished(string password)

function init() {
root.stackView.push(createPasswordPage)
}

Component {
id: createPasswordPage

CreatePasswordPage {
passwordStrengthScoreFunction: root.passwordStrengthScoreFunction

onSetPasswordRequested: root.finished(password)
}
}
}
201 changes: 201 additions & 0 deletions ui/app/AppLayouts/Onboarding2/KeycardCreateProfileFlow.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import QtQuick 2.15
import QtQuick.Controls 2.15

import StatusQ.Core.Utils 0.1 as SQUtils

import AppLayouts.Onboarding2.pages 1.0
import AppLayouts.Onboarding.enums 1.0


SQUtils.QObject {
id: root

required property StackView stackView

required property int keycardState
required property int addKeyPairState

required property var seedWords
required property var isSeedPhraseValid
required property int splashScreenDurationMs

property bool displayKeycardPromoBanner

signal loginWithKeycardRequested
signal keycardFactoryResetRequested
signal keycardPinCreated(string pin)

signal keypairAddTryAgainRequested
signal reloadKeycardRequested
signal createProfileWithoutKeycardRequested

signal finished(bool fromBackupSeedphrase)

function init() {
root.stackView.push(d.initialComponent())
}

QtObject {
id: d

property bool fromBackupSeedphrase

function initialComponent() {
if (root.keycardState === Onboarding.KeycardState.Empty)
return createKeycardProfilePage

if (root.keycardState === Onboarding.KeycardState.NotEmpty)
return keycardNotEmptyPage

return keycardIntroPage
}
}

Component {
id: keycardIntroPage

KeycardIntroPage {
keycardState: root.keycardState
displayPromoBanner: root.displayKeycardPromoBanner

onReloadKeycardRequested: {
root.reloadKeycardRequested()
root.stackView.replace(d.initialComponent(),
StackView.PopTransition)
}

onKeycardFactoryResetRequested: root.keycardFactoryResetRequested()
onEmptyKeycardDetected: root.stackView.replace(createKeycardProfilePage)
onNotEmptyKeycardDetected: root.stackView.replace(keycardNotEmptyPage)
}
}

Component {
id: createKeycardProfilePage

CreateKeycardProfilePage {
onCreateKeycardProfileWithNewSeedphrase:
root.stackView.push(backupSeedIntroPage)
onCreateKeycardProfileWithExistingSeedphrase:
root.stackView.push(seedphrasePage)
}
}

Component {
id: keycardNotEmptyPage

KeycardNotEmptyPage {
onReloadKeycardRequested: {
root.reloadKeycardRequested()
root.stackView.replace(d.initialComponent(),
StackView.PopTransition)
}

onLoginWithThisKeycardRequested: root.loginWithKeycardRequested()
onKeycardFactoryResetRequested: root.keycardFactoryResetRequested()
}
}

Component {
id: backupSeedIntroPage

BackupSeedphraseIntro {
onBackupSeedphraseRequested: root.stackView.push(backupSeedAcksPage)
}
}

Component {
id: backupSeedAcksPage

BackupSeedphraseAcks {
onBackupSeedphraseContinue: root.stackView.push(backupSeedRevealPage)
}
}

Component {
id: backupSeedRevealPage
BackupSeedphraseReveal {
seedWords: root.seedWords

onBackupSeedphraseConfirmed:
root.stackView.push(backupSeedVerifyPage)
}
}

Component {
id: backupSeedVerifyPage
BackupSeedphraseVerify {
seedWordsToVerify: {
const randomIndexes = SQUtils.Utils.nSamples(4, root.seedWords.length)
return randomIndexes.map(i => ({ seedWordNumber: i+1,
seedWord: root.seedWords[i]
}))
}

onBackupSeedphraseVerified: root.stackView.push(backupSeedOutroPage)
}
}

Component {
id: backupSeedOutroPage

BackupSeedphraseOutro {
onBackupSeedphraseRemovalConfirmed:
root.stackView.push(keycardCreatePinPage)
}
}

Component {
id: seedphrasePage

SeedphrasePage {
title: qsTr("Create profile on empty Keycard using a recovery phrase")

isSeedPhraseValid: root.isSeedPhraseValid
onSeedphraseSubmitted: root.stackView.push(keycardCreatePinPage)

StackView.onActivated: d.fromBackupSeedphrase = true
}
}

Component {
id: keycardCreatePinPage

KeycardCreatePinPage {
onKeycardPinCreated: {
root.keycardPinCreated(pin)
root.stackView.push(addKeypairPage)
}
}
}

Component {
id: addKeypairPage

KeycardAddKeyPairPage {
readonly property bool backAvailableHint: false

addKeyPairState: root.addKeyPairState
timeoutInterval: root.splashScreenDurationMs

onKeypairAddContinueRequested: root.finished(d.fromBackupSeedphrase)

onKeypairAddTryAgainRequested: {
root.stackView.replace(addKeypairPage)
root.keypairAddTryAgainRequested()
}

onReloadKeycardRequested: {
root.reloadKeycardRequested()

const page = root.stackView.find(
item => item instanceof CreateKeycardProfilePage)

root.stackView.replace(page, d.initialComponent(),
StackView.PopTransition)
}

onCreateProfilePageRequested: root.createProfileWithoutKeycardRequested()
}
}
}
Loading

0 comments on commit 970c7a1

Please sign in to comment.