draft authentication dialogs
This commit is contained in:
parent
21b20fbc38
commit
3d46e62345
|
@ -0,0 +1,24 @@
|
||||||
|
import NimQml, chronicles, std/wrapnils
|
||||||
|
import status/status
|
||||||
|
import view
|
||||||
|
|
||||||
|
type KeycardController* = ref object
|
||||||
|
view*: KeycardView
|
||||||
|
variant*: QVariant
|
||||||
|
status: Status
|
||||||
|
|
||||||
|
proc newController*(status: Status): KeycardController =
|
||||||
|
result = KeycardController()
|
||||||
|
result.status = status
|
||||||
|
result.view = newKeycardView(status)
|
||||||
|
result.variant = newQVariant(result.view)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardController) =
|
||||||
|
delete self.variant
|
||||||
|
delete self.view
|
||||||
|
|
||||||
|
proc reset*(self: KeycardController) =
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc init*(self: KeycardController) =
|
||||||
|
discard
|
|
@ -0,0 +1,18 @@
|
||||||
|
import NimQml
|
||||||
|
import status/status
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type KeycardView* = ref object of QObject
|
||||||
|
status*: Status
|
||||||
|
|
||||||
|
proc setup(self: KeycardView) =
|
||||||
|
self.QObject.setup
|
||||||
|
|
||||||
|
proc delete*(self: KeycardView) =
|
||||||
|
self.QObject.delete
|
||||||
|
|
||||||
|
proc newKeycardView*(status: Status): KeycardView =
|
||||||
|
new(result, delete)
|
||||||
|
result.status = status
|
||||||
|
result.setup
|
||||||
|
|
|
@ -11,6 +11,7 @@ import app/profile/view
|
||||||
import app/onboarding/core as onboarding
|
import app/onboarding/core as onboarding
|
||||||
import app/login/core as login
|
import app/login/core as login
|
||||||
import app/provider/core as provider
|
import app/provider/core as provider
|
||||||
|
import app/keycard/core as keycard
|
||||||
import status/types/[account]
|
import status/types/[account]
|
||||||
import status_go
|
import status_go
|
||||||
import status/status as statuslib
|
import status/status as statuslib
|
||||||
|
@ -192,6 +193,8 @@ proc mainProc() =
|
||||||
defer: login.delete()
|
defer: login.delete()
|
||||||
var onboarding = onboarding.newController(status)
|
var onboarding = onboarding.newController(status)
|
||||||
defer: onboarding.delete()
|
defer: onboarding.delete()
|
||||||
|
var keycard = keycard.newController(status)
|
||||||
|
defer: keycard.delete()
|
||||||
|
|
||||||
proc onAccountChanged(account: Account) =
|
proc onAccountChanged(account: Account) =
|
||||||
profile.view.setAccountSettingsFile(account.name)
|
profile.view.setAccountSettingsFile(account.name)
|
||||||
|
@ -234,6 +237,7 @@ proc mainProc() =
|
||||||
|
|
||||||
engine.setRootContextProperty("loginModel", login.variant)
|
engine.setRootContextProperty("loginModel", login.variant)
|
||||||
engine.setRootContextProperty("onboardingModel", onboarding.variant)
|
engine.setRootContextProperty("onboardingModel", onboarding.variant)
|
||||||
|
engine.setRootContextProperty("keycardModel", keycard.variant)
|
||||||
engine.setRootContextProperty("singleInstance", newQVariant(singleInstance))
|
engine.setRootContextProperty("singleInstance", newQVariant(singleInstance))
|
||||||
|
|
||||||
let isExperimental = if getEnv("EXPERIMENTAL") == "1": "1" else: "0" # value explicity passed to avoid trusting input
|
let isExperimental = if getEnv("EXPERIMENTAL") == "1": "1" else: "0" # value explicity passed to avoid trusting input
|
||||||
|
@ -245,6 +249,7 @@ proc mainProc() =
|
||||||
proc initControllers() =
|
proc initControllers() =
|
||||||
login.init()
|
login.init()
|
||||||
onboarding.init()
|
onboarding.init()
|
||||||
|
keycard.init()
|
||||||
|
|
||||||
initControllers()
|
initControllers()
|
||||||
|
|
||||||
|
|
|
@ -598,6 +598,31 @@ QtObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validatePINs(item, firstPINField, repeatPINField) {
|
||||||
|
switch (item) {
|
||||||
|
case "first":
|
||||||
|
if (firstPINField.text === "") {
|
||||||
|
return [false, qsTr("You need to enter a PIN")];
|
||||||
|
} else if (!/^\d+$/.test(firstPINField.text)) {
|
||||||
|
return [false, qsTr("The PIN must contain only digits")];
|
||||||
|
} else if (firstPINField.text.length != 6) {
|
||||||
|
return [false, qsTr("The PIN must be exactly 6 digits")];
|
||||||
|
}
|
||||||
|
return [true, ""];
|
||||||
|
|
||||||
|
case "repeat":
|
||||||
|
if (repeatPINField.text === "") {
|
||||||
|
return [false, qsTr("You need to repeat your PIN")];
|
||||||
|
} else if (repeatPINField.text !== firstPINField.text) {
|
||||||
|
return [false, qsTr("PIN don't match")];
|
||||||
|
}
|
||||||
|
return [true, ""];
|
||||||
|
|
||||||
|
default:
|
||||||
|
return [false, ""];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getHostname(url) {
|
function getHostname(url) {
|
||||||
const rgx = /\:\/\/(?:[a-zA-Z0-9\-]*\.{1,}){1,}[a-zA-Z0-9]*/i
|
const rgx = /\:\/\/(?:[a-zA-Z0-9\-]*\.{1,}){1,}[a-zA-Z0-9]*/i
|
||||||
const matches = rgx.exec(url)
|
const matches = rgx.exec(url)
|
||||||
|
|
30
ui/main.qml
30
ui/main.qml
|
@ -373,6 +373,16 @@ StatusWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DSM.State {
|
||||||
|
id: keycardState
|
||||||
|
onEntered: loader.sourceComponent = keycard
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: appState
|
||||||
|
signal: onboardingModel.moveToAppState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DSM.State {
|
DSM.State {
|
||||||
id: stateLogin
|
id: stateLogin
|
||||||
onEntered: loader.sourceComponent = login
|
onEntered: loader.sourceComponent = login
|
||||||
|
@ -407,6 +417,12 @@ StatusWindow {
|
||||||
guard: path === "KeysMain"
|
guard: path === "KeysMain"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: keycardState
|
||||||
|
signal: applicationWindow.navigateTo
|
||||||
|
guard: path === "Keycard"
|
||||||
|
}
|
||||||
|
|
||||||
DSM.FinalState {
|
DSM.FinalState {
|
||||||
id: onboardingDoneState
|
id: onboardingDoneState
|
||||||
}
|
}
|
||||||
|
@ -526,6 +542,7 @@ StatusWindow {
|
||||||
KeysMain {
|
KeysMain {
|
||||||
btnGenKey.onClicked: applicationWindow.navigateTo("GenKey")
|
btnGenKey.onClicked: applicationWindow.navigateTo("GenKey")
|
||||||
btnExistingKey.onClicked: applicationWindow.navigateTo("ExistingKey")
|
btnExistingKey.onClicked: applicationWindow.navigateTo("ExistingKey")
|
||||||
|
btnKeycard.onClicked: applicationWindow.navigateTo("Keycard")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,6 +573,19 @@ StatusWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: keycard
|
||||||
|
Keycard {
|
||||||
|
onClosed: function () {
|
||||||
|
if (hasAccounts) {
|
||||||
|
applicationWindow.navigateTo("InitialState")
|
||||||
|
} else {
|
||||||
|
applicationWindow.navigateTo("KeysMain")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: login
|
id: login
|
||||||
Login {
|
Login {
|
||||||
|
|
|
@ -18,8 +18,10 @@ SOURCES = *.qml \
|
||||||
shared/*.qml \
|
shared/*.qml \
|
||||||
imports/*.qml \
|
imports/*.qml \
|
||||||
shared/status/*.qml \
|
shared/status/*.qml \
|
||||||
|
shared/keycard/*.qml \
|
||||||
onboarding/*.qml \
|
onboarding/*.qml \
|
||||||
onboarding/Login/*.qml \
|
onboarding/Login/*.qml \
|
||||||
|
onboarding/Keycard/*.qml \
|
||||||
app/AppLayouts/*.qml \
|
app/AppLayouts/*.qml \
|
||||||
app/AppLayouts/Browser/*.qml \
|
app/AppLayouts/Browser/*.qml \
|
||||||
app/AppLayouts/Chat/*.qml \
|
app/AppLayouts/Chat/*.qml \
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import "./Keycard"
|
||||||
|
import "../shared/keycard"
|
||||||
|
|
||||||
|
// this will be the entry point. for now it opens all keycard-related dialogs in sequence for test
|
||||||
|
Item {
|
||||||
|
property var onClosed: function () {}
|
||||||
|
id: keycardView
|
||||||
|
anchors.fill: parent
|
||||||
|
Component.onCompleted: {
|
||||||
|
createPinModal.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatePINModal {
|
||||||
|
id: createPinModal
|
||||||
|
onClosed: function () {
|
||||||
|
pairingModal.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PairingModal {
|
||||||
|
id: pairingModal
|
||||||
|
onClosed: function () {
|
||||||
|
pinModal.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PINModal {
|
||||||
|
id: pinModal
|
||||||
|
onClosed: function () {
|
||||||
|
keycardView.onClosed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
import QtQuick.Dialogs 1.3
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import "../../imports"
|
||||||
|
import "../../shared"
|
||||||
|
|
||||||
|
ModalPopup {
|
||||||
|
property bool firstPINFieldValid: false
|
||||||
|
property bool repeatPINFieldValid: false
|
||||||
|
property string pinValidationError: ""
|
||||||
|
property string repeatPINValidationError: ""
|
||||||
|
|
||||||
|
id: popup
|
||||||
|
title: qsTr("Create PIN")
|
||||||
|
height: 500
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
firstPINField.text = "";
|
||||||
|
firstPINField.forceActiveFocus(Qt.MouseFocusReason)
|
||||||
|
}
|
||||||
|
|
||||||
|
Input {
|
||||||
|
id: firstPINField
|
||||||
|
anchors.rightMargin: 56
|
||||||
|
anchors.leftMargin: 56
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 88
|
||||||
|
placeholderText: qsTr("New PIN")
|
||||||
|
textField.echoMode: TextInput.PIN
|
||||||
|
onTextChanged: {
|
||||||
|
[firstPINFieldValid, pinValidationError] =
|
||||||
|
Utils.validatePINs("first", firstPINField, repeatPINField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Input {
|
||||||
|
id: repeatPINField
|
||||||
|
enabled: firstPINFieldValid
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.right: firstPINField.right
|
||||||
|
anchors.left: firstPINField.left
|
||||||
|
anchors.top: firstPINField.bottom
|
||||||
|
anchors.topMargin: Style.current.xlPadding
|
||||||
|
placeholderText: qsTr("Confirm PIN")
|
||||||
|
textField.echoMode: TextInput.PIN
|
||||||
|
Keys.onReturnPressed: function(event) {
|
||||||
|
if (submitBtn.enabled) {
|
||||||
|
submitBtn.clicked(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onTextChanged: {
|
||||||
|
[repeatPINFieldValid, repeatPINValidationError] =
|
||||||
|
Utils.validatePINs("repeat", firstPINField, repeatPINField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: validationError
|
||||||
|
text: {
|
||||||
|
if (pinValidationError !== "") return pinValidationError;
|
||||||
|
if (repeatPINValidationError !== "") return repeatPINValidationError;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
anchors.top: repeatPINField.bottom
|
||||||
|
anchors.topMargin: 20
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Style.current.xlPadding
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Style.current.xlPadding
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: Style.current.danger
|
||||||
|
font.pixelSize: 11
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: qsTr("Create a 6 digit long PIN")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Style.current.xlPadding
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Style.current.xlPadding
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
color: Style.current.secondaryText
|
||||||
|
font.pixelSize: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: Item {
|
||||||
|
width: parent.width
|
||||||
|
height: submitBtn.height
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
id: submitBtn
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: qsTr("Create PIN")
|
||||||
|
enabled: firstPINFieldValid && repeatPINFieldValid
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
CreatePINModal 1.0 CreatePINModal.qml
|
|
@ -10,6 +10,7 @@ Page {
|
||||||
id: page
|
id: page
|
||||||
property alias btnExistingKey: btnExistingKey
|
property alias btnExistingKey: btnExistingKey
|
||||||
property alias btnGenKey: btnGenKey
|
property alias btnGenKey: btnGenKey
|
||||||
|
property alias btnKeycard: btnKeycard
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Style.current.background
|
color: Style.current.background
|
||||||
|
@ -98,6 +99,17 @@ Page {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
type: "secondary"
|
type: "secondary"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
id: btnKeycard
|
||||||
|
//% "I have a Keycard"
|
||||||
|
text: qsTr("I have a Keycard")
|
||||||
|
anchors.top: btnExistingKey.bottom
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: isExperimental === "1" || appSettings.isKeycardEnabled
|
||||||
|
type: "secondary"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,3 +7,4 @@ EnterSeedPhraseModal 1.0 EnterSeedPhraseModal.qml
|
||||||
CreatePasswordModal 1.0 CreatePasswordModal.qml
|
CreatePasswordModal 1.0 CreatePasswordModal.qml
|
||||||
GenKeyModal 1.0 GenKeyModal.qml
|
GenKeyModal 1.0 GenKeyModal.qml
|
||||||
BeforeGetStartedModal 1.0 BeforeGetStartedModal.qml
|
BeforeGetStartedModal 1.0 BeforeGetStartedModal.qml
|
||||||
|
Keycard 1.0 Keycard.qml
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
|
||||||
|
Item {
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
import QtQuick.Dialogs 1.3
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import "../../imports"
|
||||||
|
import "../../shared"
|
||||||
|
|
||||||
|
ModalPopup {
|
||||||
|
property bool pinFieldValid: false
|
||||||
|
|
||||||
|
id: popup
|
||||||
|
title: qsTr("Authenticate PIN")
|
||||||
|
height: 400
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
pinField.text = "";
|
||||||
|
pinField.forceActiveFocus(Qt.MouseFocusReason)
|
||||||
|
}
|
||||||
|
|
||||||
|
Input {
|
||||||
|
id: pinField
|
||||||
|
anchors.rightMargin: 56
|
||||||
|
anchors.leftMargin: 56
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 88
|
||||||
|
placeholderText: qsTr("PIN")
|
||||||
|
textField.echoMode: TextInput.Password
|
||||||
|
onTextChanged: {
|
||||||
|
[pinFieldValid, _] =
|
||||||
|
Utils.validatePINs("first", pinField, pinField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: qsTr("Insert your 6-digit PIN")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Style.current.xlPadding
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Style.current.xlPadding
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
color: Style.current.secondaryText
|
||||||
|
font.pixelSize: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: Item {
|
||||||
|
width: parent.width
|
||||||
|
height: submitBtn.height
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
id: submitBtn
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: qsTr("Authenticate")
|
||||||
|
enabled: pinFieldValid
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
import QtQuick.Dialogs 1.3
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import "../../imports"
|
||||||
|
import "../../shared"
|
||||||
|
|
||||||
|
ModalPopup {
|
||||||
|
property bool pairingPasswordFieldValid: false
|
||||||
|
|
||||||
|
id: popup
|
||||||
|
title: qsTr("Insert pairing code")
|
||||||
|
height: 400
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
pairingPasswordField.text = "";
|
||||||
|
pairingPasswordField.forceActiveFocus(Qt.MouseFocusReason)
|
||||||
|
}
|
||||||
|
|
||||||
|
Input {
|
||||||
|
id: pairingPasswordField
|
||||||
|
anchors.rightMargin: 56
|
||||||
|
anchors.leftMargin: 56
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 88
|
||||||
|
placeholderText: qsTr("Pairing code")
|
||||||
|
textField.echoMode: TextInput.Password
|
||||||
|
onTextChanged: {
|
||||||
|
pairingPasswordFieldValid = pairingPasswordField.text !== "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: qsTr("Insert the Keycard pairing code")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Style.current.xlPadding
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Style.current.xlPadding
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
color: Style.current.secondaryText
|
||||||
|
font.pixelSize: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: Item {
|
||||||
|
width: parent.width
|
||||||
|
height: submitBtn.height
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
id: submitBtn
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: qsTr("Pair")
|
||||||
|
enabled: pairingPasswordFieldValid
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
InsertCard 1.0 InsertCard.qml
|
||||||
|
PairingModal 1.0 PairingModal.qml
|
||||||
|
PINModal 1.0 PINModal.qml
|
Loading…
Reference in New Issue