Files
pay/desktop/main.qml
T
tomFlowee 7e87c7d4a4 Adjust version numbers down
Allow running an older Qt with this UI
2021-05-23 00:27:34 +02:00

506 lines
18 KiB
QML

/*
* This file is part of the Flowee project
* Copyright (C) 2020-2021 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 2.11
import QtQuick.Controls 2.11
import QtQuick.Layouts 1.11
import QtGraphicalEffects 1.0
import Flowee.org.pay 1.0
import "./ControlColors.js" as ControlColors
ApplicationWindow {
id: mainWindow
visible: true
width: Flowee.windowWidth === -1 ? 750 : Flowee.windowWidth
height: Flowee.windowHeight === -1 ? 500 : Flowee.windowHeight
minimumWidth: 800
minimumHeight: 600
title: "Flowee Pay"
onWidthChanged: Flowee.windowWidth = width
onHeightChanged: Flowee.windowHeight = height
onVisibleChanged: if (visible) ControlColors.applySkin(mainWindow)
property bool isLoading: typeof portfolio === "undefined";
onIsLoadingChanged: {
if (!isLoading) {
if (!portfolio.current.isUserOwned) { // Open on receive tab if the wallet is effectively empty
tabbar.currentIndex = 2;
}
else {
tabbar.currentIndex = 0;
}
// delay loading to avoid errors due to not having a portfolio
// portfolio is only initialized after a second or so.
receivePane.source = "./ReceiveTransactionPane.qml"
sendTransactionPane.source = "./SendTransactionPane.qml"
}
}
property color floweeSalmon: "#ff9d94"
property color floweeBlue: "#0b1088"
property color floweeGreen: "#90e4b5"
header: Rectangle {
color: Flowee.useDarkSkin ? "#00000000" : mainWindow.floweeBlue
width: parent.width
height: {
var h = mainWindow.height;
if (h > 700)
return 120;
return h / 700 * 120;
}
Rectangle {
color: mainWindow.floweeBlue
opacity: Flowee.useDarkSkin ? 1 : 0
width: parent.height / 5 * 4
height: width
radius: width / 2
x: 2
y: 8
Behavior on opacity { NumberAnimation { duration: 300 } }
}
Image {
anchors.verticalCenter: parent.verticalCenter
x: 20
smooth: true
source: "FloweePay-light.svg"
// ratio: 77 / 449
height: (parent.height - 20) * 7 / 10
width: height * 449 / 77
}
Text {
y: parent.height / 3 * 2 - height
anchors.right: parent.right
anchors.rightMargin: 30
text: "BCH: " + Fiat.formattedPrice(100000000, Fiat.price)
visible: Flowee.isMainChain
font.pixelSize: 18
color: "white"
}
}
Pane {
id: mainScreen
anchors.fill: parent
focus: true
Item {
id: tabbedPane
height: parent.height
width: parent.width * 65 / 100
anchors.right: parent.right
Rectangle {
anchors.fill: parent
opacity: 0.2
anchors.margins: -5
radius: 5
color: Flowee.useDarkSkin ? "black" : "white"
}
FloweeTabBar {
id: tabbar
anchors.fill: parent
Pane {
property string title: qsTr("Activity")
property string icon: Flowee.useDarkSkin ? "activityIcon-light.png" : "activityIcon.png"
anchors.fill: parent
ListView {
id: activityView
model: isLoading || portfolio.current === null ? 0 : portfolio.current.transactions
clip: true
delegate: WalletTransaction { width: activityView.width }
anchors.fill: parent
}
}
Loader {
id: sendTransactionPane
anchors.fill: parent
property string title: qsTr("Send")
property string icon: Flowee.useDarkSkin ? "sendIcon-light.png" : "sendIcon.png"
}
Loader {
id: receivePane
anchors.fill: parent
property string icon: "receiveIcon.png"
property string title: qsTr("Receive")
}
Pane {
id: settingsPane
anchors.fill: parent
property string title: qsTr("Settings")
property string icon: Flowee.useDarkSkin ? "settingsIcon-light.png" : "settingsIcon.png"
GridLayout {
columns: 3
Label {
text: qsTr("Night mode") + ":"
}
FloweeCheckBox {
Layout.columnSpan: 2
checked: Flowee.useDarkSkin
onClicked: {
Flowee.useDarkSkin = !Flowee.useDarkSkin
ControlColors.applySkin(mainWindow);
}
}
Label {
text: qsTr("Unit") + ":"
}
ComboBox {
width: 210
model: {
var answer = [];
for (let i = 0; i < 5; ++i) {
answer[i] = Flowee.nameOfUnit(i);
}
return answer;
}
currentIndex: Flowee.unit
onCurrentIndexChanged: {
Flowee.unit = currentIndex
}
}
Rectangle {
color: "#00000000"
radius: 6
border.color: mainWindow.palette.button
border.width: 2
implicitHeight: units.height + 10
implicitWidth: units.width + 10
GridLayout {
id: units
columns: 3
x: 5; y: 5
rowSpacing: 0
Label {
text: {
var answer = "1";
for (let i = Flowee.unitAllowedDecimals; i < 8; ++i) {
answer += "0";
}
return answer + " " + Flowee.unitName;
}
Layout.alignment: Qt.AlignRight
}
Label { text: "=" }
Label { text: "1 Bitcoin Cash" }
Label { text: "1 " + Flowee.unitName; Layout.alignment: Qt.AlignRight; visible: Flowee.isMainChain}
Label { text: "="; visible: Flowee.isMainChain}
Label {
text: {
var amount = 1;
for (let i = 0; i < Flowee.unitAllowedDecimals; ++i) {
amount = amount * 10;
}
return Fiat.formattedPrice(amount, Fiat.price);
}
visible: Flowee.isMainChain
}
}
}
}
Button {
anchors.right: parent.right
text: qsTr("Network Status")
onClicked: {
netView.source = "./NetView.qml"
netView.item.show();
}
}
}
}
}
Flickable {
id: overviewPane
width: parent.width - tabbedPane.width
height: parent.height
contentWidth: leftColumn.width
contentHeight: leftColumn.height
flickableDirection: Flickable.VerticalFlick
Column {
id: leftColumn
y: 40
width: overviewPane.width - 60
Item {
height: balanceLabel.height
width: parent.width
Label {
id: balanceLabel
text: qsTr("Balance");
height: implicitHeight / 10 * 7
}
Image {
anchors.right: parent.right
source: {
if (Flowee.hideBalance)
return Flowee.useDarkSkin ? "eye-closed-light.png" : "eye-closed.png"
return Flowee.useDarkSkin ? "eye-open-light.png" : "eye-open.png"
}
smooth: true
opacity: 0.5
MouseArea {
anchors.fill: parent
onClicked: Flowee.hideBalance = !Flowee.hideBalance
}
}
}
Item {
height: balance.height
width: balance.width
BitcoinAmountLabel {
id: balance
opacity: blurredBalance.visible ? 0 : 1
value: {
if (isLoading)
return 0;
var account = portfolio.current;
if (account === null)
return 0;
if (Flowee.hideBalance)
return 88888888;
return account.balanceConfirmed + account.balanceUnconfirmed
}
colorize: false
showFiat: false
textColor: mainWindow.palette.text
fontPtSize: {
if (leftColumn.width < 300)
return mainWindow.font.pointSize * 2
return mainWindow.font.pointSize * 3
}
}
FastBlur {
id: blurredBalance
anchors.fill: parent
anchors.margins: -5
visible: Flowee.hideBalance
source: balance
radius: 58
}
}
Label {
text: {
if (Flowee.hideBalance)
return "-- " + Fiat.currencyName
return Fiat.formattedPrice(balance.value, Fiat.price)
}
opacity: 0.6
}
Item { // spacer
width: 10
height: 20
}
Label {
id: totalBalanceLabel
visible: mainWindow.isLoading || portfolio.accounts.length > 1
text: qsTr("Total balance");
height: implicitHeight / 10 * 9
}
Item {
visible: totalBalanceLabel.visible
width: totalBalance.width
height: totalBalance.height
BitcoinAmountLabel {
id: totalBalance
value: {
if (isLoading)
return 0;
if (Flowee.hideBalance)
return 88888888;
return portfolio.totalBalance
}
colorize: false
showFiat: false
fontPtSize: mainWindow.font.pointSize * 2
opacity: blurredTotalBalance.visible ? 0 : 1
}
FastBlur {
id: blurredTotalBalance
anchors.fill: parent
anchors.margins: -5
visible: Flowee.hideBalance
source: totalBalance
radius: 58
}
}
Label {
text: {
if (Flowee.hideBalance)
return "-- " + Fiat.currencyName
return Fiat.formattedPrice(totalBalance.value, Fiat.price)
}
visible: totalBalanceLabel.visible
opacity: 0.6
}
Item { // spacer
visible: totalBalanceLabel.visible
width: 10
height: 40
}
Label {
text: qsTr("Network status")
opacity: 0.6
}
SyncIndicator {
accountBlockHeight: isLoading || portfolio.current === null ? 0 : portfolio.current.lastBlockSynched
visible: !isLoading && portfolio.current !== null
}
Item { // spacer
width: 10
height: 60
}
Repeater { // the portfolio listing our accounts
width: parent.width
model: (typeof portfolio === "undefined") ? 0 : portfolio.accounts;
delegate: AccountListItem {
width: leftColumn.width
account: modelData
}
}
Item { // spacer
width: 10
height: 40
}
Rectangle {
color: mainWindow.floweeGreen
radius: 10
width: leftColumn.width
height: buttonLabel.height + 30
Text {
id: buttonLabel
anchors.centerIn: parent
width: parent.width - 20
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
text: qsTr("Import your Bitcoin Cash wallet")
}
MouseArea {
anchors.fill: parent
onClicked: newAccountDiag.source = "./NewAccountDialog.qml"
}
}
Item { // spacer
width: 10
height: 40
}
}
}
Rectangle {
id: splashScreen
color: "#FFFFFF"
anchors.fill: parent
Text {
text: qsTr("Preparing...")
anchors.centerIn: parent
font.pointSize: 20
}
opacity: mainWindow.isLoading ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 300 } }
}
Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) !== 0) {
if (event.key === Qt.Key_Q) {
mainWindow.close()
}
}
}
Loader {
id: accountDetailsDialog
property QtObject account: null
function open(account_) {
account = account_;
if (item) {
item.account = accountDetailsDialog.account;
item.raise();
} else {
source = "./AccountDetails.qml";
}
}
onLoaded: {
item.account = accountDetailsDialog.account
ControlColors.applySkin(item)
accountDetailsHandler.target = item
}
Connections {
id: accountDetailsHandler
function onVisibleChanged() {
if (!accountDetailsDialog.item.visible) {
accountDetailsDialog.source = ""
accountDetailsDialog.account = null
}
}
}
}
// NetView (reachable from settings)
Loader {
id: netView
onLoaded: {
ControlColors.applySkin(item)
netViewHandler.target = item
}
Connections {
id: netViewHandler
function onVisibleChanged() {
if (!netView.item.visible)
netView.source = ""
}
}
}
Loader {
id: newAccountDiag
onLoaded: {
ControlColors.applySkin(item)
newAccountHandler.target = item
}
Connections {
id: newAccountHandler
function onVisibleChanged() {
if (!newAccountDiag.item.visible) {
newAccountDiag.source = ""
}
}
}
}
}
}