monero-gui/pages/History.qml

1841 lines
81 KiB
QML
Raw Normal View History

2024-01-27 20:12:09 +01:00
// Copyright (c) 2014-2024, The Monero Project
2019-03-18 00:12:51 +01:00
//
2015-04-01 10:56:05 +02:00
// All rights reserved.
2019-03-18 00:12:51 +01:00
//
2015-04-01 10:56:05 +02:00
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
2019-03-18 00:12:51 +01:00
//
2015-04-01 10:56:05 +02:00
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
2019-03-18 00:12:51 +01:00
//
2015-04-01 10:56:05 +02:00
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
2019-03-18 00:12:51 +01:00
//
2015-04-01 10:56:05 +02:00
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
2019-03-18 00:12:51 +01:00
//
2015-04-01 10:56:05 +02:00
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2014-07-07 19:08:30 +02:00
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
2019-04-11 03:17:29 +02:00
import QtGraphicalEffects 1.0
import moneroComponents.Wallet 1.0
import moneroComponents.WalletManager 1.0
2016-10-07 22:05:51 +02:00
import moneroComponents.TransactionHistory 1.0
2016-10-08 02:26:45 +02:00
import moneroComponents.TransactionInfo 1.0
2016-10-07 22:05:51 +02:00
import moneroComponents.TransactionHistoryModel 1.0
2019-03-18 00:12:51 +01:00
import moneroComponents.Clipboard 1.0
import FontAwesome 1.0
2019-04-11 03:17:29 +02:00
import "../components/effects/" as MoneroEffects
2019-03-18 00:12:51 +01:00
import "../components" as MoneroComponents
import "../js/Utils.js" as Utils
import "../js/TxUtils.js" as TxUtils
2016-10-07 22:05:51 +02:00
2014-07-07 19:08:30 +02:00
Rectangle {
2019-03-18 00:12:51 +01:00
id: root
property var model
2019-04-25 21:09:23 +02:00
property int sideMargin: 50
2019-03-18 00:12:51 +01:00
property var initialized: false
2019-07-03 15:24:39 +02:00
property int txMax: Math.max(5, ((appWindow.height - 250) / 60))
2019-03-18 00:12:51 +01:00
property int txOffset: 0
2019-07-03 15:24:39 +02:00
property int txPage: (txOffset / txMax) + 1
2019-03-18 00:12:51 +01:00
property int txCount: 0
property var sortSearchString: null
property bool sortDirection: true // true = desc, false = asc
property string sortBy: "blockheight"
property var txModelData: [] // representation of transaction data (appWindow.currentWallet.historyModel)
property var txData: [] // representation of FILTERED transation data
property var txDataCollapsed: [] // keep track of which txs are collapsed
property string historyStatusMessage: ""
property alias contentHeight: pageRoot.height
Clipboard { id: clipboard }
ListModel { id: txListViewModel }
2016-11-06 13:33:41 +01:00
color: "transparent"
2019-07-03 15:24:39 +02:00
onTxMaxChanged: root.updateDisplay(root.txOffset, root.txMax);
2019-03-18 00:12:51 +01:00
ColumnLayout {
id: pageRoot
2019-04-25 21:09:23 +02:00
anchors.topMargin: 40
2019-03-18 00:12:51 +01:00
anchors.left: parent.left
anchors.top: parent.top
anchors.right: parent.right
2019-03-18 00:12:51 +01:00
RowLayout {
Layout.preferredHeight: 24
Layout.preferredWidth: parent.width - root.sideMargin
Layout.leftMargin: sideMargin
Layout.rightMargin: sideMargin
2019-04-25 21:09:23 +02:00
Layout.bottomMargin: 10
2016-11-06 13:33:41 +01:00
2019-03-18 00:12:51 +01:00
MoneroComponents.Label {
2019-04-25 21:09:23 +02:00
fontSize: 24
2019-03-18 00:12:51 +01:00
text: qsTr("Transactions") + translationManager.emptyString
2016-11-06 13:33:41 +01:00
}
2016-10-02 23:03:37 +02:00
2019-03-18 00:12:51 +01:00
Item {
Layout.fillWidth: true
}
2019-03-18 00:12:51 +01:00
RowLayout {
id: sortAndFilter
visible: root.txCount > 0
2019-03-18 00:12:51 +01:00
property bool collapsed: false
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.preferredWidth: 100
Layout.preferredHeight: 15
2019-04-25 21:09:23 +02:00
spacing: 8
2019-03-18 00:12:51 +01:00
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
Layout.alignment: Qt.AlignVCenter
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Sort & filter") + translationManager.emptyString
2019-03-18 00:12:51 +01:00
color: MoneroComponents.Style.defaultFontColor
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
sortAndFilter.collapsed = !sortAndFilter.collapsed
}
}
}
2019-04-11 03:17:29 +02:00
MoneroEffects.ImageMask {
id: sortCollapsedIcon
2019-03-18 00:12:51 +01:00
Layout.alignment: Qt.AlignVCenter
2019-04-25 21:09:23 +02:00
height: 8
width: 12
2019-04-11 03:17:29 +02:00
image: "qrc:///images/whiteDropIndicator.png"
fontAwesomeFallbackIcon: FontAwesome.arrowDown
fontAwesomeFallbackSize: 14
rotation: sortAndFilter.collapsed ? 180 : 0
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.defaultFontColor
2019-03-18 00:12:51 +01:00
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
sortAndFilter.collapsed = !sortAndFilter.collapsed
}
}
}
}
}
2019-03-18 00:12:51 +01:00
ColumnLayout {
Layout.fillWidth: true
2019-04-25 21:09:23 +02:00
Layout.topMargin: 8
2019-03-18 00:12:51 +01:00
Layout.leftMargin: sideMargin
Layout.rightMargin: sideMargin
visible: sortAndFilter.collapsed
2019-03-18 00:12:51 +01:00
MoneroComponents.LineEdit {
id: searchInput
Layout.fillWidth: true
2019-04-25 21:09:23 +02:00
input.topPadding: 6
input.bottomPadding: 6
fontSize: 15
2019-04-25 21:09:23 +02:00
labelFontSize: 14
placeholderText: qsTr("Search by Transaction ID, Address, Description, Amount or Blockheight") + translationManager.emptyString
placeholderFontSize: 15
2019-03-18 00:12:51 +01:00
inputHeight: 34
onTextUpdated: {
if (!sortAndFilter.collapsed) {
sortAndFilter.collapsed = true;
}
2019-03-18 00:12:51 +01:00
if(searchInput.text != null && searchInput.text.length >= 3){
root.sortSearchString = searchInput.text;
root.reset();
root.updateFilter();
} else {
root.sortSearchString = null;
root.reset();
root.updateFilter();
}
2018-03-21 23:58:51 +01:00
}
Rectangle {
color: "transparent"
height: cleanButton.height
width: cleanButton.width
Layout.rightMargin: -8
Layout.leftMargin: -2
MoneroComponents.InlineButton {
id: cleanButton
buttonColor: "transparent"
fontFamily: FontAwesome.fontFamilySolid
fontStyleName: "Solid"
fontPixelSize: 18
text: FontAwesome.times
tooltip: qsTr("Clean") + translationManager.emptyString
tooltipLeft: true
visible: searchInput.text != ""
onClicked: searchInput.text = ""
}
}
2018-03-21 23:58:51 +01:00
}
2019-03-18 00:12:51 +01:00
}
2018-03-21 23:58:51 +01:00
2019-03-18 00:12:51 +01:00
GridLayout {
visible: sortAndFilter.collapsed
Layout.fillWidth: true
2019-04-25 21:09:23 +02:00
Layout.topMargin: 4
2019-03-18 00:12:51 +01:00
Layout.leftMargin: sideMargin
Layout.rightMargin: sideMargin
columns: 2
2019-04-25 21:09:23 +02:00
columnSpacing: 20
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
MoneroComponents.DatePicker {
id: fromDatePicker
Layout.fillWidth: true
2019-04-25 21:09:23 +02:00
width: 100
2019-03-18 00:12:51 +01:00
inputLabel.text: qsTr("Date from") + translationManager.emptyString
2019-04-25 21:09:23 +02:00
inputLabel.font.pixelSize: 14
2019-03-18 00:12:51 +01:00
onCurrentDateChanged: {
if(root.initialized){
root.reset();
root.updateFilter();
}
}
}
2016-10-07 22:05:51 +02:00
2019-03-18 00:12:51 +01:00
MoneroComponents.DatePicker {
id: toDatePicker
Layout.fillWidth: true
2019-04-25 21:09:23 +02:00
width: 100
2019-03-18 00:12:51 +01:00
inputLabel.text: qsTr("Date to") + translationManager.emptyString
onCurrentDateChanged: {
if(root.initialized){
root.reset();
root.updateFilter();
}
}
}
}
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
RowLayout {
2019-04-25 21:09:23 +02:00
Layout.topMargin: 20
Layout.bottomMargin: 20
Layout.fillWidth: true
2019-03-18 00:12:51 +01:00
Layout.leftMargin: sideMargin
Layout.rightMargin: sideMargin
Rectangle {
visible: sortAndFilter.collapsed
color: "transparent"
2019-04-25 21:09:23 +02:00
Layout.preferredWidth: childrenRect.width + 38
2019-03-18 00:12:51 +01:00
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-12-02 00:08:43 +01:00
text: qsTr("Sort by") + ":" + translationManager.emptyString
2019-03-18 00:12:51 +01:00
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
}
}
2019-03-18 00:12:51 +01:00
Rectangle {
visible: sortAndFilter.collapsed
id: sortBlockheight
color: "transparent"
2019-04-25 21:09:23 +02:00
Layout.preferredWidth: sortBlockheightText.width + 42
2019-03-18 00:12:51 +01:00
Layout.preferredHeight: 20
2014-07-16 18:04:34 +02:00
RowLayout {
2019-03-18 00:12:51 +01:00
clip: true
anchors.fill: parent
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
id: sortBlockheightText
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Blockheight") + translationManager.emptyString
2019-03-18 00:12:51 +01:00
color: root.sortBy === "blockheight" ? MoneroComponents.Style.defaultFontColor : MoneroComponents.Style.dimmedFontColor
2019-04-11 03:17:29 +02:00
themeTransition: false
2019-03-18 00:12:51 +01:00
}
2014-07-16 18:04:34 +02:00
2019-04-11 03:17:29 +02:00
MoneroEffects.ImageMask {
2019-04-25 21:09:23 +02:00
height: 8
width: 12
2019-03-18 00:12:51 +01:00
visible: root.sortBy === "blockheight" ? true : false
2019-04-11 03:17:29 +02:00
opacity: root.sortBy === "blockheight" ? 1 : 0.2
image: "qrc:///images/whiteDropIndicator.png"
fontAwesomeFallbackIcon: FontAwesome.arrowDown
fontAwesomeFallbackSize: 14
color: MoneroComponents.Style.defaultFontColor
2019-03-18 00:12:51 +01:00
rotation: {
if(root.sortBy === "blockheight"){
return root.sortDirection ? 0 : 180
} else {
return 0;
}
}
}
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
Item {
Layout.fillWidth: true
2019-03-18 00:12:51 +01:00
}
}
2019-03-18 00:12:51 +01:00
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
if(root.sortBy !== "blockheight") {
root.sortDirection = true;
} else {
root.sortDirection = !root.sortDirection
}
2019-03-18 00:12:51 +01:00
root.sortBy = "blockheight";
root.updateSort();
}
}
}
2019-03-18 00:12:51 +01:00
Rectangle {
visible: sortAndFilter.collapsed
color: "transparent"
2019-04-25 21:09:23 +02:00
Layout.preferredWidth: sortDateText.width + 42
2019-03-18 00:12:51 +01:00
Layout.preferredHeight: 20
RowLayout {
2019-03-18 00:12:51 +01:00
clip: true
anchors.fill: parent
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
id: sortDateText
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Date") + translationManager.emptyString
2019-03-18 00:12:51 +01:00
color: root.sortBy === "timestamp" ? MoneroComponents.Style.defaultFontColor : MoneroComponents.Style.dimmedFontColor
2019-04-11 03:17:29 +02:00
themeTransition: false
2019-03-18 00:12:51 +01:00
}
2019-04-11 03:17:29 +02:00
MoneroEffects.ImageMask {
2019-04-25 21:09:23 +02:00
height: 8
width: 12
2019-03-18 00:12:51 +01:00
visible: root.sortBy === "timestamp" ? true : false
2019-04-11 03:17:29 +02:00
opacity: root.sortBy === "timestamp" ? 1 : 0.2
image: "qrc:///images/whiteDropIndicator.png"
fontAwesomeFallbackIcon: FontAwesome.arrowDown
fontAwesomeFallbackSize: 14
color: MoneroComponents.Style.defaultFontColor
2019-03-18 00:12:51 +01:00
rotation: {
if(root.sortBy === "timestamp"){
return root.sortDirection ? 0 : 180
} else {
return 0;
}
}
}
2019-03-18 00:12:51 +01:00
Item {
Layout.fillWidth: true
2019-03-18 00:12:51 +01:00
}
}
2019-03-18 00:12:51 +01:00
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
if(root.sortBy !== "timestamp") {
root.sortDirection = true;
} else {
root.sortDirection = !root.sortDirection
}
2019-03-18 00:12:51 +01:00
root.sortBy = "timestamp";
root.updateSort();
}
2016-10-08 01:28:27 +02:00
}
}
2019-03-18 00:12:51 +01:00
Rectangle {
visible: sortAndFilter.collapsed
color: "transparent"
2019-04-25 21:09:23 +02:00
Layout.preferredWidth: sortAmountText.width + 42
2019-03-18 00:12:51 +01:00
Layout.preferredHeight: 20
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
RowLayout {
clip: true
anchors.fill: parent
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
id: sortAmountText
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Amount") + translationManager.emptyString
2019-03-18 00:12:51 +01:00
color: root.sortBy === "amount" ? MoneroComponents.Style.defaultFontColor : MoneroComponents.Style.dimmedFontColor
2019-04-11 03:17:29 +02:00
themeTransition: false
2019-03-18 00:12:51 +01:00
}
2014-07-16 18:04:34 +02:00
2019-04-11 03:17:29 +02:00
MoneroEffects.ImageMask {
2019-04-25 21:09:23 +02:00
height: 8
width: 12
2019-03-18 00:12:51 +01:00
visible: root.sortBy === "amount" ? true : false
2019-04-11 03:17:29 +02:00
opacity: root.sortBy === "amount" ? 1 : 0.2
image: "qrc:///images/whiteDropIndicator.png"
fontAwesomeFallbackIcon: FontAwesome.arrowDown
fontAwesomeFallbackSize: 14
color: MoneroComponents.Style.defaultFontColor
2019-03-18 00:12:51 +01:00
rotation: {
if(root.sortBy === "amount"){
return root.sortDirection ? 0 : 180
} else {
return 0;
}
}
}
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
Item {
Layout.fillWidth: true
}
}
2016-10-08 01:28:27 +02:00
2019-03-18 00:12:51 +01:00
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
if(root.sortBy !== "amount") {
root.sortDirection = true;
} else {
root.sortDirection = !root.sortDirection
}
2019-03-18 00:12:51 +01:00
root.sortBy = "amount";
root.updateSort();
}
}
}
2019-03-18 00:12:51 +01:00
Rectangle {
visible: !sortAndFilter.collapsed
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
// status message
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-03-18 00:12:51 +01:00
text: root.historyStatusMessage
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
}
}
Item {
Layout.fillWidth: true
2019-03-18 00:12:51 +01:00
}
RowLayout {
id: pagination
visible: root.txCount > 0
2019-03-18 00:12:51 +01:00
spacing: 0
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: childrenRect.width
Layout.preferredHeight: 20
2014-07-16 18:04:34 +02:00
Rectangle {
2019-03-18 00:12:51 +01:00
color: "transparent"
2019-04-25 21:09:23 +02:00
Layout.preferredWidth: childrenRect.width + 2
2019-03-18 00:12:51 +01:00
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-12-02 00:08:43 +01:00
text: qsTr("Page") + ":" + translationManager.emptyString
2019-03-18 00:12:51 +01:00
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
}
}
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
Rectangle {
2018-03-21 02:12:02 +01:00
color: "transparent"
2019-04-25 21:09:23 +02:00
Layout.preferredWidth: childrenRect.width + 10
Layout.leftMargin: 4
Layout.preferredHeight: 20
2019-03-18 00:12:51 +01:00
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
id: paginationText
text: root.txPage + "/" + Math.ceil(root.txCount / root.txMax)
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.defaultFontColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
MouseArea {
// jump to page functionality
property int pages: Math.ceil(root.txCount / root.txMax)
anchors.fill: parent
hoverEnabled: pages > 1
cursorShape: hoverEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onEntered: parent.color = MoneroComponents.Style.orange
onExited: parent.color = MoneroComponents.Style.defaultFontColor
onClicked: {
if(pages === 1)
return;
inputDialog.labelText = qsTr("Jump to page (1-%1)").arg(pages) + translationManager.emptyString;
inputDialog.onAcceptedCallback = function() {
var pageNumber = parseInt(inputDialog.inputText);
if (!isNaN(pageNumber) && pageNumber >= 1 && pageNumber <= pages) {
root.paginationJump(parseInt(pageNumber));
return;
}
appWindow.showStatusMessage(qsTr("Invalid page. Must be a number within the specified range."), 4);
}
inputDialog.onRejectedCallback = null;
inputDialog.open()
}
}
}
}
2019-03-18 00:12:51 +01:00
Rectangle {
id: paginationPrev
2019-04-25 21:09:23 +02:00
Layout.preferredWidth: 18
Layout.preferredHeight: 20
2019-03-18 00:12:51 +01:00
color: "transparent"
2019-04-11 03:17:29 +02:00
opacity: enabled ? 1.0 : 0.2
2019-03-18 00:12:51 +01:00
enabled: false
2019-04-11 03:17:29 +02:00
MoneroEffects.ImageMask {
id: prevIcon
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
2019-04-25 21:09:23 +02:00
height: 8
width: 12
2019-04-11 03:17:29 +02:00
image: "qrc:///images/whiteDropIndicator.png"
fontAwesomeFallbackIcon: FontAwesome.arrowDown
fontAwesomeFallbackSize: 14
color: MoneroComponents.Style.defaultFontColor
2019-03-18 00:12:51 +01:00
rotation: 90
}
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
MouseArea {
enabled: parent.enabled
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.paginationPrevClicked();
}
}
2019-03-18 00:12:51 +01:00
}
Rectangle {
id: paginationNext
2019-04-25 21:09:23 +02:00
Layout.preferredWidth: 18
Layout.preferredHeight: 20
2019-03-18 00:12:51 +01:00
color: "transparent"
2019-04-11 03:17:29 +02:00
opacity: enabled ? 1.0 : 0.2
2019-03-18 00:12:51 +01:00
enabled: false
2014-07-16 18:04:34 +02:00
2019-04-11 03:17:29 +02:00
MoneroEffects.ImageMask {
id: nextIcon
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
2019-04-25 21:09:23 +02:00
height: 8
width: 12
2019-04-11 03:17:29 +02:00
image: "qrc:///images/whiteDropIndicator.png"
fontAwesomeFallbackIcon: FontAwesome.arrowDown
fontAwesomeFallbackSize: 14
2019-03-18 00:12:51 +01:00
rotation: 270
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.defaultFontColor
2019-03-18 00:12:51 +01:00
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.paginationNextClicked();
}
}
}
}
}
ListView {
visible: true
id: txListview
Layout.fillWidth: true
Layout.preferredHeight: contentHeight;
model: txListViewModel
interactive: false
delegate: Rectangle {
id: delegate
property bool collapsed: root.txDataCollapsed.indexOf(hash) >= 0 ? true : false
2020-06-12 03:09:18 +02:00
anchors.left: parent ? parent.left : undefined
anchors.right: parent ? parent.right : undefined
2019-03-18 00:12:51 +01:00
height: {
if(!collapsed) return 60;
return 320;
2019-03-18 00:12:51 +01:00
}
2019-04-11 03:17:29 +02:00
color: {
if(!collapsed) return "transparent"
return MoneroComponents.Style.blackTheme ? "#06FFFFFF" : "#04000000"
}
2019-03-18 00:12:51 +01:00
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: sideMargin
color: "transparent"
Rectangle {
visible: !isFailed && !isPending
anchors.top: parent.top
2019-04-25 21:09:23 +02:00
anchors.topMargin: 24
2019-03-18 00:12:51 +01:00
anchors.horizontalCenter: parent.horizontalCenter
2019-04-25 21:09:23 +02:00
width: 10
height: 10
radius: 8
2019-03-18 00:12:51 +01:00
color: isout ? "#d85a00" : "#2eb358"
}
MoneroComponents.TextPlain {
visible: isFailed || isPending
anchors.top: parent.top
anchors.topMargin: 24
anchors.horizontalCenter: parent.horizontalCenter
font.family: FontAwesome.fontFamilySolid
font.styleName: isFailed ? "Solid" : ""
font.pixelSize: 15
text: isFailed ? FontAwesome.times : FontAwesome.clockO
color: isFailed ? "#FF0000" : MoneroComponents.Style.defaultFontColor
themeTransition: false
}
2019-03-18 00:12:51 +01:00
}
2019-03-18 00:12:51 +01:00
ColumnLayout {
spacing: 0
clip: true
height: parent.height
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: sideMargin
anchors.rightMargin: sideMargin
RowLayout {
spacing: 0
Layout.fillWidth: true
height: 60
Layout.preferredHeight: 60
ColumnLayout {
spacing: 0
clip: true
Layout.preferredHeight: 120
Layout.minimumWidth: 180
2019-03-18 00:12:51 +01:00
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: (isout ? qsTr("Sent") : qsTr("Received")) + (isFailed ? " (" + qsTr("Failed") + ")" : (isPending ? " (" + qsTr("Pending") + ")" : "")) + translationManager.emptyString
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.historyHeaderTextColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
2019-04-11 03:17:29 +02:00
themeTransitionBlackColor: MoneroComponents.Style._b_historyHeaderTextColor
themeTransitionWhiteColor: MoneroComponents.Style._w_historyHeaderTextColor
2019-03-18 00:12:51 +01:00
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: (amount == 0 ? qsTr("Unknown amount") : displayAmount) + translationManager.emptyString
2019-03-18 00:12:51 +01:00
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
state: "copyable"
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = MoneroComponents.Style.orange
onExited: parent.color = MoneroComponents.Style.defaultFontColor
}
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-04-11 03:17:29 +02:00
text: isout ? qsTr("Fee") : confirmationsRequired === 60 ? qsTr("Mined") : qsTr("Fee") + translationManager.emptyString
color: MoneroComponents.Style.historyHeaderTextColor
themeTransitionBlackColor: MoneroComponents.Style._b_historyHeaderTextColor
themeTransitionWhiteColor: MoneroComponents.Style._w_historyHeaderTextColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-03-18 00:12:51 +01:00
text: {
if(!isout && confirmationsRequired === 60) return qsTr("Yes") + translationManager.emptyString;
if(fee !== "") return Utils.removeTrailingZeros(fee) + " XMR";
2019-03-18 00:12:51 +01:00
return "-";
}
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
state: "copyable"
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = MoneroComponents.Style.orange
onExited: parent.color = MoneroComponents.Style.defaultFontColor
}
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
}
2019-03-18 00:12:51 +01:00
ColumnLayout {
spacing: 0
clip: true
Layout.preferredHeight: 120
Layout.minimumWidth: 230
2019-03-18 00:12:51 +01:00
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: (isout ? qsTr("To") : qsTr("In")) + translationManager.emptyString
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.historyHeaderTextColor
themeTransitionBlackColor: MoneroComponents.Style._b_historyHeaderTextColor
themeTransitionWhiteColor: MoneroComponents.Style._w_historyHeaderTextColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
id: addressField
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 15
text: {
if (isout) {
if (address) {
return (addressBookName ? FontAwesome.addressBook + " " + addressBookName : TxUtils.addressTruncate(address, 8));
}
if (amount != 0) {
return qsTr("Unknown recipient") + translationManager.emptyString;
} else {
return qsTr("My wallet") + translationManager.emptyString;
}
} else {
if (receivingAddress) {
if (subaddrIndex == 0) {
return qsTr("Address") + " #0" + " (" + qsTr("Primary address") + ")" + translationManager.emptyString;
} else {
if (receivingAddressLabel) {
return qsTr("Address") + " #" + subaddrIndex + " (" + receivingAddressLabel + ")" + translationManager.emptyString;
} else {
return qsTr("Address") + " #" + subaddrIndex + " (" + TxUtils.addressTruncate(receivingAddress, 4) + ")" + translationManager.emptyString;
}
}
} else {
return qsTr("Unknown address") + translationManager.emptyString;
}
}
}
2019-03-18 00:12:51 +01:00
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
state: isout ? "copyable_address" : "copyable_receiving_address"
2019-03-18 00:12:51 +01:00
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = MoneroComponents.Style.orange
onExited: parent.color = MoneroComponents.Style.defaultFontColor
}
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Confirmations") + translationManager.emptyString
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.historyHeaderTextColor
themeTransitionBlackColor: MoneroComponents.Style._b_historyHeaderTextColor
themeTransitionWhiteColor: MoneroComponents.Style._w_historyHeaderTextColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
property bool confirmed: confirmations < confirmationsRequired ? false : true
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-03-18 00:12:51 +01:00
text: confirmed ? confirmations : confirmations + "/" + confirmationsRequired
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
state: "copyable"
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = MoneroComponents.Style.orange
onExited: parent.color = MoneroComponents.Style.defaultFontColor
}
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
}
ColumnLayout {
spacing: 0
clip: true
Layout.preferredHeight: 120
Layout.minimumWidth: 130
2019-03-18 00:12:51 +01:00
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-04-11 03:17:29 +02:00
text: qsTr("Date")
color: MoneroComponents.Style.historyHeaderTextColor
themeTransitionBlackColor: MoneroComponents.Style._b_historyHeaderTextColor
themeTransitionWhiteColor: MoneroComponents.Style._w_historyHeaderTextColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: persistentSettings.historyHumanDates ? dateHuman : dateTime
2019-03-18 00:12:51 +01:00
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
state: "copyable"
anchors.fill: parent
hoverEnabled: true
onEntered: {
parent.color = MoneroComponents.Style.orange
if (persistentSettings.historyHumanDates) {
parent.text = dateTime;
}
}
onExited: {
parent.color = MoneroComponents.Style.defaultFontColor
if (persistentSettings.historyHumanDates) {
parent.text = dateHuman
}
}
2019-03-18 00:12:51 +01:00
}
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
2019-03-18 00:12:51 +01:00
Item {
Layout.fillWidth: true
Layout.preferredHeight: 50
2019-03-18 00:12:51 +01:00
MoneroComponents.StandardButton {
id: btnDetails
text: FontAwesome.info
small: true
label.font.family: FontAwesome.fontFamily
2019-04-25 21:09:23 +02:00
fontSize: 18
width: 34
tooltip: qsTr("Transaction details") + translationManager.emptyString
tooltipLeft: true
2019-03-18 00:12:51 +01:00
MouseArea {
state: "details"
anchors.fill: parent
hoverEnabled: true
z: parent.z + 1
2019-04-11 03:17:29 +02:00
onEntered: {
parent.opacity = 0.8;
parent.tooltipPopup.open()
}
onExited: {
parent.opacity = 1.0;
parent.tooltipPopup.close()
}
2019-03-18 00:12:51 +01:00
}
}
Image {
visible: !isout && confirmationsRequired === 60
anchors.left: btnDetails.right
2019-04-25 21:09:23 +02:00
anchors.leftMargin: 16
2019-03-18 00:12:51 +01:00
width: 28
height: 28
source: "qrc:///images/miningxmr.png"
}
MoneroComponents.StandardButton {
visible: isout
enabled: currentWallet ? !currentWallet.isHwBacked() : false
2019-03-18 00:12:51 +01:00
anchors.left: btnDetails.right
2019-04-25 21:09:23 +02:00
anchors.leftMargin: 10
2019-03-18 00:12:51 +01:00
text: FontAwesome.productHunt
small: true
2019-09-03 20:41:08 +02:00
label.font.family: FontAwesome.fontFamilyBrands
2019-04-25 21:09:23 +02:00
fontSize: 18
width: 34
tooltip: qsTr("Generate payment proof") + translationManager.emptyString
tooltipLeft: true
2019-03-18 00:12:51 +01:00
MouseArea {
state: "proof"
anchors.fill: parent
hoverEnabled: true
z: parent.z + 1
2019-04-11 03:17:29 +02:00
onEntered: {
parent.opacity = 0.8;
parent.tooltipPopup.open()
}
onExited: {
parent.opacity = 1.0;
parent.tooltipPopup.close()
}
2019-03-18 00:12:51 +01:00
}
}
}
}
}
ColumnLayout {
spacing: 0
Layout.fillWidth: true
Layout.preferredHeight: 40
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Description") + translationManager.emptyString
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.historyHeaderTextColor
themeTransitionBlackColor: MoneroComponents.Style._b_historyHeaderTextColor
themeTransitionWhiteColor: MoneroComponents.Style._w_historyHeaderTextColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
id: txNoteText
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-03-18 00:12:51 +01:00
text: tx_note !== "" ? tx_note : "-"
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
state: "copyable"
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = MoneroComponents.Style.orange
onExited: parent.color = MoneroComponents.Style.defaultFontColor
}
}
2019-04-11 03:17:29 +02:00
MoneroEffects.ImageMask {
2019-03-18 00:12:51 +01:00
anchors.top: parent.top
anchors.left: txNoteText.right
2019-04-25 21:09:23 +02:00
anchors.leftMargin: 12
2019-04-11 03:17:29 +02:00
image: "qrc:///images/edit.svg"
fontAwesomeFallbackIcon: FontAwesome.pencilSquare
fontAwesomeFallbackSize: 22
color: MoneroComponents.Style.defaultFontColor
opacity: 0.75
width: 23
height: 21
2019-03-18 00:12:51 +01:00
MouseArea {
id: txNoteArea
state: "set_tx_note"
anchors.fill: parent
hoverEnabled: true
2019-04-11 03:17:29 +02:00
onEntered: parent.opacity = 0.4;
onExited: parent.opacity = 0.75;
2019-03-18 00:12:51 +01:00
cursorShape: Qt.PointingHandCursor
}
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Transaction ID") + translationManager.emptyString
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.historyHeaderTextColor
themeTransitionBlackColor: MoneroComponents.Style._b_historyHeaderTextColor
themeTransitionWhiteColor: MoneroComponents.Style._w_historyHeaderTextColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-03-18 00:12:51 +01:00
text: hash
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
state: "copyable"
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = MoneroComponents.Style.orange
onExited: parent.color = MoneroComponents.Style.defaultFontColor
}
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Transaction key") + translationManager.emptyString
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.historyHeaderTextColor
themeTransitionBlackColor: MoneroComponents.Style._b_historyHeaderTextColor
themeTransitionWhiteColor: MoneroComponents.Style._w_historyHeaderTextColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Click to reveal")
2019-03-18 00:12:51 +01:00
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
state: "txkey_hidden"
2019-03-18 00:12:51 +01:00
MouseArea {
state: "copyable_txkey"
2019-03-18 00:12:51 +01:00
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = MoneroComponents.Style.orange
onExited: parent.color = MoneroComponents.Style.defaultFontColor
}
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
text: qsTr("Blockheight") + translationManager.emptyString
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.historyHeaderTextColor
themeTransitionBlackColor: MoneroComponents.Style._b_historyHeaderTextColor
themeTransitionWhiteColor: MoneroComponents.Style._w_historyHeaderTextColor
2019-03-18 00:12:51 +01:00
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 20
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 14
text: (blockheight > 0 ? blockheight : qsTr('Pending')) + translationManager.emptyString;
2019-03-18 00:12:51 +01:00
color: MoneroComponents.Style.defaultFontColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
state: "copyable"
2019-03-18 00:12:51 +01:00
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = MoneroComponents.Style.orange
onExited: parent.color = MoneroComponents.Style.defaultFontColor
}
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.preferredHeight: 10
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
MouseArea {
id: collapseArea
objectName: "collapseArea"
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
// detect clicks on text (for copying), otherwise toggle collapse
var doCollapse = true;
var res = Utils.qmlEach(delegate, ['containsMouse', 'preventStealing', 'scrollGestureEnabled'], ['collapseArea'], []);
for(var i = 0; i < res.length; i+=1){
if(res[i].containsMouse === true){
if(res[i].state === 'copyable' && res[i].parent.hasOwnProperty('text')) toClipboard(res[i].parent.text);
if(res[i].state === 'copyable_address') (address ? root.toClipboard(address) : root.toClipboard(addressField.text));
if(res[i].state === 'copyable_receiving_address') root.toClipboard(currentWallet.address(subaddrAccount, subaddrIndex));
if(res[i].state === 'copyable_txkey') root.getTxKey(hash, res[i]);
if(res[i].state === 'set_tx_note') root.editDescription(hash, tx_note, root.txPage);
if(res[i].state === 'details') root.showTxDetails(hash, paymentId, destinations, subaddrAccount, subaddrIndex, dateTime, displayAmount, isout);
2019-03-18 00:12:51 +01:00
if(res[i].state === 'proof') root.showTxProof(hash, paymentId, destinations, subaddrAccount, subaddrIndex);
doCollapse = false;
break;
}
}
if(doCollapse){
collapsed = !collapsed;
// remember collapsed state
if(collapsed){
root.txDataCollapsed.push(hash);
} else {
root.removeFromCollapsedList(hash);
}
}
}
2019-03-18 00:12:51 +01:00
}
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: sideMargin
color: "transparent"
2014-07-16 18:04:34 +02:00
2019-04-11 03:17:29 +02:00
MoneroEffects.ImageMask {
id: collapsedIcon
anchors.top: parent.top
2019-04-25 21:09:23 +02:00
anchors.topMargin: 24
2019-03-18 00:12:51 +01:00
anchors.horizontalCenter: parent.horizontalCenter
2019-04-25 21:09:23 +02:00
height: 8
width: 12
2019-04-11 03:17:29 +02:00
image: "qrc:///images/whiteDropIndicator.png"
2019-03-18 00:12:51 +01:00
rotation: delegate.collapsed ? 180 : 0
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.defaultFontColor
2021-06-09 17:14:55 +02:00
fontAwesomeFallbackIcon: FontAwesome.arrowDown
fontAwesomeFallbackSize: 14
}
2016-10-09 00:40:13 +02:00
}
2019-03-18 00:12:51 +01:00
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: 1
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.appWindowBorderColor
MoneroEffects.ColorTransition {
targetObj: parent
blackColor: MoneroComponents.Style._b_appWindowBorderColor
whiteColor: MoneroComponents.Style._w_appWindowBorderColor
}
2019-03-18 00:12:51 +01:00
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.bottom
height: 1
2019-04-11 03:17:29 +02:00
color: MoneroComponents.Style.appWindowBorderColor
MoneroEffects.ColorTransition {
targetObj: parent
blackColor: MoneroComponents.Style._b_appWindowBorderColor
whiteColor: MoneroComponents.Style._w_appWindowBorderColor
}
2019-03-18 00:12:51 +01:00
}
2014-07-16 18:04:34 +02:00
}
2019-03-18 00:12:51 +01:00
}
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
Item {
visible: sortAndFilter.collapsed
2019-04-25 21:09:23 +02:00
Layout.topMargin: 10
Layout.bottomMargin: 10
2019-03-18 00:12:51 +01:00
Layout.leftMargin: sideMargin
Layout.rightMargin: sideMargin
2019-04-11 03:17:29 +02:00
MoneroComponents.TextPlain {
2019-03-18 00:12:51 +01:00
// status message
Layout.fillWidth: true
2019-03-18 00:12:51 +01:00
Layout.alignment: Qt.AlignHCenter
font.family: MoneroComponents.Style.fontRegular.name
2019-04-25 21:09:23 +02:00
font.pixelSize: 15
2019-03-18 00:12:51 +01:00
text: root.historyStatusMessage;
color: MoneroComponents.Style.dimmedFontColor
2019-04-11 03:17:29 +02:00
themeTransitionBlackColor: MoneroComponents.Style._b_dimmedFontColor
themeTransitionWhiteColor: MoneroComponents.Style._w_dimmedFontColor
2019-03-18 00:12:51 +01:00
}
}
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
MoneroComponents.CheckBox2 {
id: showAdvancedCheckbox
2019-04-25 21:09:23 +02:00
Layout.topMargin: 30
Layout.bottomMargin: 20
2019-03-18 00:12:51 +01:00
Layout.leftMargin: sideMargin
Layout.rightMargin: sideMargin
checked: persistentSettings.historyShowAdvanced
onClicked: persistentSettings.historyShowAdvanced = !persistentSettings.historyShowAdvanced
text: qsTr("Advanced options") + translationManager.emptyString
}
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
ColumnLayout {
visible: persistentSettings.historyShowAdvanced
Layout.leftMargin: sideMargin
Layout.rightMargin: sideMargin
2019-04-25 21:09:23 +02:00
spacing: 20
2019-03-18 00:12:51 +01:00
MoneroComponents.CheckBox {
id: humanDatesCheckBox
checked: persistentSettings.historyHumanDates
onClicked: {
persistentSettings.historyHumanDates = !persistentSettings.historyHumanDates
root.updateDisplay(root.txOffset, root.txMax);
}
2019-03-18 00:12:51 +01:00
text: qsTr("Human readable date format") + translationManager.emptyString
}
MoneroComponents.StandardButton {
visible: !isIOS
2019-03-18 00:12:51 +01:00
small: true
text: qsTr("Export all history") + translationManager.emptyString
onClicked: {
writeCSVFileDialog.open();
}
}
}
}
function refresh(){
if(appWindow.currentWallet != null && typeof appWindow.currentWallet.history !== "undefined" ) {
currentWallet.history.refresh(currentWallet.currentSubaddressAccount);
}
if (typeof root.model !== 'undefined' && root.model != null) {
toDatePicker.currentDate = root.model.transactionHistory.lastDateTime
2019-03-18 00:12:51 +01:00
}
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
// extract from model, create JS array of txs
root.updateTransactionsFromModel();
2014-07-16 18:04:34 +02:00
2019-03-18 00:12:51 +01:00
// fill listview, update UI
root.updateDisplay(root.txOffset, root.txMax);
}
function reset(keepDate) {
2019-03-18 00:12:51 +01:00
root.txOffset = 0;
if (typeof root.model !== 'undefined' && root.model != null) {
if (!keepDate) {
root.model.dateFromFilter = "2014-04-18" // genesis block
root.model.dateToFilter = "9999-09-09" // fix before september 9999
}
2019-03-18 00:12:51 +01:00
// negative values disable filters here;
root.model.amountFromFilter = -1;
root.model.amountToFilter = -1;
root.model.directionFilter = TransactionInfo.Direction_Both;
}
}
function updateFilter(currentPage){
2019-03-18 00:12:51 +01:00
// applying filters
root.txData = JSON.parse(JSON.stringify(root.txModelData)); // deepcopy
const timezoneOffset = new Date().getTimezoneOffset() * 60;
var fromDate = Math.floor(fromDatePicker.currentDate.getTime() / 86400000) * 86400 + timezoneOffset;
var toDate = (Math.floor(toDatePicker.currentDate.getTime() / 86400000) + 1) * 86400 + timezoneOffset;
2019-03-18 00:12:51 +01:00
var txs = [];
for (var i = 0; i < root.txData.length; i++){
var item = root.txData[i];
var matched = "";
// daterange filtering
if(item.timestamp < fromDate || item.timestamp > toDate){
continue;
}
// search string filtering
if(root.sortSearchString == null || root.sortSearchString === ""){
txs.push(root.txData[i]);
continue;
}
if(root.sortSearchString.length >= 1){
if(item.amount && item.amount.toString().startsWith(root.sortSearchString)){
txs.push(item);
} else if(item.address !== "" && item.address.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
2019-03-18 00:12:51 +01:00
txs.push(item);
} else if(item.receivingAddress !== "" && item.receivingAddress.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
txs.push(item);
} else if(item.receivingAddressLabel !== "" && item.receivingAddressLabel.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
txs.push(item);
} else if(item.addressBookName !== "" && item.addressBookName.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
txs.push(item);
} else if(typeof item.blockheight !== "undefined" && item.blockheight.toString().startsWith(root.sortSearchString)) {
2019-03-18 00:12:51 +01:00
txs.push(item);
2019-07-03 13:54:01 +02:00
} else if(item.tx_note.toLowerCase().indexOf(root.sortSearchString.toLowerCase()) !== -1) {
txs.push(item);
2019-03-18 00:12:51 +01:00
} else if (item.hash.startsWith(root.sortSearchString)){
txs.push(item);
2016-10-07 22:05:51 +02:00
}
}
2014-07-16 18:04:34 +02:00
}
2019-03-18 00:12:51 +01:00
root.txData = txs;
root.txCount = root.txData.length;
root.updateSort();
root.updateDisplay(root.txOffset, root.txMax);
if (currentPage) {
root.paginationJump(parseInt(currentPage));
}
2019-03-18 00:12:51 +01:00
}
function updateSort(){
// applying sorts
root.txOffset = 0;
root.txData.sort(function(a, b) {
return a[root.sortBy] - b[root.sortBy];
});
if(root.sortDirection)
root.txData.reverse();
root.updateDisplay(root.txOffset, root.txMax);
}
function updateDisplay(tx_offset, tx_max) {
2019-03-18 00:12:51 +01:00
txListViewModel.clear();
// limit results as per tx_max (root.txMax)
var txs = root.txData.slice(tx_offset, tx_offset + tx_max);
// collapse tx if there is a single result
if(root.txPage === 1 && txs.length === 1)
2019-03-18 00:12:51 +01:00
root.txDataCollapsed.push(txs[0]['hash']);
// populate listview
for (var i = 0; i < txs.length; i++){
txListViewModel.append(txs[i]);
}
root.updateHistoryStatusMessage();
// determine pagination button states
var count = txData.length;
if(count <= root.txMax) {
paginationPrev.enabled = false;
paginationNext.enabled = false;
return;
}
if(root.txOffset < root.txMax)
paginationPrev.enabled = false;
else
paginationPrev.enabled = true;
if((root.txOffset + root.txMax) >= count)
paginationNext.enabled = false;
else
paginationNext.enabled = true;
}
function updateTransactionsFromModel() {
// This function copies the items of `appWindow.currentWallet.historyModel` to `root.txModelData`, as a list of javascript objects
if(currentWallet == null || typeof currentWallet.history === "undefined" ) return;
var _model = root.model;
var total = 0
var count = _model.rowCount()
root.txModelData = [];
for (var i = 0; i < count; ++i) {
var idx = _model.index(i, 0);
var isPending = model.data(idx, TransactionHistoryModel.TransactionPendingRole);
var isFailed = model.data(idx, TransactionHistoryModel.TransactionFailedRole);
2019-03-18 00:12:51 +01:00
var isout = _model.data(idx, TransactionHistoryModel.TransactionIsOutRole);
var amount = _model.data(idx, TransactionHistoryModel.TransactionAmountRole);
var hash = _model.data(idx, TransactionHistoryModel.TransactionHashRole);
var paymentId = _model.data(idx, TransactionHistoryModel.TransactionPaymentIdRole);
var destinations = _model.data(idx, TransactionHistoryModel.TransactionDestinationsRole);
var time = _model.data(idx, TransactionHistoryModel.TransactionTimeRole);
var date = _model.data(idx, TransactionHistoryModel.TransactionDateRole);
var blockheight = _model.data(idx, TransactionHistoryModel.TransactionBlockHeightRole);
var confirmations = _model.data(idx, TransactionHistoryModel.TransactionConfirmationsRole);
var confirmationsRequired = _model.data(idx, TransactionHistoryModel.TransactionConfirmationsRequiredRole);
var fee = _model.data(idx, TransactionHistoryModel.TransactionFeeRole);
var subaddrAccount = model.data(idx, TransactionHistoryModel.TransactionSubaddrAccountRole);
var subaddrIndex = model.data(idx, TransactionHistoryModel.TransactionSubaddrIndexRole);
var timestamp = new Date(date + " " + time).getTime() / 1000;
var dateHuman = Utils.ago(timestamp);
if (amount === 0) {
// transactions to the same account have amount === 0, while the 'destinations string'
2019-03-18 00:12:51 +01:00
// has the correct amount, so we try to fetch it from that instead.
amount = Number(TxUtils.destinationsToAmount(destinations));
2019-03-18 00:12:51 +01:00
}
var displayAmount = Utils.removeTrailingZeros(amount.toFixed(12)) + " XMR";
2019-03-18 00:12:51 +01:00
var tx_note = currentWallet.getUserNote(hash);
var address = "";
var addressBookName = "";
var receivingAddress = "";
var receivingAddressLabel = "";
if (isout) {
2019-03-18 00:12:51 +01:00
address = TxUtils.destinationsToAddress(destinations);
addressBookName = currentWallet ? currentWallet.addressBook.getDescription(address) : null;
} else {
receivingAddress = currentWallet ? currentWallet.address(subaddrAccount, subaddrIndex) : null;
receivingAddressLabel = currentWallet ? appWindow.currentWallet.getSubaddressLabel(subaddrAccount, subaddrIndex) : null;
2019-03-18 00:12:51 +01:00
}
if (isout)
total = walletManager.subi(total, amount)
else
total = walletManager.addi(total, amount)
root.txModelData.push({
"i": i,
"isPending": isPending,
"isFailed": isFailed,
2019-03-18 00:12:51 +01:00
"isout": isout,
"amount": amount,
"displayAmount": displayAmount,
2019-03-18 00:12:51 +01:00
"hash": hash,
"paymentId": paymentId,
"address": address,
"addressBookName": addressBookName,
2019-03-18 00:12:51 +01:00
"destinations": destinations,
"tx_note": tx_note,
"dateHuman": dateHuman,
"dateTime": date + " " + time,
2019-03-18 00:12:51 +01:00
"blockheight": blockheight,
"address": address,
"timestamp": timestamp,
"fee": fee,
"confirmations": confirmations,
"confirmationsRequired": confirmationsRequired,
"receivingAddress": receivingAddress,
"receivingAddressLabel": receivingAddressLabel,
2019-03-18 00:12:51 +01:00
"subaddrAccount": subaddrAccount,
"subaddrIndex": subaddrIndex
});
}
root.txData = JSON.parse(JSON.stringify(root.txModelData)); // deepcopy
root.txCount = root.txData.length;
}
function update(currentPage) {
2019-03-18 00:12:51 +01:00
// handle outside mutation of tx model; incoming/outgoing funds or new blocks. Update table.
currentWallet.history.refresh(currentWallet.currentSubaddressAccount);
root.updateTransactionsFromModel();
root.updateFilter(currentPage);
2019-03-18 00:12:51 +01:00
}
function editDescription(_hash, _tx_note, currentPage){
2019-03-18 00:12:51 +01:00
inputDialog.labelText = qsTr("Set description:") + translationManager.emptyString;
inputDialog.onAcceptedCallback = function() {
appWindow.currentWallet.setUserNote(_hash, inputDialog.inputText);
appWindow.showStatusMessage(qsTr("Updated description."),3);
root.update(currentPage);
2019-03-18 00:12:51 +01:00
}
inputDialog.onRejectedCallback = null;
2019-12-09 00:44:39 +01:00
inputDialog.open(_tx_note);
2019-03-18 00:12:51 +01:00
}
function paginationPrevClicked(){
root.txOffset -= root.txMax;
updateDisplay(root.txOffset, root.txMax);
}
function paginationNextClicked(){
root.txOffset += root.txMax;
updateDisplay(root.txOffset, root.txMax);
}
function paginationJump(pageNumber){
root.txOffset = root.txMax * Math.ceil(pageNumber - 1 || 0);
updateDisplay(root.txOffset, root.txMax);
}
function removeFromCollapsedList(hash){
root.txDataCollapsed = root.txDataCollapsed.filter(function(item) {
return item !== hash
});
}
function updateHistoryStatusMessage(){
if(root.txModelData.length <= 0){
root.historyStatusMessage = qsTr("No transaction history yet.") + translationManager.emptyString;
2019-03-18 00:12:51 +01:00
} else if (root.txData.length <= 0){
root.historyStatusMessage = qsTr("No results.") + translationManager.emptyString;
2019-03-18 00:12:51 +01:00
} else {
root.historyStatusMessage = qsTr("%1 transactions total, showing %2.").arg(root.txData.length).arg(txListViewModel.count) + translationManager.emptyString;
}
}
function getTxKey(hash, elem){
if (elem.parent.state != 'ready'){
currentWallet.getTxKeyAsync(hash, function(hash, txKey) {
elem.parent.text = txKey ? txKey : '-';
elem.parent.state = 'ready';
});
} else {
toClipboard(elem.parent.text);
}
}
function showTxDetails(hash, paymentId, destinations, subaddrAccount, subaddrIndex, dateTime, amount, isout) {
2019-03-18 00:12:51 +01:00
var tx_note = currentWallet.getUserNote(hash)
var rings = currentWallet.getRings(hash)
var address_label = subaddrIndex == 0 ? (qsTr("Primary address") + translationManager.emptyString) : currentWallet.getSubaddressLabel(subaddrAccount, subaddrIndex)
2019-03-18 00:12:51 +01:00
var address = currentWallet.address(subaddrAccount, subaddrIndex)
const hasPaymentId = parseInt(paymentId, 16);
const integratedAddress = !isout && hasPaymentId ? currentWallet.integratedAddress(paymentId) : null;
2019-03-18 00:12:51 +01:00
if (rings)
rings = rings.replace(/\|/g, '\n')
currentWallet.getTxKeyAsync(hash, function(hash, tx_key) {
informationPopup.title = qsTr("Transaction details") + translationManager.emptyString;
informationPopup.content = buildTxDetailsString(hash, hasPaymentId ? paymentId : null, tx_key, tx_note, destinations, rings, address, address_label, integratedAddress, dateTime, amount);
informationPopup.onCloseCallback = null
informationPopup.open();
});
2019-03-18 00:12:51 +01:00
}
function showTxProof(hash, paymentId, destinations, subaddrAccount, subaddrIndex){
var address = TxUtils.destinationsToAddress(destinations);
if(address === undefined){
console.log('getProof: Error fetching address')
return;
}
var checked = (TxUtils.checkTxID(hash) && TxUtils.checkAddress(address, appWindow.persistentSettings.nettype));
if(!checked){
console.log('getProof: Error checking TxId and/or address');
}
console.log("getProof: Generate clicked: txid " + hash + ", address " + address);
middlePanel.getProofClicked(hash, address, '', null);
informationPopup.title = qsTr("Payment proof") + translationManager.emptyString;
informationPopup.text = qsTr("Generating payment proof") + "..." + translationManager.emptyString;
informationPopup.onCloseCallback = null
informationPopup.open()
2019-03-18 00:12:51 +01:00
}
function toClipboard(text){
console.log("Copied to clipboard");
clipboard.setText(text);
appWindow.showStatusMessage(qsTr("Copied to clipboard"),3);
}
function buildTxDetailsString(tx_id, paymentId, tx_key,tx_note, destinations, rings, address, address_label, integratedAddress, dateTime, amount) {
var trStart = '<tr><td style="white-space: nowrap; padding-top:5px"><b>',
2019-03-18 00:12:51 +01:00
trMiddle = '</b></td><td style="padding-left:10px;padding-top:5px;">',
trEnd = "</td></tr>";
return '<table border="0">'
+ (tx_id ? trStart + qsTr("Tx ID:") + trMiddle + tx_id + trEnd : "")
2019-12-05 16:16:11 +01:00
+ (dateTime ? trStart + qsTr("Date") + ":" + trMiddle + dateTime + trEnd : "")
+ (amount ? trStart + qsTr("Amount") + ":" + trMiddle + amount + trEnd : "")
2019-03-18 00:12:51 +01:00
+ (address ? trStart + qsTr("Address:") + trMiddle + address + trEnd : "")
+ (paymentId ? trStart + qsTr("Payment ID:") + trMiddle + paymentId + trEnd : "")
2019-12-05 16:16:11 +01:00
+ (integratedAddress ? trStart + qsTr("Integrated address") + ":" + trMiddle + integratedAddress + trEnd : "")
2019-03-18 00:12:51 +01:00
+ (tx_key ? trStart + qsTr("Tx key:") + trMiddle + tx_key + trEnd : "")
+ (tx_note ? trStart + qsTr("Tx note:") + trMiddle + tx_note + trEnd : "")
+ (destinations ? trStart + qsTr("Destinations:") + trMiddle + destinations + trEnd : "")
+ (rings ? trStart + qsTr("Rings:") + trMiddle + rings + trEnd : "")
+ "</table>"
+ translationManager.emptyString;
}
FileDialog {
id: writeCSVFileDialog
title: qsTr("Please choose a folder") + translationManager.emptyString
selectFolder: true
onRejected: {
console.log("csv write canceled")
}
onAccepted: {
var dataDir = walletManager.urlToLocalPath(writeCSVFileDialog.fileUrl);
var written = currentWallet.history.writeCSV(currentWallet.currentSubaddressAccount, dataDir);
if(written !== ""){
confirmationDialog.title = qsTr("Success") + translationManager.emptyString;
var text = qsTr("CSV file written to: %1").arg(written) + "\n\n"
text += qsTr("Tip: Use your favorite spreadsheet software to sort on blockheight.") + "\n\n" + translationManager.emptyString;
confirmationDialog.text = text;
confirmationDialog.icon = StandardIcon.Information;
confirmationDialog.cancelText = qsTr("Open folder") + translationManager.emptyString;
confirmationDialog.onAcceptedCallback = null;
confirmationDialog.onRejectedCallback = function() {
oshelper.openContainingFolder(written);
}
confirmationDialog.open();
} else {
informationPopup.title = qsTr("Error") + translationManager.emptyString;
informationPopup.text = qsTr("Error exporting transaction data.") + "\n\n" + translationManager.emptyString;
informationPopup.icon = StandardIcon.Critical;
informationPopup.onCloseCallback = null;
informationPopup.open();
}
}
Component.onCompleted: {
var _folder = 'file://' + appWindow.accountsDir;
try {
_folder = 'file://' + desktopFolder;
}
catch(err) {}
finally {
writeCSVFileDialog.folder = _folder;
}
}
}
function onPageCompleted() {
2019-03-18 00:12:51 +01:00
// setup date filter scope according to real transactions
if(appWindow.currentWallet != null){
root.model = appWindow.currentWallet.historyModel;
root.model.sortRole = TransactionHistoryModel.TransactionBlockHeightRole
root.model.sort(0, Qt.DescendingOrder);
var count = root.model.rowCount()
if (count > 0) {
//date of the first transaction
fromDatePicker.currentDate = root.model.data(root.model.index((count - 1), 0), TransactionHistoryModel.TransactionDateRole);
} else {
//date of monero birth (2014-04-18)
fromDatePicker.currentDate = model.transactionHistory.firstDateTime
}
2017-08-08 11:07:06 +02:00
}
2019-03-18 00:12:51 +01:00
root.reset();
root.refresh();
root.initialized = true;
root.updateFilter();
}
2018-12-25 01:12:14 +01:00
2019-03-18 00:12:51 +01:00
function onPageClosed(){
root.initialized = false;
root.reset(true);
root.clearFields();
}
function searchInHistory(searchTerm){
searchInput.text = searchTerm;
searchInput.forceActiveFocus();
searchInput.cursorPosition = searchInput.text.length;
sortAndFilter.collapsed = true;
}
function clearFields() {
sortAndFilter.collapsed = false;
searchInput.text = "";
root.txDataCollapsed = [];
2018-12-25 01:12:14 +01:00
}
2014-07-07 19:08:30 +02:00
}