draft authentication dialogs

This commit is contained in:
Michele Balistreri 2021-09-24 15:03:57 +03:00 committed by Iuri Matias
parent 21b20fbc38
commit 3d46e62345
15 changed files with 400 additions and 4 deletions

24
src/app/keycard/core.nim Normal file
View File

@ -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

18
src/app/keycard/view.nim Normal file
View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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 {

View File

@ -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 \

34
ui/onboarding/Keycard.qml Normal file
View File

@ -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()
}
}
}

View File

@ -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: {
}
}
}
}

View File

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

View File

@ -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"
}
} }
} }

View File

@ -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

View File

@ -0,0 +1,4 @@
import QtQuick 2.13
Item {
}

View File

@ -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: {
}
}
}
}

View File

@ -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: {
}
}
}
}

3
ui/shared/keycard/qmldir Normal file
View File

@ -0,0 +1,3 @@
InsertCard 1.0 InsertCard.qml
PairingModal 1.0 PairingModal.qml
PINModal 1.0 PINModal.qml