2024-11-06 00:39:08 +01:00
|
|
|
import QtQuick 2.15
|
|
|
|
import QtQuick.Controls 2.15
|
|
|
|
import QtQuick.Layouts 1.15
|
|
|
|
|
|
|
|
import StatusQ 0.1
|
|
|
|
|
2025-01-09 12:41:26 +01:00
|
|
|
import AppLayouts.Onboarding.enums 1.0
|
2024-11-06 00:39:08 +01:00
|
|
|
import AppLayouts.Onboarding2 1.0
|
2025-01-09 12:41:26 +01:00
|
|
|
import AppLayouts.Onboarding2.pages 1.0
|
2024-11-06 00:39:08 +01:00
|
|
|
import AppLayouts.Onboarding2.stores 1.0
|
|
|
|
|
|
|
|
import shared.panels 1.0
|
|
|
|
import shared.stores 1.0 as SharedStores
|
2025-01-09 12:41:26 +01:00
|
|
|
import utils 1.0
|
|
|
|
|
|
|
|
import Storybook 1.0
|
2024-11-06 00:39:08 +01:00
|
|
|
|
|
|
|
SplitView {
|
|
|
|
id: root
|
|
|
|
orientation: Qt.Vertical
|
|
|
|
|
|
|
|
Logs { id: logs }
|
|
|
|
|
|
|
|
QtObject {
|
|
|
|
id: mockDriver
|
2025-01-09 12:41:26 +01:00
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
readonly property string mnemonic: "dog dog dog dog dog dog dog dog dog dog dog dog"
|
|
|
|
readonly property var seedWords: ["apple", "banana", "cat", "cow", "catalog", "catch", "category", "cattle", "dog", "elephant", "fish", "grape"]
|
2025-01-09 12:41:26 +01:00
|
|
|
readonly property string pin: "111111"
|
2024-11-06 00:39:08 +01:00
|
|
|
|
|
|
|
// TODO simulation
|
|
|
|
function restart() {
|
|
|
|
// add keypair state
|
|
|
|
// sync state
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OnboardingLayout {
|
|
|
|
id: onboarding
|
2024-12-17 13:26:04 +01:00
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
SplitView.fillWidth: true
|
|
|
|
SplitView.fillHeight: true
|
2024-12-17 13:26:04 +01:00
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
onboardingStore: OnboardingStore {
|
2025-01-09 12:41:26 +01:00
|
|
|
id: store
|
|
|
|
|
|
|
|
property int keycardState: Onboarding.KeycardState.NoPCSCService
|
|
|
|
property int addKeyPairState: Onboarding.AddKeyPairState.InProgress
|
|
|
|
property int syncState: Onboarding.SyncState.InProgress
|
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
property int keycardRemainingPinAttempts: 5
|
|
|
|
|
|
|
|
function setPin(pin: string) { // -> bool
|
|
|
|
logs.logEvent("OnboardingStore.setPin", ["pin"], arguments)
|
2025-01-09 12:41:26 +01:00
|
|
|
const valid = pin === mockDriver.pin
|
2024-11-06 00:39:08 +01:00
|
|
|
if (!valid)
|
|
|
|
keycardRemainingPinAttempts--
|
|
|
|
return valid
|
|
|
|
}
|
|
|
|
|
|
|
|
function startKeypairTransfer() { // -> void
|
|
|
|
logs.logEvent("OnboardingStore.startKeypairTransfer")
|
|
|
|
addKeyPairState = Onboarding.AddKeyPairState.InProgress
|
|
|
|
}
|
|
|
|
|
|
|
|
// password
|
|
|
|
function getPasswordStrengthScore(password: string) { // -> int
|
|
|
|
logs.logEvent("OnboardingStore.getPasswordStrengthScore", ["password"], arguments)
|
|
|
|
return Math.min(password.length-1, 4)
|
|
|
|
}
|
|
|
|
|
|
|
|
// seedphrase/mnemonic
|
|
|
|
function validMnemonic(mnemonic: string) { // -> bool
|
|
|
|
logs.logEvent("OnboardingStore.validMnemonic", ["mnemonic"], arguments)
|
|
|
|
return mnemonic === mockDriver.mnemonic
|
|
|
|
}
|
2025-01-09 12:41:26 +01:00
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
function getMnemonic() { // -> string
|
|
|
|
logs.logEvent("OnboardingStore.getMnemonic()")
|
|
|
|
return mockDriver.seedWords.join(" ")
|
|
|
|
}
|
2025-01-09 12:41:26 +01:00
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
function mnemonicWasShown() { // -> void
|
|
|
|
logs.logEvent("OnboardingStore.mnemonicWasShown()")
|
|
|
|
}
|
2025-01-09 12:41:26 +01:00
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
function removeMnemonic() { // -> void
|
|
|
|
logs.logEvent("OnboardingStore.removeMnemonic()")
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateLocalPairingConnectionString(connectionString: string) { // -> bool
|
|
|
|
logs.logEvent("OnboardingStore.validateLocalPairingConnectionString", ["connectionString"], arguments)
|
|
|
|
return !Number.isNaN(parseInt(connectionString))
|
|
|
|
}
|
2025-01-09 12:41:26 +01:00
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
function inputConnectionStringForBootstrapping(connectionString: string) { // -> void
|
|
|
|
logs.logEvent("OnboardingStore.inputConnectionStringForBootstrapping", ["connectionString"], arguments)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
metricsStore: SharedStores.MetricsStore {
|
|
|
|
readonly property var d: QtObject {
|
|
|
|
id: d
|
|
|
|
property bool isCentralizedMetricsEnabled
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggleCentralizedMetrics(enabled) {
|
|
|
|
d.isCentralizedMetricsEnabled = enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
function addCentralizedMetricIfEnabled(eventName, eventValue = null) {}
|
|
|
|
|
|
|
|
readonly property bool isCentralizedMetricsEnabled : d.isCentralizedMetricsEnabled
|
|
|
|
}
|
|
|
|
|
|
|
|
splashScreenDurationMs: 3000
|
|
|
|
biometricsAvailable: ctrlBiometrics.checked
|
|
|
|
|
|
|
|
QtObject {
|
|
|
|
id: localAppSettings
|
|
|
|
property bool metricsPopupSeen
|
|
|
|
}
|
|
|
|
|
2024-12-17 13:26:04 +01:00
|
|
|
onFinished: (flow, data) => {
|
|
|
|
console.warn("!!! ONBOARDING FINISHED; flow:", flow, "; data:", JSON.stringify(data))
|
|
|
|
logs.logEvent("onFinished", ["flow", "data"], arguments)
|
2024-11-06 00:39:08 +01:00
|
|
|
|
|
|
|
console.warn("!!! SIMULATION: SHOWING SPLASH")
|
|
|
|
stack.clear()
|
|
|
|
stack.push(splashScreen, { runningProgressAnimation: true })
|
2025-01-09 12:41:26 +01:00
|
|
|
|
|
|
|
flow.currentKeycardState = Onboarding.KeycardState.NoPCSCService
|
2024-11-06 00:39:08 +01:00
|
|
|
}
|
|
|
|
onKeycardFactoryResetRequested: {
|
|
|
|
logs.logEvent("onKeycardFactoryResetRequested")
|
|
|
|
console.warn("!!! FACTORY RESET; RESTARTING FLOW")
|
|
|
|
restartFlow()
|
2025-01-09 12:41:26 +01:00
|
|
|
flow.currentKeycardState = Onboarding.KeycardState.NoPCSCService
|
2024-11-06 00:39:08 +01:00
|
|
|
}
|
|
|
|
onKeycardReloaded: {
|
|
|
|
logs.logEvent("onKeycardReloaded")
|
|
|
|
console.warn("!!! RELOAD KEYCARD")
|
2025-01-09 12:41:26 +01:00
|
|
|
flow.currentKeycardState = Onboarding.KeycardState.NoPCSCService
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
text: "Paste password"
|
|
|
|
focusPolicy: Qt.NoFocus
|
|
|
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.margins: 10
|
|
|
|
|
|
|
|
visible: onboarding.stack.currentItem instanceof CreatePasswordPage
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
const password = "somepassword"
|
|
|
|
const currentItem = onboarding.stack.currentItem
|
|
|
|
|
|
|
|
const input1 = StorybookUtils.findChild(
|
|
|
|
currentItem,
|
|
|
|
"passwordViewNewPassword")
|
|
|
|
const input2 = StorybookUtils.findChild(
|
|
|
|
currentItem,
|
|
|
|
"passwordViewNewPasswordConfirm")
|
|
|
|
|
|
|
|
if (!input1 || !input2)
|
|
|
|
return
|
|
|
|
|
|
|
|
input1.text = password
|
|
|
|
input2.text = password
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
text: "Paste seed phrase"
|
|
|
|
focusPolicy: Qt.NoFocus
|
|
|
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.margins: 10
|
|
|
|
|
|
|
|
visible: onboarding.stack.currentItem instanceof SeedphrasePage
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
for (let i = 1;; i++) {
|
|
|
|
const input = StorybookUtils.findChild(
|
|
|
|
onboarding.stack.currentItem,
|
|
|
|
`enterSeedPhraseInputField${i}`)
|
|
|
|
|
|
|
|
if (input === null)
|
|
|
|
break
|
|
|
|
|
|
|
|
input.text = "dog"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.margins: 10
|
|
|
|
|
|
|
|
visible: onboarding.stack.currentItem instanceof KeycardEnterPinPage ||
|
|
|
|
onboarding.stack.currentItem instanceof KeycardCreatePinPage
|
|
|
|
|
|
|
|
text: "Copy valid PIN (\"%1\")".arg(mockDriver.pin)
|
|
|
|
focusPolicy: Qt.NoFocus
|
|
|
|
onClicked: ClipboardUtils.setText(mockDriver.pin)
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.margins: 10
|
|
|
|
|
|
|
|
visible: onboarding.stack.currentItem instanceof BackupSeedphraseVerify
|
|
|
|
|
|
|
|
text: "Paste seed phrase verification"
|
|
|
|
focusPolicy: Qt.NoFocus
|
|
|
|
onClicked: {
|
|
|
|
for (let i = 0;; i++) {
|
|
|
|
const input = StorybookUtils.findChild(
|
|
|
|
onboarding.stack.currentItem,
|
|
|
|
`seedInput_${i}`)
|
|
|
|
|
|
|
|
if (input === null)
|
|
|
|
break
|
|
|
|
|
|
|
|
const index = input.seedWordIndex
|
|
|
|
input.text = mockDriver.seedWords[index]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.margins: 10
|
|
|
|
|
|
|
|
visible: onboarding.stack.currentItem instanceof BackupSeedphraseAcks
|
|
|
|
|
|
|
|
text: "Paste seed phrase verification"
|
|
|
|
focusPolicy: Qt.NoFocus
|
|
|
|
onClicked: {
|
|
|
|
for (let i = 1;; i++) {
|
|
|
|
const checkBox = StorybookUtils.findChild(
|
|
|
|
onboarding.stack.currentItem,
|
|
|
|
`ack${i}`)
|
|
|
|
|
|
|
|
if (checkBox === null)
|
|
|
|
break
|
|
|
|
|
|
|
|
checkBox.checked = true
|
|
|
|
}
|
|
|
|
}
|
2024-11-06 00:39:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Component {
|
|
|
|
id: splashScreen
|
2025-01-09 12:41:26 +01:00
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
DidYouKnowSplashScreen {
|
|
|
|
property bool runningProgressAnimation
|
|
|
|
NumberAnimation on progress {
|
|
|
|
from: 0.0
|
|
|
|
to: 1
|
|
|
|
duration: onboarding.splashScreenDurationMs
|
|
|
|
running: runningProgressAnimation
|
|
|
|
onStopped: {
|
|
|
|
console.warn("!!! SPLASH SCREEN DONE")
|
|
|
|
console.warn("!!! RESTARTING FLOW")
|
|
|
|
onboarding.restartFlow()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
target: Global
|
2025-01-09 12:41:26 +01:00
|
|
|
|
2024-11-06 00:39:08 +01:00
|
|
|
function onOpenLink(link: string) {
|
|
|
|
console.warn("Opening link in an external web browser:", link)
|
|
|
|
Qt.openUrlExternally(link)
|
|
|
|
}
|
|
|
|
function onOpenLinkWithConfirmation(link: string, domain: string) {
|
|
|
|
console.warn("Opening link in an external web browser:", link, domain)
|
|
|
|
Qt.openUrlExternally(link)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LogsAndControlsPanel {
|
|
|
|
id: logsAndControlsPanel
|
|
|
|
|
2025-01-09 12:41:26 +01:00
|
|
|
SplitView.minimumHeight: 250
|
|
|
|
SplitView.preferredHeight: 250
|
2024-11-06 00:39:08 +01:00
|
|
|
|
|
|
|
logsView.logText: logs.logText
|
|
|
|
|
2025-01-09 12:41:26 +01:00
|
|
|
ColumnLayout {
|
2024-11-06 00:39:08 +01:00
|
|
|
anchors.fill: parent
|
2025-01-09 12:41:26 +01:00
|
|
|
|
|
|
|
spacing: 10
|
|
|
|
|
|
|
|
Label {
|
2024-11-06 00:39:08 +01:00
|
|
|
Layout.fillWidth: true
|
2025-01-09 12:41:26 +01:00
|
|
|
|
|
|
|
text: {
|
|
|
|
const stack = onboarding.stack
|
|
|
|
let content = `Stack (${stack.depth}):`
|
|
|
|
|
|
|
|
for (let i = 0; i < stack.depth; i++)
|
|
|
|
content += " " + InspectionUtils.baseName(
|
|
|
|
stack.get(i, StackView.ForceLoad))
|
|
|
|
|
|
|
|
return content
|
2024-11-06 00:39:08 +01:00
|
|
|
}
|
2025-01-09 12:41:26 +01:00
|
|
|
|
|
|
|
wrapMode: Text.Wrap
|
2024-11-06 00:39:08 +01:00
|
|
|
}
|
2025-01-09 12:41:26 +01:00
|
|
|
|
|
|
|
RowLayout {
|
2024-11-06 00:39:08 +01:00
|
|
|
Layout.fillWidth: true
|
2024-12-17 17:45:49 +01:00
|
|
|
|
2025-01-09 12:41:26 +01:00
|
|
|
Button {
|
|
|
|
text: "Restart"
|
|
|
|
focusPolicy: Qt.NoFocus
|
|
|
|
onClicked: onboarding.restartFlow()
|
2024-11-06 00:39:08 +01:00
|
|
|
}
|
2025-01-09 12:41:26 +01:00
|
|
|
|
|
|
|
Switch {
|
|
|
|
id: ctrlBiometrics
|
|
|
|
text: "Biometrics available"
|
|
|
|
checked: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
Label {
|
|
|
|
text: "Keycard state:"
|
|
|
|
}
|
|
|
|
|
|
|
|
Flow {
|
|
|
|
Layout.fillWidth: true
|
|
|
|
spacing: 2
|
|
|
|
|
|
|
|
ButtonGroup {
|
|
|
|
id: keycardStateButtonGroup
|
2024-11-06 00:39:08 +01:00
|
|
|
}
|
2025-01-09 12:41:26 +01:00
|
|
|
|
|
|
|
Repeater {
|
2024-11-06 00:39:08 +01:00
|
|
|
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.Locked, text: "Locked" },
|
|
|
|
{ value: Onboarding.KeycardState.NotEmpty, text: "NotEmpty" },
|
|
|
|
{ value: Onboarding.KeycardState.Empty, text: "Empty" }
|
|
|
|
]
|
2025-01-09 12:41:26 +01:00
|
|
|
|
|
|
|
RoundButton {
|
|
|
|
text: modelData.text
|
|
|
|
checkable: true
|
|
|
|
checked: store.keycardState === modelData.value
|
|
|
|
|
|
|
|
ButtonGroup.group: keycardStateButtonGroup
|
|
|
|
|
|
|
|
onClicked: store.keycardState = modelData.value
|
|
|
|
}
|
2024-11-06 00:39:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-01-09 12:41:26 +01:00
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
Label {
|
|
|
|
text: "Add key pair state:"
|
|
|
|
}
|
|
|
|
|
|
|
|
Flow {
|
|
|
|
spacing: 2
|
|
|
|
|
|
|
|
ButtonGroup {
|
|
|
|
id: addKeypairStateButtonGroup
|
|
|
|
}
|
|
|
|
|
|
|
|
Repeater {
|
|
|
|
model: [
|
|
|
|
{ value: Onboarding.AddKeyPairState.InProgress, text: "InProgress" },
|
|
|
|
{ value: Onboarding.AddKeyPairState.Success, text: "Success" },
|
|
|
|
{ value: Onboarding.AddKeyPairState.Failed, text: "Failed" }
|
|
|
|
]
|
|
|
|
|
|
|
|
RoundButton {
|
|
|
|
text: modelData.text
|
|
|
|
checkable: true
|
|
|
|
checked: store.addKeyPairState === modelData.value
|
|
|
|
|
|
|
|
ButtonGroup.group: addKeypairStateButtonGroup
|
|
|
|
|
|
|
|
onClicked: store.addKeyPairState = modelData.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ToolSeparator {}
|
|
|
|
|
|
|
|
Label {
|
|
|
|
text: "Sync state:"
|
|
|
|
}
|
|
|
|
|
|
|
|
Flow {
|
|
|
|
Layout.fillWidth: true
|
|
|
|
spacing: 2
|
|
|
|
|
|
|
|
ButtonGroup {
|
|
|
|
id: syncStateButtonGroup
|
|
|
|
}
|
|
|
|
|
|
|
|
Repeater {
|
|
|
|
model: [
|
|
|
|
{ value: Onboarding.SyncState.InProgress, text: "InProgress" },
|
|
|
|
{ value: Onboarding.SyncState.Success, text: "Success" },
|
|
|
|
{ value: Onboarding.SyncState.Failed, text: "Failed" }
|
|
|
|
]
|
|
|
|
|
|
|
|
RoundButton {
|
|
|
|
text: modelData.text
|
|
|
|
checkable: true
|
|
|
|
checked: store.syncState === modelData.value
|
|
|
|
|
|
|
|
ButtonGroup.group: syncStateButtonGroup
|
|
|
|
|
|
|
|
onClicked: store.syncState = modelData.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Item {
|
|
|
|
Layout.fillHeight: true
|
|
|
|
}
|
2024-11-06 00:39:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// category: Onboarding
|
|
|
|
// status: good
|
|
|
|
// https://www.figma.com/design/Lw4nPYQcZOPOwTgETiiIYo/Desktop-Onboarding-Redesign?node-id=1-25&node-type=canvas&m=dev
|