status-desktop/ui/app/AppLayouts/Onboarding/views/LoginView.qml

1004 lines
33 KiB
QML

import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtQuick.Dialogs 1.3
import QtGraphicalEffects 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import StatusQ.Popups 0.1
import SortFilterProxyModel 0.2
import shared.panels 1.0
import shared.popups 1.0
import shared.status 1.0
import shared.controls 1.0
import shared.controls.chat 1.0
import "../panels"
import "../popups"
import "../stores"
import utils 1.0
Item {
id: root
property StartupStore startupStore
Component.onCompleted: {
d.resetLogin()
}
onStateChanged: {
d.loading = false
pinInputField.statesInitialization()
pinInputField.forceFocus()
}
QtObject {
id: d
property bool loading: false
readonly property string stateLoginRegularUser: "regularUserLogin"
readonly property string stateLoginKeycardUser: "keycardUserLogin"
property int index: 0
property variant images : [
Style.svg("keycard/card0@2x"),
Style.svg("keycard/card1@2x"),
Style.svg("keycard/card2@2x"),
Style.svg("keycard/card3@2x")
]
property int remainingAttempts: parseInt(root.startupStore.startupModuleInst.keycardData, 10)
onRemainingAttemptsChanged: {
pinInputField.statesInitialization()
pinInputField.forceFocus()
}
function doLogin(password) {
if (d.loading || password.length === 0)
return
d.loading = true
txtPassword.textField.clear()
root.startupStore.setPassword(password)
root.startupStore.doPrimaryAction()
}
function doKeycardLogin(pin) {
if (d.loading || pin.length === 0)
return
d.loading = true
root.startupStore.setPin(pin)
root.startupStore.doPrimaryAction()
}
function resetLogin() {
if(localAccountSettings.storeToKeychainValue !== Constants.keychain.storedValue.store)
{
if (!root.startupStore.selectedLoginAccount.keycardCreatedAccount){
txtPassword.visible = true
txtPassword.forceActiveFocus(Qt.MouseFocusReason)
}
}
}
}
Timer {
interval: 400
running: root.state === d.stateLoginKeycardUser ||
root.state === Constants.startupState.loginKeycardInsertKeycard ||
root.state === Constants.startupState.loginKeycardReadingKeycard
repeat: true
onTriggered: {
d.index++
image.source = d.images[d.index % d.images.length]
}
}
Connections{
target: root.startupStore.startupModuleInst
onObtainingPasswordError: {
if (root.startupStore.selectedLoginAccount.keycardCreatedAccount) {
root.startupStore.doPrimaryAction() // in this case, switch to enter pin state
}
if (errorType === Constants.keychain.errorType.authentication) {
// We are notifying user only about keychain errors.
return
}
obtainingPasswordErrorNotification.confirmationText = errorDescription
obtainingPasswordErrorNotification.open()
}
onObtainingPasswordSuccess: {
if(localAccountSettings.storeToKeychainValue !== Constants.keychain.storedValue.store)
return
if (root.startupStore.selectedLoginAccount.keycardCreatedAccount) {
d.doKeycardLogin(password)
}
else {
d.doLogin(password)
}
}
onAccountLoginError: {
if (error) {
if (!root.startupStore.selectedLoginAccount.keycardCreatedAccount) {
// SQLITE_NOTADB: "file is not a database"
if (error === "file is not a database") {
txtPassword.validationError = qsTr("Password incorrect")
} else {
txtPassword.validationError = qsTr("Login failed: %1").arg(error.toUpperCase())
}
d.loading = false
txtPassword.textField.forceActiveFocus()
}
}
}
}
ConfirmationDialog {
id: obtainingPasswordErrorNotification
height: 270
confirmButtonLabel: qsTr("Ok")
onConfirmButtonClicked: {
close()
}
onClosed: {
txtPassword.visible = true
}
}
ColumnLayout {
anchors.centerIn: parent
spacing: Style.current.bigPadding
Image {
id: image
Layout.alignment: Qt.AlignHCenter
fillMode: Image.PreserveAspectFit
antialiasing: true
mipmap: true
}
StatusBaseText {
id: title
Layout.alignment: Qt.AlignHCenter
font.weight: Font.Bold
font.pixelSize: Constants.onboarding.titleFontSize
color: Theme.palette.directColor1
}
Item {
id: userInfo
height: userImage.height
width: 318
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Style.current.xlPadding
UserImage {
id: userImage
objectName: "loginViewUserImage"
image: root.startupStore.selectedLoginAccount.thumbnailImage
name: root.startupStore.selectedLoginAccount.username
colorId: root.startupStore.selectedLoginAccount.colorId
colorHash: root.startupStore.selectedLoginAccount.colorHash
anchors.left: parent.left
imageHeight: Constants.onboarding.userImageHeight
imageWidth: Constants.onboarding.userImageWidth
}
StatusBaseText {
id: usernameText
objectName: "currentUserNameLabel"
text: root.startupStore.selectedLoginAccount.username
font.pixelSize: 17
anchors.left: userImage.right
anchors.right: root.startupStore.selectedLoginAccount.keycardCreatedAccount?
keycardIcon.left : changeAccountBtn.left
anchors.leftMargin: Style.current.padding
anchors.rightMargin: Style.current.padding
anchors.verticalCenter: userImage.verticalCenter
color: Theme.palette.directColor1
elide: Text.ElideRight
}
StatusIcon {
id: keycardIcon
visible: root.startupStore.selectedLoginAccount.keycardCreatedAccount
anchors.right: changeAccountBtn.left
anchors.verticalCenter: userImage.verticalCenter
icon: "keycard"
height: Style.current.padding
color: Theme.palette.baseColor1
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: changeAccountBtn.clicked(mouse)
}
StatusFlatRoundButton {
id: changeAccountBtn
objectName: "loginChangeAccountButton"
icon.name: "chevron-down"
icon.rotation: accountsPopup.opened ? 180 : 0
type: StatusFlatRoundButton.Type.Tertiary
width: 24
height: 24
anchors.verticalCenter: usernameText.verticalCenter
anchors.right: parent.right
onClicked: {
if (accountsPopup.opened) {
accountsPopup.close()
} else {
accountsPopup.topMargin =
Qt.binding(function(){
return userInfo.mapToItem(
root,
root.height,
userInfo.height + Style.current.halfPadding).y
})
accountsPopup.popup(
userInfo,
(userInfo.width - accountsPopup.width) / 2,
userInfo.height + Style.current.halfPadding)
}
}
}
StatusPopupMenu {
id: accountsPopup
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
width: parent.width + Style.current.bigPadding
dim: false
SortFilterProxyModel {
id: proxyModel
sourceModel: root.startupStore.startupModuleInst.loginAccountsModel
filters: ValueFilter {
roleName: "keyUid"
value: root.startupStore.selectedLoginAccount.keyUid
inverted: true
}
}
Column {
width: parent.width
Repeater {
model: proxyModel
delegate: AccountMenuItemPanel {
label: model.username
image: model.thumbnailImage
colorId: model.colorId
colorHash: model.colorHash
keycardCreatedAccount: model.keycardCreatedAccount
onClicked: {
d.resetLogin()
accountsPopup.close()
const realIndex = proxyModel.mapToSource(index)
root.startupStore.setSelectedLoginAccountByIndex(realIndex)
}
}
}
AccountMenuItemPanel {
label: qsTr("Add new user")
asset.name: "add"
onClicked: {
accountsPopup.close()
root.startupStore.doSecondaryAction()
}
}
AccountMenuItemPanel {
label: qsTr("Add existing Status user")
asset.name: "wallet"
onClicked: {
accountsPopup.close()
root.startupStore.doTertiaryAction()
}
}
}
}
}
Item {
id: passwordSection
Layout.fillWidth: true
Layout.preferredHeight: txtPassword.height
Layout.alignment: Qt.AlignHCenter
Input {
id: txtPassword
textField.objectName: "loginPasswordInput"
validationErrorObjectName: "loginPassworkInputValidationErrorText"
width: 318
height: 70
enabled: !d.loading
validationErrorAlignment: Text.AlignHCenter
validationErrorTopMargin: 10
placeholderText: d.loading ?
qsTr("Connecting...") :
qsTr("Password")
textField.echoMode: TextInput.Password
Keys.onReturnPressed: {
d.doLogin(textField.text)
}
onTextEdited: {
validationError = "";
d.loading = false
}
}
StatusRoundButton {
id: submitBtn
width: 40
height: 40
type: StatusRoundButton.Type.Secondary
icon.name: "arrow-right"
opacity: (d.loading || txtPassword.text.length > 0) ? 1 : 0
anchors.left: txtPassword.right
anchors.leftMargin: (d.loading || txtPassword.text.length > 0) ? Style.current.padding : Style.current.smallPadding
state: d.loading ? "pending" : "default"
onClicked: {
d.doLogin(txtPassword.textField.text)
}
// https://www.figma.com/file/BTS422M9AkvWjfRrXED3WC/%F0%9F%91%8B-Onboarding%E2%8E%9CDesktop?node-id=6%3A0
Behavior on opacity {
OpacityAnimator {
from: 0.5
duration: 200
}
}
Behavior on anchors.leftMargin {
NumberAnimation {
duration: 200
}
}
}
}
Column {
id: pinSection
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
spacing: Style.current.padding
StatusBaseText {
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Constants.keycard.general.fontSize2
wrapMode: Text.WordWrap
text: qsTr("Enter Keycard PIN")
}
StatusPinInput {
id: pinInputField
anchors.horizontalCenter: parent.horizontalCenter
validator: StatusIntValidator{bottom: 0; top: 999999;}
pinLen: Constants.keycard.general.keycardPinLength
enabled: !d.loading
onPinInputChanged: {
if(pinInput.length == 0)
return
root.startupStore.setPin(pinInput)
root.startupStore.doPrimaryAction()
}
}
}
StatusBaseText {
id: info
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize3
wrapMode: Text.WordWrap
}
StatusBaseText {
id: message
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Constants.keycard.general.fontSize3
wrapMode: Text.WordWrap
}
StatusButton {
id: button
Layout.alignment: Qt.AlignHCenter
onClicked: {
root.startupStore.doPrimaryAction()
}
}
StatusBaseText {
id: link
Layout.alignment: Qt.AlignHCenter
color: Theme.palette.primaryColor1
font.pixelSize: Constants.keycard.general.fontSize2
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
parent.font.underline = true
}
onExited: {
parent.font.underline = false
}
onClicked: {
root.startupStore.doPrimaryAction()
}
}
}
}
states: [
State {
name: d.stateLoginRegularUser
when: !root.startupStore.selectedLoginAccount.keycardCreatedAccount
PropertyChanges {
target: image
source: Style.png("status-logo")
Layout.preferredHeight: 128
Layout.preferredWidth: 128
}
PropertyChanges {
target: title
text: qsTr("Welcome back")
}
PropertyChanges {
target: passwordSection
visible: true
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: ""
visible: false
}
PropertyChanges {
target: message
text: ""
visible: false
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: d.stateLoginKeycardUser
when: root.startupStore.selectedLoginAccount.keycardCreatedAccount &&
root.startupStore.currentStartupState.stateType === Constants.startupState.login
PropertyChanges {
target: image
source: Style.svg("keycard/card3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
visible: false
}
PropertyChanges {
target: message
text: ""
visible: false
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginPlugin
when: root.startupStore.selectedLoginAccount.keycardCreatedAccount &&
root.startupStore.currentStartupState.stateType === Constants.startupState.loginPlugin
PropertyChanges {
target: image
source: Style.svg("keycard/card3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("Plug in Keycard reader...")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.baseColor1
}
PropertyChanges {
target: message
text: ""
visible: false
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginKeycardInsertKeycard
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardInsertKeycard
PropertyChanges {
target: image
source: Style.svg("keycard/card3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("Insert your Keycard...")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.baseColor1
}
PropertyChanges {
target: message
visible: root.startupStore.startupModuleInst.keycardData !== ""
text: qsTr("Check the card, it might be wrongly inserted")
font.pixelSize: Constants.keycard.general.fontSize3
color: Theme.palette.baseColor1
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginKeycardReadingKeycard
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardReadingKeycard
PropertyChanges {
target: image
source: Style.svg("keycard/card3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("Reading Keycard...")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.baseColor1
}
PropertyChanges {
target: message
text: ""
visible: false
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginKeycardEnterPin
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardEnterPin
PropertyChanges {
target: image
source: Style.svg("keycard/card3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: true
}
PropertyChanges {
target: info
text: ""
visible: false
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.baseColor1
}
PropertyChanges {
target: message
text: ""
visible: false
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginKeycardWrongKeycard
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardWrongKeycard
PropertyChanges {
target: image
source: Style.svg("keycard/card-wrong3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("Wrong Keycard!\nThe card inserted is not linked to your profile.")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: message
text: qsTr("Insert another Keycard")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.baseColor1
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginKeycardWrongPin
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardWrongPin
PropertyChanges {
target: image
source: Style.svg("keycard/card-wrong3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: true
}
PropertyChanges {
target: info
text: qsTr("PIN incorrect")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: message
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
text: qsTr("%n attempt(s) remaining", "", d.remainingAttempts)
color: d.remainingAttempts === 1?
Theme.palette.dangerColor1 :
Theme.palette.baseColor1
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginKeycardMaxPinRetriesReached
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardMaxPinRetriesReached
PropertyChanges {
target: image
source: Style.svg("keycard/card-error3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("Keycard locked")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: message
text: ""
visible: false
}
PropertyChanges {
target: button
text: qsTr("Recover your Keycard")
visible: true
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginKeycardMaxPukRetriesReached
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardMaxPukRetriesReached
PropertyChanges {
target: image
source: Style.svg("keycard/card-error3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("Keycard locked")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: message
text: ""
visible: false
}
PropertyChanges {
target: button
text: qsTr("Recover with seed phrase")
visible: true
}
PropertyChanges {
target: link
text: ""
visible: false
}
},
State {
name: Constants.startupState.loginKeycardEmpty
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardEmpty
PropertyChanges {
target: image
source: Style.svg("keycard/card-wrong3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("The card inserted is empty")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: message
text: ""
visible: false
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: qsTr("Generate keys for a new Keycard")
visible: true
}
},
State {
name: Constants.startupState.loginNotKeycard
when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginNotKeycard
PropertyChanges {
target: image
source: Style.svg("keycard/card-wrong3@2x")
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
}
PropertyChanges {
target: title
text: ""
visible: false
}
PropertyChanges {
target: passwordSection
visible: false
}
PropertyChanges {
target: pinSection
visible: false
}
PropertyChanges {
target: info
text: qsTr("This is not a Keycard")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: message
text: qsTr("The card inserted is not a recognised Keycard,\nplease remove and try and again")
visible: true
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.baseColor1
}
PropertyChanges {
target: button
text: ""
visible: false
}
PropertyChanges {
target: link
text: ""
visible: false
}
}
]
}