add (today, yesterday, last week, last month, older) sections to history
This commit is contained in:
@@ -2,6 +2,7 @@ import QtQuick 2.6
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Controls.Material 2.0
|
||||
import QtQml.Models 2.2
|
||||
|
||||
import org.electrum 1.0
|
||||
|
||||
@@ -15,99 +16,149 @@ Pane {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
model: Daemon.currentWallet.historyModel
|
||||
model: visualModel
|
||||
|
||||
delegate: Item {
|
||||
id: delegate
|
||||
section.property: 'section'
|
||||
section.criteria: ViewSection.FullString
|
||||
section.delegate: RowLayout {
|
||||
width: ListView.view.width
|
||||
height: delegateLayout.height
|
||||
required property string section
|
||||
Label {
|
||||
text: section == 'today'
|
||||
? qsTr('Today')
|
||||
: section == 'yesterday'
|
||||
? qsTr('Yesterday')
|
||||
: section == 'lastweek'
|
||||
? qsTr('Last week')
|
||||
: section == 'lastmonth'
|
||||
? qsTr('Last month')
|
||||
: qsTr('Older')
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: constants.paddingLarge
|
||||
font.pixelSize: constants.fontSizeLarge
|
||||
color: constants.mutedForeground
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateLayout
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
DelegateModel {
|
||||
id: visualModel
|
||||
model: Daemon.currentWallet.historyModel
|
||||
|
||||
groups: [
|
||||
DelegateModelGroup { name: 'today'; includeByDefault: false },
|
||||
DelegateModelGroup { name: 'yesterday'; includeByDefault: false },
|
||||
DelegateModelGroup { name: 'lastweek'; includeByDefault: false },
|
||||
DelegateModelGroup { name: 'lastmonth'; includeByDefault: false },
|
||||
DelegateModelGroup { name: 'older'; includeByDefault: false }
|
||||
]
|
||||
|
||||
delegate: Item {
|
||||
id: delegate
|
||||
width: ListView.view.width
|
||||
height: delegateLayout.height
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateLayout
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: txinfo.height
|
||||
|
||||
GridLayout {
|
||||
id: txinfo
|
||||
columns: 3
|
||||
|
||||
x: constants.paddingSmall
|
||||
width: delegate.width - 2*constants.paddingSmall
|
||||
|
||||
Item { Layout.columnSpan: 3; Layout.preferredWidth: 1; Layout.preferredHeight: 1}
|
||||
Image {
|
||||
readonly property variant tx_icons : [
|
||||
"../../../gui/icons/unconfirmed.png",
|
||||
"../../../gui/icons/clock1.png",
|
||||
"../../../gui/icons/clock2.png",
|
||||
"../../../gui/icons/clock3.png",
|
||||
"../../../gui/icons/clock4.png",
|
||||
"../../../gui/icons/clock5.png",
|
||||
"../../../gui/icons/confirmed.png"
|
||||
]
|
||||
|
||||
Layout.preferredWidth: constants.iconSizeLarge
|
||||
Layout.preferredHeight: constants.iconSizeLarge
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.rowSpan: 2
|
||||
source: tx_icons[Math.min(6,model.confirmations)]
|
||||
}
|
||||
|
||||
Label {
|
||||
font.pixelSize: constants.fontSizeLarge
|
||||
Layout.fillWidth: true
|
||||
text: model.label !== '' ? model.label : '<no label>'
|
||||
color: model.label !== '' ? Material.accentColor : 'gray'
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
Label {
|
||||
id: valueLabel
|
||||
font.family: FixedFont
|
||||
font.pixelSize: constants.fontSizeMedium
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: Config.formatSats(model.bc_value)
|
||||
font.bold: true
|
||||
color: model.incoming ? constants.colorCredit : constants.colorDebit
|
||||
}
|
||||
Label {
|
||||
font.pixelSize: constants.fontSizeSmall
|
||||
text: model.date
|
||||
}
|
||||
Label {
|
||||
font.pixelSize: constants.fontSizeXSmall
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: model.fee !== undefined ? 'fee: ' + model.fee : ''
|
||||
}
|
||||
Item { Layout.columnSpan: 3; Layout.preferredWidth: 1; Layout.preferredHeight: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: delegate.ListView.section == delegate.ListView.nextSection
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: constants.paddingTiny
|
||||
color: Qt.rgba(0,0,0,0.10)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: index > 0
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: constants.paddingSmall
|
||||
color: Qt.rgba(0,0,0,0.10)
|
||||
}
|
||||
|
||||
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: txinfo.height
|
||||
|
||||
GridLayout {
|
||||
id: txinfo
|
||||
columns: 3
|
||||
|
||||
x: constants.paddingSmall
|
||||
width: delegate.width - 2*constants.paddingSmall
|
||||
|
||||
Item { Layout.columnSpan: 3; Layout.preferredWidth: 1; Layout.preferredHeight: 1}
|
||||
Image {
|
||||
readonly property variant tx_icons : [
|
||||
"../../../gui/icons/unconfirmed.png",
|
||||
"../../../gui/icons/clock1.png",
|
||||
"../../../gui/icons/clock2.png",
|
||||
"../../../gui/icons/clock3.png",
|
||||
"../../../gui/icons/clock4.png",
|
||||
"../../../gui/icons/clock5.png",
|
||||
"../../../gui/icons/confirmed.png"
|
||||
]
|
||||
|
||||
Layout.preferredWidth: constants.iconSizeLarge
|
||||
Layout.preferredHeight: constants.iconSizeLarge
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.rowSpan: 2
|
||||
source: tx_icons[Math.min(6,model.confirmations)]
|
||||
}
|
||||
|
||||
Label {
|
||||
font.pixelSize: constants.fontSizeLarge
|
||||
Layout.fillWidth: true
|
||||
text: model.label !== '' ? model.label : '<no label>'
|
||||
color: model.label !== '' ? Material.accentColor : 'gray'
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
Label {
|
||||
id: valueLabel
|
||||
font.family: FixedFont
|
||||
font.pixelSize: constants.fontSizeMedium
|
||||
text: Config.formatSats(model.bc_value)
|
||||
font.bold: true
|
||||
color: model.incoming ? constants.colorCredit : constants.colorDebit
|
||||
}
|
||||
Label {
|
||||
font.pixelSize: constants.fontSizeSmall
|
||||
text: model.date
|
||||
}
|
||||
Label {
|
||||
font.pixelSize: constants.fontSizeXSmall
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: model.fee !== undefined ? 'fee: ' + model.fee : ''
|
||||
}
|
||||
Item { Layout.columnSpan: 3; Layout.preferredWidth: 1; Layout.preferredHeight: 1 }
|
||||
// as the items in the model are not bindings to QObjects,
|
||||
// hook up events that might change the appearance
|
||||
Connections {
|
||||
target: Config
|
||||
function onBaseUnitChanged() {
|
||||
valueLabel.text = Config.formatSats(model.bc_value)
|
||||
}
|
||||
function onThousandsSeparatorChanged() {
|
||||
valueLabel.text = Config.formatSats(model.bc_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
// as the items in the model are not bindings to QObjects,
|
||||
// hook up events that might change the appearance
|
||||
Connections {
|
||||
target: Config
|
||||
function onBaseUnitChanged() {
|
||||
valueLabel.text = Config.formatSats(model.bc_value)
|
||||
}
|
||||
function onThousandsSeparatorChanged() {
|
||||
valueLabel.text = Config.formatSats(model.bc_value)
|
||||
}
|
||||
}
|
||||
|
||||
} // delegate
|
||||
Component.onCompleted: {
|
||||
if (model.section == 'today') {
|
||||
delegate.DelegateModel.inToday = true
|
||||
} else if (model.section == 'yesterday') {
|
||||
delegate.DelegateModel.inYesterday = true
|
||||
} else if (model.section == 'lastweek') {
|
||||
delegate.DelegateModel.inLastweek = true
|
||||
} else if (model.section == 'lastmonth') {
|
||||
delegate.DelegateModel.inLastmonth = true
|
||||
} else if (model.section == 'older') {
|
||||
delegate.DelegateModel.inOlder = true
|
||||
}
|
||||
}
|
||||
|
||||
} // delegate
|
||||
}
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
|
||||
from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
|
||||
@@ -17,7 +17,7 @@ class QETransactionListModel(QAbstractListModel):
|
||||
# define listmodel rolemap
|
||||
_ROLE_NAMES=('txid','fee_sat','height','confirmations','timestamp','monotonic_timestamp',
|
||||
'incoming','bc_value','bc_balance','date','label','txpos_in_block','fee',
|
||||
'inputs','outputs')
|
||||
'inputs','outputs','section')
|
||||
_ROLE_KEYS = range(Qt.UserRole + 1, Qt.UserRole + 1 + len(_ROLE_NAMES))
|
||||
_ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES]))
|
||||
_ROLE_RMAP = dict(zip(_ROLE_NAMES, _ROLE_KEYS))
|
||||
@@ -43,14 +43,38 @@ class QETransactionListModel(QAbstractListModel):
|
||||
self.tx_history = []
|
||||
self.endResetModel()
|
||||
|
||||
def tx_to_model(self, tx):
|
||||
item = tx
|
||||
for output in item['outputs']:
|
||||
output['value'] = output['value'].value
|
||||
|
||||
# newly arriving txs have no (block) timestamp
|
||||
# TODO?
|
||||
if not item['timestamp']:
|
||||
item['timestamp'] = datetime.timestamp(datetime.now())
|
||||
|
||||
txts = datetime.fromtimestamp(item['timestamp'])
|
||||
today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
if (txts > today):
|
||||
item['section'] = 'today'
|
||||
elif (txts > today - timedelta(days=1)):
|
||||
item['section'] = 'yesterday'
|
||||
elif (txts > today - timedelta(days=7)):
|
||||
item['section'] = 'lastweek'
|
||||
elif (txts > today - timedelta(days=31)):
|
||||
item['section'] = 'lastmonth'
|
||||
else:
|
||||
item['section'] = 'older'
|
||||
|
||||
return item
|
||||
|
||||
# initial model data
|
||||
def init_model(self):
|
||||
history = self.wallet.get_detailed_history(show_addresses = True)
|
||||
txs = history['transactions']
|
||||
# use primitives
|
||||
for tx in txs:
|
||||
for output in tx['outputs']:
|
||||
output['value'] = output['value'].value
|
||||
txs = []
|
||||
for tx in history['transactions']:
|
||||
txs.append(self.tx_to_model(tx))
|
||||
|
||||
self.clear()
|
||||
self.beginInsertRows(QModelIndex(), 0, len(txs) - 1)
|
||||
|
||||
Reference in New Issue
Block a user