Files
pay/guis/mobile/UnlockWidget.qml
T
tomFlowee b82bf5c753 Add quick recieve on lock screen.
This moves the creation of the portfolio to happen the moment we
finished loading. (wallets were loaded either way)
The networking is the part that now waits for the user to unlock before
it does anything.
2025-02-07 00:13:59 +01:00

357 lines
11 KiB
QML

/*
* This file is part of the Flowee project
* Copyright (C) 2023-2025 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick
import "../Flowee" as Flowee
import Flowee.org.pay;
Item {
id: root
implicitWidth: 300
implicitHeight: 600
/// This will hold the password when the user is done typing it.
property string password : "";
property alias buttonText: openButton.text
property bool assumeDarkBackground: false
/// Emitted when the user submits the password
signal passwordEntered;
/// in an onPasswordEntered callback, call this method
/// to signify the password is incorrect.
/// Otherwise just close this widget, or call acceptedPassword()
function passwordIncorrect() {
shaker.start();
moveFocusTimer.start();
keyboard.flashErrorFeedback();
smallKeyboard.flashErrorFeedback();
}
/// clears the password typed, if needed.
function acceptedPassword() {
pwdField.text = "";
lockIcon.open = true;
}
function takeFocus() {
moveFocusTimer.start();
lockIcon.open = false;
}
Item {
id: passwordData
property QtObject editor: Item {
property string enteredString;
property int insertedIndex: -1
function insertNumber(character) {
if (enteredString.length >= 10)
return false;
insertedIndex = enteredString.length;
enteredString = enteredString + character;
return true;
}
function addSeparator() { return false; }
function backspacePressed() {
if (enteredString == "")
return false;
insertedIndex = -1;
enteredString = enteredString.substring(0, enteredString.length - 1);
return true;
}
function reset() {
insertedIndex = -1;
enteredString = "";
}
}
function finished() {
var pwd = editor.enteredString;
if (pwd !== "") {
root.password = pwd;
editor.reset();
root.passwordEntered();
}
}
function shake() { }
}
// we can't move focus from things like the onClicked handler of a button
// therefore a little timer that does it very briefly afterwards is used.
Timer {
id: moveFocusTimer
interval: 50
onTriggered: {
if (Pay.unlockingKeyboard === FloweePay.FullKeyboard) {
pwdField.selectAll();
pwdField.forceActiveFocus();
}
}
}
Item {
id: lockIcon
property bool open: false
anchors.horizontalCenter: parent.horizontalCenter
y: parent.height > 700 ? 40 : 10
width: 60
height: 60
Item {
clip: true
width: 60
height: 21
rotation: lockIcon.open ? 20 : 0
transformOrigin: Item.Bottom
Image {
id: lockIconOne
source: "qrc:/lock" + (Pay.useDarkSkin || assumeDarkBackground ? "-light.svg" : ".svg");
width: 60
height: 60
}
}
Item {
clip: true
y: 21
width: 60
height: 40
rotation: lockIcon.open ? -10 : 0
transformOrigin: Item.TopRight
Image {
source: lockIconOne.source
y: -22
width: 60
height: 60
}
}
}
Image {
width: Pay.unlockingKeyboard === FloweePay.FullKeyboard ? 30 : 40
height: width
anchors.right: parent.right
source: {
var s = "qrc:/";
if (Pay.unlockingKeyboard === FloweePay.BigNumbersKeyboard)
s += "keyboard" // next one
else
s += "num-keyboard"
return s + (Pay.useDarkSkin || assumeDarkBackground ? "-light.svg" : ".svg");
}
MouseArea {
anchors.fill: parent
onClicked: {
passwordData.editor.reset();
var cur = Pay.unlockingKeyboard;
if (cur === FloweePay.BigNumbersKeyboard)
var newValue = FloweePay.FullKeyboard;
else
newValue = cur + 1;
Pay.unlockingKeyboard = newValue;
if (newValue === FloweePay.FullKeyoard)
pwdField.forceActiveFocus();
}
}
}
Flowee.Label {
id: introText
text: qsTr("Enter your wallet passcode")
anchors.top: lockIcon.bottom
anchors.topMargin: 20
horizontalAlignment: Text.AlignHCenter
width: parent.width
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
color: assumeDarkBackground ? "#fcfcfc" : palette.text
Flowee.ObjectShaker { id: shaker } // 'shake' to give feedback on mistakes
}
// Show the typed pin code, but as bullets as you type.
Row {
id: pinPreview
anchors.top: introText.bottom
anchors.topMargin: root.height > 700 ? 20 : 6
spacing: 6
anchors.horizontalCenter: parent.horizontalCenter
visible: Pay.unlockingKeyboard !== FloweePay.FullKeyboard
Flowee.Label { // for height
text: " ";
font.pixelSize: introText.font.pixelSize * 2
visible: repeater.model == 0 // 2 equals only!!
}
Repeater {
id: repeater
// take the number typed and turn it into an array of characters.
model: {
var inputString = passwordData.editor.enteredString
var answer = [];
for (let i = 0; i < inputString.length; ++i) {
answer.push(inputString.substr(i, 1));
}
return answer;
}
Rectangle {
width: 40
height: parent.height
color: !Pay.useDarkSkin && assumeDarkBackground ? palette.base : "#00000000"
Flowee.Label {
id: dot
anchors.centerIn: parent
text: {
if (index !== passwordData.editor.insertedIndex)
return "∙"
return modelData;
}
font.pixelSize: introText.font.pixelSize * 2
Timer {
interval: 1000
running: index === passwordData.editor.insertedIndex
onTriggered: dot.text = "∙"
}
}
}
}
}
Rectangle {
anchors.bottom: pinPreview.top
width: parent.width / 10 * 8
color: mainWindow.floweeGreen
height: 2
opacity: Pay.unlockingKeyboard === FloweePay.FullKeyboard ? 0 : 1
x: parent.width / 10
Behavior on opacity { NumberAnimation {} }
}
Rectangle {
anchors.top: pinPreview.bottom
width: parent.width / 10 * 8
color: mainWindow.floweeGreen
x: parent.width / 10
height: 2
opacity: Pay.unlockingKeyboard === FloweePay.FullKeyboard ? 0 : 1
Behavior on opacity { NumberAnimation {} }
}
NumericKeyboardWidget {
id: keyboard
opacity: Pay.unlockingKeyboard === FloweePay.BigNumbersKeyboard ? 1 : 0
enabled: opacity > 0.1
hasSeparator: false
width: parent.width
anchors.top: pinPreview.top
anchors.topMargin: introText.height * 2
anchors.bottom: parent.bottom
anchors.bottomMargin: 40
dataInput: passwordData
onFinished: passwordData.finished();
Behavior on opacity { NumberAnimation {} }
}
Flowee.TextField {
id: pwdField
opacity: Pay.unlockingKeyboard === FloweePay.FullKeyboard ? 1 : 0
enabled: opacity > 0.1
anchors.top: introText.bottom
anchors.topMargin: 20
width: parent.width
focus: visible
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveDataTyped
| (hideText ? Qt.ImhHiddenTextCharacters : Qt.ImhNone)
echoMode: hideText ? TextInput.Password : TextInput.Normal
property bool hideText: true
onEditingFinished: openButton.clicked()
Image {
width: 14
height: 14
anchors.right: parent.right
anchors.rightMargin: 5
anchors.verticalCenter: parent.verticalCenter
source: {
var state = (pwdField.hideText) ? "open" : "closed";
var skin = Pay.useDarkSkin ? "-light" : ""
return "qrc:/eye-" + state + skin + ".png";
}
MouseArea {
width: parent.width + 50 // extend to right physical edge
x: -15
y: 0 - parent.y - 5
height: pwdField.height + 10
onClicked: pwdField.hideText = !pwdField.hideText
}
}
Behavior on opacity { NumberAnimation {} }
}
Flowee.Button {
id: openButton
opacity: Pay.unlockingKeyboard === FloweePay.FullKeyboard ? 1 : 0
enabled: opacity > 0.1
anchors.right: parent.right
y: pwdField.y + pwdField.height + 20
text: qsTr("Open", "open wallet with PIN")
onClicked: {
var pwd = pwdField.text;
if (pwd !== "") {
root.password = pwd;
passwordData.editor.reset();
root.passwordEntered();
}
}
Behavior on opacity { NumberAnimation {} }
}
Rectangle {
width: parent.width + 20
x: -10
opacity: Pay.unlockingKeyboard === FloweePay.SmallNumbersKeyboard ? 1 : 0
anchors.bottom: parent.bottom
anchors.bottomMargin: -10
color: palette.base
height: 225
Behavior on opacity { NumberAnimation {} }
}
NumericKeyboardWidget {
id: smallKeyboard
opacity: Pay.unlockingKeyboard === FloweePay.SmallNumbersKeyboard ? 1 : 0
enabled: opacity > 0.1
hasSeparator: false
width: parent.width
height: 200
anchors.bottom: parent.bottom
dataInput: passwordData
onFinished: passwordData.finished();
buttonBackground: Item {
property int index: 0
property bool pressed: false
Rectangle {
anchors.fill: parent
visible: parent.index === 11
color: palette.midlight
opacity: 0.6
}
}
Behavior on opacity { NumberAnimation {} }
}
}