2022-11-15 11:38:28 +01:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2025-01-03 17:46:56 +01:00
|
|
|
* Copyright (C) 2022-2025 Tom Zander <tom@flowee.org>
|
2022-11-15 11:38:28 +01:00
|
|
|
*
|
|
|
|
|
* 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/>.
|
|
|
|
|
*/
|
2022-11-21 19:02:23 +01:00
|
|
|
import QtQuick
|
2025-06-18 17:48:29 +02:00
|
|
|
import QtQuick.Controls.Basic as QQC2
|
2022-11-21 19:02:23 +01:00
|
|
|
import QtQuick.Layouts
|
2022-11-15 15:19:35 +01:00
|
|
|
import "../Flowee" as Flowee
|
2023-11-04 16:27:15 +01:00
|
|
|
import Flowee.org.pay;
|
2022-11-15 11:38:28 +01:00
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
id: root
|
|
|
|
|
property bool open: false
|
|
|
|
|
|
2022-12-14 14:38:33 +01:00
|
|
|
onOpenChanged: if (!open) baseArea.openAccounts = false; // close the accounts when the menu is closed
|
|
|
|
|
|
2022-11-15 11:38:28 +01:00
|
|
|
Rectangle {
|
|
|
|
|
anchors.fill: parent
|
2022-11-17 00:12:17 +01:00
|
|
|
opacity: {
|
|
|
|
|
if (!root.open)
|
|
|
|
|
return 0;
|
|
|
|
|
// we become 50% opaque when the menuArea is fully open (x == 0)
|
|
|
|
|
return (menuArea.x + 250) / 500;
|
|
|
|
|
}
|
2022-11-15 11:38:28 +01:00
|
|
|
color: "black"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
id: menuArea
|
2023-02-21 16:40:46 +01:00
|
|
|
color: palette.window
|
2022-11-15 11:38:28 +01:00
|
|
|
width: 300
|
|
|
|
|
height: parent.height
|
|
|
|
|
x: root.open ? 0 : 0 - width -3
|
2022-12-15 12:37:21 +01:00
|
|
|
clip: true
|
2022-11-15 11:38:28 +01:00
|
|
|
|
2025-08-30 10:39:01 +02:00
|
|
|
Item {
|
|
|
|
|
id: contentArea // avoid the screen insets
|
|
|
|
|
y: Pay.screenInsets.y
|
|
|
|
|
x: Pay.screenInsets.x
|
|
|
|
|
width: parent.width - x
|
|
|
|
|
height: parent.height - y - Pay.screenInsets.height
|
2022-11-21 19:02:23 +01:00
|
|
|
|
|
|
|
|
Rectangle {
|
2025-08-30 10:39:01 +02:00
|
|
|
id: baseArea
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: {
|
|
|
|
|
var h = logo.height + 20;
|
|
|
|
|
// if its opened
|
|
|
|
|
if (openAccounts)
|
|
|
|
|
h = h + extraOptions.height + 10
|
|
|
|
|
// but we just don't show the accounts at all if
|
|
|
|
|
// this is the initial (empty) wallet.
|
|
|
|
|
if (!isLoading && portfolio.accounts.length > 1)
|
|
|
|
|
h = h+ Math.max(currentAccountName.height, 12) + 10
|
|
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
color: Qt.lighter(palette.window)
|
|
|
|
|
property bool openAccounts: false
|
|
|
|
|
clip: true
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
id: logo
|
|
|
|
|
width: 70
|
|
|
|
|
height: 70
|
|
|
|
|
x: 5
|
|
|
|
|
y: 5
|
|
|
|
|
radius: 35
|
|
|
|
|
color: mainWindow.floweeBlue
|
|
|
|
|
Item {
|
|
|
|
|
// clip the logo only, ignore the text part
|
|
|
|
|
width: 50
|
2022-11-21 19:02:23 +01:00
|
|
|
height: 50
|
2025-08-30 10:39:01 +02:00
|
|
|
clip: true
|
|
|
|
|
x: 13
|
|
|
|
|
y: 16
|
|
|
|
|
Image {
|
|
|
|
|
source: "qrc:/FloweePay-light.svg"
|
|
|
|
|
// ratio: 449 / 77
|
|
|
|
|
width: height / 77 * 449
|
|
|
|
|
height: 50
|
2022-12-15 11:59:07 +01:00
|
|
|
}
|
2022-11-21 19:02:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-30 10:39:01 +02:00
|
|
|
Image {
|
|
|
|
|
width: 25
|
|
|
|
|
height: 25
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.rightMargin: 10
|
|
|
|
|
y: 10
|
|
|
|
|
source: Pay.useDarkSkin ? "qrc:/maslenica.svg" : "qrc:/moon.svg"
|
|
|
|
|
MouseArea {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.margins: -10
|
|
|
|
|
property date colorChanged: new Date(2000)
|
2022-12-16 11:37:13 +01:00
|
|
|
onClicked: {
|
2025-08-30 10:39:01 +02:00
|
|
|
var now = new Date();
|
|
|
|
|
if (now - colorChanged < 3 * 1000)
|
|
|
|
|
return;
|
|
|
|
|
Pay.skinFollowsPlatform = false;
|
|
|
|
|
Pay.useDarkSkin = !Pay.useDarkSkin;
|
|
|
|
|
colorChanged = now;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Flowee.Label {
|
|
|
|
|
id: currentAccountName
|
|
|
|
|
x: 10
|
|
|
|
|
y: logo.height + 20
|
|
|
|
|
text: {
|
|
|
|
|
if (mainWindow.isLoading)
|
|
|
|
|
return ""
|
|
|
|
|
return portfolio.current.name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Image {
|
|
|
|
|
id: openButton
|
|
|
|
|
source: Pay.useDarkSkin ? "qrc:/smallArrow-light.svg" : "qrc:/smallArrow.svg"
|
|
|
|
|
rotation: baseArea.openAccounts ? 180 : 0
|
|
|
|
|
Behavior on rotation { NumberAnimation {} }
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.rightMargin: 10
|
|
|
|
|
anchors.bottom: currentAccountName.bottom
|
|
|
|
|
anchors.bottomMargin: 8
|
|
|
|
|
width: 20
|
|
|
|
|
height: 7
|
|
|
|
|
}
|
|
|
|
|
MouseArea {
|
|
|
|
|
anchors.top: currentAccountName.top
|
|
|
|
|
anchors.bottom: openButton.bottom
|
|
|
|
|
anchors.left: parent.left
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.margins: -10
|
|
|
|
|
onClicked: baseArea.openAccounts = !baseArea.openAccounts
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
|
id: extraOptions
|
|
|
|
|
width: baseArea.width - 20
|
|
|
|
|
y: logo.height + 20 + (currentAccountName.visible ? currentAccountName.height + 10 : 0)
|
|
|
|
|
x: 10
|
|
|
|
|
Repeater { // portfolio holds all our accounts
|
|
|
|
|
width: parent.width
|
|
|
|
|
model: mainWindow.isLoading ? 0 : portfolio.accounts
|
|
|
|
|
TextButton {
|
|
|
|
|
text: modelData.name
|
|
|
|
|
visible: portfolio.current !== modelData
|
|
|
|
|
onClicked: {
|
|
|
|
|
portfolio.current = modelData
|
|
|
|
|
baseArea.openAccounts = false
|
2022-12-16 11:37:13 +01:00
|
|
|
}
|
2022-11-21 14:29:54 +01:00
|
|
|
}
|
2022-11-15 11:38:28 +01:00
|
|
|
}
|
2025-08-30 10:39:01 +02:00
|
|
|
Loader {
|
|
|
|
|
width: parent.width
|
|
|
|
|
active: !isLoading && portfolio.accounts.length > 1
|
|
|
|
|
sourceComponent: addWalletRow
|
|
|
|
|
}
|
2022-11-15 11:38:28 +01:00
|
|
|
}
|
2025-08-30 10:39:01 +02:00
|
|
|
|
|
|
|
|
Behavior on height { NumberAnimation { } }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Flickable {
|
|
|
|
|
anchors.top: baseArea.bottom
|
|
|
|
|
anchors.topMargin: 10
|
|
|
|
|
anchors.bottom: versionLabel.top
|
|
|
|
|
contentHeight: contentLayout.height
|
|
|
|
|
width: parent.width - 20
|
|
|
|
|
clip: true
|
|
|
|
|
boundsBehavior: Flickable.StopAtBounds // don't show this is a flickable if there is no space issues
|
|
|
|
|
x: 10
|
|
|
|
|
ColumnLayout {
|
|
|
|
|
id: contentLayout
|
2022-12-16 11:37:13 +01:00
|
|
|
width: parent.width
|
2025-08-30 10:39:01 +02:00
|
|
|
Repeater {
|
|
|
|
|
model: MenuModel
|
|
|
|
|
TextButton {
|
|
|
|
|
text: model.name
|
|
|
|
|
pageButton: true
|
|
|
|
|
buttonId: model.id
|
|
|
|
|
onClicked: {
|
|
|
|
|
var target = model.target
|
|
|
|
|
if (target !== "") {
|
|
|
|
|
thePile.push(model.target)
|
|
|
|
|
root.open = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Loader {
|
|
|
|
|
width: parent.width
|
|
|
|
|
active: isLoading || portfolio.accounts.length <= 1
|
|
|
|
|
sourceComponent: addWalletRow
|
|
|
|
|
}
|
2022-12-16 11:37:13 +01:00
|
|
|
}
|
2022-12-15 22:14:25 +01:00
|
|
|
}
|
2022-11-15 11:38:28 +01:00
|
|
|
|
2025-08-30 10:39:01 +02:00
|
|
|
Flowee.Label {
|
|
|
|
|
id: versionLabel
|
|
|
|
|
x: 10
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
anchors.bottomMargin: 5
|
|
|
|
|
text: "Flowee Pay (mobile) v" + Application.version
|
|
|
|
|
font.pointSize: mainWindow.font.pointSize * 0.9
|
|
|
|
|
font.bold: false
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
width: parent.width - 20
|
|
|
|
|
}
|
2022-11-21 19:02:23 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-17 00:12:17 +01:00
|
|
|
Behavior on x { NumberAnimation { duration: 100 } }
|
|
|
|
|
|
|
|
|
|
property bool opened: false
|
|
|
|
|
onXChanged: {
|
|
|
|
|
if (!root.open)
|
|
|
|
|
opened = false;
|
|
|
|
|
else if (x === 0)
|
|
|
|
|
opened = true;
|
|
|
|
|
// close on user drag to the left
|
2022-11-17 23:07:15 +01:00
|
|
|
if (opened && x < -50)
|
2022-11-17 00:12:17 +01:00
|
|
|
root.open = false
|
|
|
|
|
}
|
|
|
|
|
// gesture (swipe right) to close menu
|
|
|
|
|
DragHandler {
|
|
|
|
|
id: dragHandler
|
|
|
|
|
enabled: root.open
|
|
|
|
|
yAxis.enabled: false
|
|
|
|
|
xAxis.minimum: -200
|
|
|
|
|
xAxis.maximum: 0
|
|
|
|
|
|
|
|
|
|
onActiveChanged: {
|
|
|
|
|
// should the user abort the swipe left, restore
|
|
|
|
|
// the original binding
|
|
|
|
|
if (!active && root.open)
|
|
|
|
|
menuArea.x = root.open ? 0 : 0 - width -3
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-15 11:38:28 +01:00
|
|
|
}
|
|
|
|
|
// allow close by clicking next to the menu
|
|
|
|
|
MouseArea {
|
|
|
|
|
width: parent.width - menuArea.width
|
|
|
|
|
height: parent.height
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
enabled: root.open
|
|
|
|
|
onClicked: root.open = false;
|
|
|
|
|
}
|
2023-08-28 18:37:32 +02:00
|
|
|
Item {
|
|
|
|
|
id: menuSwipy
|
2023-11-04 16:27:15 +01:00
|
|
|
width: 50
|
2023-08-28 18:37:32 +02:00
|
|
|
height: parent.height / 3
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
|
|
|
|
|
onXChanged: {
|
|
|
|
|
// moving this drag area makes the menu slowly open.
|
2023-11-04 16:27:15 +01:00
|
|
|
let SwipeDistance = dragOpenHandler.xAxis.maximum
|
|
|
|
|
let progress = x / SwipeDistance;
|
2023-08-28 18:37:32 +02:00
|
|
|
let menuX = 0;
|
|
|
|
|
if (progress < 0.2) // threshold for movement
|
|
|
|
|
return;
|
|
|
|
|
if (progress < 0.39) { // first 50% movement
|
2023-11-04 16:27:15 +01:00
|
|
|
menuX = Math.pow((progress - 0.1) * 10, 2) * SwipeDistance / 10;
|
2023-08-28 18:37:32 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// progress between 0.4 and 1.0
|
|
|
|
|
// this movement goes linear instead.
|
|
|
|
|
menuX = 10 + (progress - 0.4) * 2 * (menuArea.width / 2) + menuArea.width / 2;
|
|
|
|
|
}
|
|
|
|
|
menuArea.x = Math.min(0, 0 - menuArea.width + menuX + 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DragHandler {
|
|
|
|
|
id: dragOpenHandler
|
2023-11-04 16:27:15 +01:00
|
|
|
enabled: root.open === false && thePile.depth === 1 && Pay.appProtection !== FloweePay.AppPassword
|
2023-08-28 18:37:32 +02:00
|
|
|
yAxis.enabled: false // the anchors of parent do that too ¯\_(ツ)_/¯
|
|
|
|
|
xAxis.minimum: 0
|
2023-11-04 16:27:15 +01:00
|
|
|
xAxis.maximum: mainWindow.width / 2
|
2023-09-06 15:44:56 +02:00
|
|
|
acceptedDevices: PointerDevice.TouchScreen | PointerDevice.Stylus
|
2023-08-28 18:37:32 +02:00
|
|
|
onActiveChanged: {
|
|
|
|
|
if (!active) {
|
|
|
|
|
if (menuArea.x > -30)
|
|
|
|
|
root.open = true;
|
|
|
|
|
menuSwipy.x = 0; // reset
|
|
|
|
|
// restore the original binding
|
|
|
|
|
menuArea.x = root.open ? 0 : 0 - width -3
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-15 11:38:28 +01:00
|
|
|
|
2022-12-15 22:14:25 +01:00
|
|
|
Component {
|
|
|
|
|
id: addWalletRow
|
|
|
|
|
Item {
|
|
|
|
|
height: addWalletButton.height
|
|
|
|
|
Rectangle {
|
|
|
|
|
id: horizontalBar
|
|
|
|
|
width: 13
|
|
|
|
|
height: 2
|
|
|
|
|
x: 2
|
2023-02-21 16:40:46 +01:00
|
|
|
color: palette.mid
|
2022-12-15 22:14:25 +01:00
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
|
}
|
|
|
|
|
Rectangle {
|
|
|
|
|
id: verticalBar
|
|
|
|
|
width: 2
|
|
|
|
|
height: 13
|
|
|
|
|
anchors.horizontalCenter: horizontalBar.horizontalCenter
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
2023-02-21 16:40:46 +01:00
|
|
|
color: palette.mid
|
2022-12-15 22:14:25 +01:00
|
|
|
}
|
|
|
|
|
TextButton {
|
|
|
|
|
id: addWalletButton
|
|
|
|
|
text: qsTr("Add Wallet")
|
2024-12-23 18:51:55 +01:00
|
|
|
pageButton: true
|
2022-12-15 22:14:25 +01:00
|
|
|
anchors.left: horizontalBar.right
|
|
|
|
|
anchors.leftMargin: 6
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
onClicked: {
|
|
|
|
|
thePile.push("./NewAccount.qml")
|
|
|
|
|
root.open = false
|
|
|
|
|
baseArea.openAccounts = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-15 11:38:28 +01:00
|
|
|
}
|