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/login/core as login
|
||||
import app/provider/core as provider
|
||||
import app/keycard/core as keycard
|
||||
import status/types/[account]
|
||||
import status_go
|
||||
import status/status as statuslib
|
||||
|
@ -108,7 +109,7 @@ proc mainProc() =
|
|||
engine.addImportPath("qrc:/./imports")
|
||||
engine.setNetworkAccessManagerFactory(networkAccessFactory)
|
||||
engine.setRootContextProperty("uiScaleFilePath", newQVariant(uiScaleFilePath))
|
||||
|
||||
|
||||
# Register events objects
|
||||
let dockShowAppEvent = newStatusDockShowAppEventObject(engine)
|
||||
defer: dockShowAppEvent.delete()
|
||||
|
@ -145,7 +146,7 @@ proc mainProc() =
|
|||
signal_handler(signalsQObjPointer, ($(%* {"type": "chronicles-log", "event": msg})).cstring, "receiveSignal")
|
||||
except:
|
||||
logLoggingFailure(cstring(msg), getCurrentException())
|
||||
|
||||
|
||||
let logFile = fmt"app_{getTime().toUnix}.log"
|
||||
discard defaultChroniclesStream.outputs[1].open(LOGDIR & logFile, fmAppend)
|
||||
|
||||
|
@ -192,6 +193,8 @@ proc mainProc() =
|
|||
defer: login.delete()
|
||||
var onboarding = onboarding.newController(status)
|
||||
defer: onboarding.delete()
|
||||
var keycard = keycard.newController(status)
|
||||
defer: keycard.delete()
|
||||
|
||||
proc onAccountChanged(account: Account) =
|
||||
profile.view.setAccountSettingsFile(account.name)
|
||||
|
@ -234,17 +237,19 @@ proc mainProc() =
|
|||
|
||||
engine.setRootContextProperty("loginModel", login.variant)
|
||||
engine.setRootContextProperty("onboardingModel", onboarding.variant)
|
||||
engine.setRootContextProperty("keycardModel", keycard.variant)
|
||||
engine.setRootContextProperty("singleInstance", newQVariant(singleInstance))
|
||||
|
||||
let isExperimental = if getEnv("EXPERIMENTAL") == "1": "1" else: "0" # value explicity passed to avoid trusting input
|
||||
let experimentalFlag = newQVariant(isExperimental)
|
||||
engine.setRootContextProperty("isExperimental", experimentalFlag)
|
||||
|
||||
|
||||
# Initialize only controllers whose init functions
|
||||
# do not need a running node
|
||||
proc initControllers() =
|
||||
login.init()
|
||||
onboarding.init()
|
||||
keycard.init()
|
||||
|
||||
initControllers()
|
||||
|
||||
|
@ -272,7 +277,7 @@ proc mainProc() =
|
|||
var prValue = newQVariant(if defined(production): true else: false)
|
||||
engine.setRootContextProperty("production", prValue)
|
||||
|
||||
# We're applying default language before we load qml. Also we're aware that
|
||||
# We're applying default language before we load qml. Also we're aware that
|
||||
# switch language at runtime will have some impact to cpu usage.
|
||||
# https://doc.qt.io/archives/qtjambi-4.5.2_01/com/trolltech/qt/qtjambi-linguist-programmers.html
|
||||
changeLanguage("en")
|
||||
|
|
|
@ -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) {
|
||||
const rgx = /\:\/\/(?:[a-zA-Z0-9\-]*\.{1,}){1,}[a-zA-Z0-9]*/i
|
||||
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 {
|
||||
id: stateLogin
|
||||
onEntered: loader.sourceComponent = login
|
||||
|
@ -407,6 +417,12 @@ StatusWindow {
|
|||
guard: path === "KeysMain"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: keycardState
|
||||
signal: applicationWindow.navigateTo
|
||||
guard: path === "Keycard"
|
||||
}
|
||||
|
||||
DSM.FinalState {
|
||||
id: onboardingDoneState
|
||||
}
|
||||
|
@ -526,6 +542,7 @@ StatusWindow {
|
|||
KeysMain {
|
||||
btnGenKey.onClicked: applicationWindow.navigateTo("GenKey")
|
||||
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 {
|
||||
id: login
|
||||
Login {
|
||||
|
|
|
@ -18,8 +18,10 @@ SOURCES = *.qml \
|
|||
shared/*.qml \
|
||||
imports/*.qml \
|
||||
shared/status/*.qml \
|
||||
shared/keycard/*.qml \
|
||||
onboarding/*.qml \
|
||||
onboarding/Login/*.qml \
|
||||
onboarding/Keycard/*.qml \
|
||||
app/AppLayouts/*.qml \
|
||||
app/AppLayouts/Browser/*.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
|
||||
property alias btnExistingKey: btnExistingKey
|
||||
property alias btnGenKey: btnGenKey
|
||||
property alias btnKeycard: btnKeycard
|
||||
|
||||
background: Rectangle {
|
||||
color: Style.current.background
|
||||
|
@ -98,6 +99,17 @@ Page {
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
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
|
||||
GenKeyModal 1.0 GenKeyModal.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