Files
pay/guis/mobile/ActivityTab.qml
T
tomFlowee 9fa365b329 More smoothing UX
This takes user feedback on the spacing and adjusts it
further.

The intention is to avoid a jump when the text line of the
sync status goes away, while not taking space for it always.
2025-11-02 23:21:03 +01:00

275 lines
9.2 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 QtQuick.Controls.Basic as QQC2
import "../Flowee" as Flowee
import "../Utils.js" as Utils
import Flowee.org.pay;
Item {
property string icon: "qrc:/homeButtonIcon" + (Pay.useDarkSkin ? "-light.svg" : ".svg");
property string title: qsTr("Home")
property bool showFilterIcon: tabBar.currentIndex === 0
property bool itIsChristmas: {
var today = new Date();
return today.getMonth() == 11 && today.getDate() >= 24 && today.getDate() <= 31
}
Rectangle {
id: headerBg
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: tabBar.bottom
anchors.bottomMargin: -6
color: palette.window
property var headerGradient: Gradient {
GradientStop { position: 0.93; color: headerBg.color }
GradientStop { position: 1; color: {
let c = headerBg.color;
return Qt.rgba(c.r, c.g, c.b, 0);
}
}
}
gradient: tabBar.visible ? undefined : headerGradient
}
Column {
id: header
property int leeway: 30;
Behavior on leeway { NumberAnimation {} }
width: parent.width
y: {
if (!accountSyncState.hasProgressbar && accountSyncState.implicitHeight > 0)
return leeway - accountSyncState.initialHeight / 4 - 8;
return leeway;
}
spacing: {
if (!accountSyncState.hasProgressbar && accountSyncState.implicitHeight > 0)
return accountSyncState.initialHeight / -4 + 4;
return 0
}
Flowee.BitcoinAmountLabel {
id: bchPriceWidget
opacity: Pay.hideBalance ? 0.2 : 1
fontPixelSize: 30
anchors.horizontalCenter: parent.horizontalCenter
value: {
if (Pay.hideBalance)
return 88888888;
return portfolio.current.balanceConfirmed + portfolio.current.balanceUnconfirmed
}
colorize: false
showFiat: false
}
Flowee.Label { // fiat price
opacity: Pay.hideBalance ? 0.2 : 1
width: parent.width
horizontalAlignment: Text.AlignHCenter
text: {
if (Pay.hideBalance)
return Fiat.currencySymbolPrefix + "——" + Fiat.currencySymbolPost
Fiat.formattedPrice(portfolio.current.balanceConfirmed
+ portfolio.current.balanceUnconfirmed, Fiat.price)
}
MouseArea {
// make the click area nice and large
width: parent.width
height: parent.height + bchPriceWidget.height + 10
y: 0 - 5- bchPriceWidget.height
onClicked: popupOverlay.open(priceDetails, parent)
cursorShape: Qt.PointingHandCursor
}
Component {
id: priceDetails
PriceDetails { }
}
}
AccountSyncState {
id: accountSyncState
account: portfolio.current
width: parent.width
property int initialHeight: -1
Component.onCompleted: initialHeight = implicitHeight
}
Item { width: 1; height: parent.leeway + 1 } // spacer
}
ListView {
id: tabBar
orientation: Qt.Horizontal
width: parent.width
height: {
if (!visible || count <= 1 || currentItem === null)
return 0;
return currentItem.height;
}
anchors.top: header.bottom
property bool havePlannedTab: Pay.haveRepeatPayments
visible: havePlannedTab
function selectTab(index) {
// fast move
activityTabs.currentIndex = index;
activityTabs.positionViewAtIndex(index, ListView.Beginning)
// slow scroll
currentIndex = index;
}
clip: true
boundsBehavior: Flickable.StopAtBounds
currentIndex: activityTabs.currentIndex
model: ListModel {
id: tabsModel
ListElement {
title: qsTr("Activity")
qml: "AccountHistory.qml"
}
}
onHavePlannedTabChanged: {
if (havePlannedTab && tabsModel.count < 2) {
tabsModel.append({ title: qsTr("Planned"), qml: "PlannedPayments.qml" });
}
}
delegate: Item {
width: Math.max(tabName.width, 120)
height: tabName.height + 20
Rectangle {
x: 5
height: 4
width: parent.width - 10
color: palette.highlight
visible: index === tabBar.currentIndex
anchors.bottom: parent.bottom
}
Rectangle {
anchors.fill: parent
color: palette.highlight
visible: index === tabBar.currentIndex
opacity: 0.15
}
Text {
id: tabName
color: palette.windowText
text: model.title
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: tabBar.selectTab(index);
}
}
}
ListView {
id: activityTabs
anchors.top: tabBar.bottom
anchors.topMargin: tabBar.visible ? 0 : 6
anchors.bottom: parent.bottom
width: parent.width
model: tabBar.model
orientation: ListView.Horizontal
snapMode: ListView.SnapOneItem
onContentXChanged: currentIndex = Math.round(contentX / width);
boundsBehavior: Flickable.StopAtBounds
onCurrentItemChanged: currentItem.makeActive();
onCurrentIndexChanged: tabBar.currentIndex = currentIndex
delegate: Loader {
id: delegateRoot
width: activityTabs.width
height: activityTabs.height
source: model.qml
function makeActive() {
item.forceActiveFocus();
leewayHandler.target = item;
}
// Tabs that choose to do so can add the leewayHint property
// in order to make the spacing around the price compress as the
// user moves the list content.
Connections {
id: leewayHandler
// notice that this gives warnings before the item is loaded,
// but it works, so just ignore.
function onLeewayHintChanged() {
var hint = target.leewayHint
if (hint < 0) hint = 0;
if (hint > 40) hint = 40;
header.leeway = hint;
}
}
}
}
Rectangle {
id: startQRButton
width: height
height: {
let i = activityTabs.currentItem;
if (i != null) { // at creation this is the case.
let hide = i.item.hideQRScanButton
if (typeof(hide) === "boolean" && hide)
return 0;
}
return 70;
}
radius: 35
clip: true
x: parent.width - width - 30 - (70 / 2 - width / 2) // almost right, but keep this centered around the max width
y: parent.height - height - 15 - (70 / 2 - height / 2)
color: mainWindow.floweeBlue
Image {
source: "qrc:/qr-code-scan-light.svg"
anchors.centerIn: parent
width: 40
height: 40
}
MouseArea {
anchors.fill: parent
onClicked: thePile.push("ScanQRPage.qml")
}
Behavior on height { NumberAnimation { } }
}
Keys.onPressed: (event)=> {
if (event.key === Qt.Key_Escape || event.key === Qt.Key_Back) {
// when we are on another tab, we can go to 'main' on back.
if (activityTabs.currentIndex !== 0) {
event.accepted = true;
tabBar.selectTab(0);
}
}
}
// should the app be started with the intent to open the "Planned Payment" screen,
// we handle that here.
Connections {
target: intent
function onGenericIntentChanged() { // it only ever changes at most once
if (intent.genericIntent === Intent.OpenPlannedPaymentScreen)
tabBar.selectTab(1);
}
}
}