From 83153eff50cbf10afb99697b8469d9f976b45489 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 28 Nov 2019 20:49:52 -0500 Subject: [PATCH 1/9] [explorer] refs #248 - Add modelExplorer.go. Include initial implementation of backend model for explorer view as a list of blocks --- src/models/modelExplorer.go | 85 +++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/models/modelExplorer.go diff --git a/src/models/modelExplorer.go b/src/models/modelExplorer.go new file mode 100644 index 00000000..e6e57758 --- /dev/null +++ b/src/models/modelExplorer.go @@ -0,0 +1,85 @@ +package models + +import ( + "github.com/fibercrypto/FiberCryptoWallet/src/util/logging" + "github.com/therecipe/qt/core" + qtcore "github.com/therecipe/qt/core" +) + +// Properties: + +// For blocks: +// - Time +// - Block number +// - Transactions (number of Transactions) +// - Blockhash + +// For block's details: +// - Height +// - Timestamp +// - Size +// - Hash +// - Parent Hash +// - Total Amount + +// - Next block +// - Previous block + +// For Transactions: +// - Inputs: +// * Coins +// * Initial hours +// * Final hours + +// - Outputs: +// * Coins +// * Hours + +// - Transaction fee (in hours) + +// - Links for the transactions + +var logExplorer = logging.MustGetLogger("modelsExplorer") + +const ( + QBlocks = int(qtcore.Qt__UserRole) + iota + 1 +) + +// ModelBlocks List of Blocks to be show. +type ModelBlocks struct { + qtcore.QObject + _ func() `constructor:"init"` + + _ map[int]*core.QByteArray `property:"roles"` + _ []*QBlock `property:"outputs"` + + _ func([]*QBlock) `slot:"addBlocks"` + _ func([]*QBlock) `slot:"insertBlocks"` + _ func([]*QBlock) `slot:"loadModel"` + _ func() `slot:"cleanModel"` +} + +// QBlock Contains info about the block to be show. +type QBlock struct { + core.QObject + + _ string `property:"time"` + _ string `property:"blockNumber"` + _ string `property:"Transactions"` + _ string `property:"Blockhash"` +} + +func (m *ModelBlocks) init() { + m.SetRoles(map[int]*core.QByteArray{ + QBlocks: core.NewQByteArray2("qblocks", -1), + }) + + m.ConnectRowCount(m.rowCount) + m.ConnectRoleNames(m.roleNames) + m.ConnectData(m.data) + m.ConnectAddBlocks(m.addBlocks) + m.ConnectInsertBlocks(m.insertBlocks) + m.ConnectLoadModel(m.loadModel) + m.ConnectCleanModel(m.cleanModel) + +} From 5c6616f0c9afc6ba56b67d6a99d9762f4ee9ac4a Mon Sep 17 00:00:00 2001 From: stdevHsequeda Date: Wed, 25 Dec 2019 23:44:59 -0500 Subject: [PATCH 2/9] [explorer] refs 248 Added return Block.Head.BqSeq in GetHash method of SkycoinBlock type in skycoin package. Added GetTransactions () ([]Transaction,error) method to block interface in core package. Implemented GetTransaction method in SkycoinBlock type. Added GetRangeBlocks(start,end uint64) ([]core.Block,error) method to BlockchainStatus interface. Implemented GetRangeBlocks method in SkycoinBLockchain type. Added BlockInRange(start,end uint64) (*readable.Blocks,error) method to SkycoinAPI interface. Added GetSkycoinTransactionByTxId(txId string ) (core.Transaction,error) to coin.go file in skycoin package. Created models/explorer/blockModel.go and models/explorer/explorerManager.go. Created ui/ExplorerPage.qml and ui/Delegates/BlockListDelegate.qml files. Added explorer window to UI. Implemented Block list in explorer interface in the UI. --- src/coin/skycoin/models/blockchain.go | 51 +++++++++++-- src/coin/skycoin/models/coin.go | 22 ++++++ src/coin/skycoin/skytypes/api.go | 2 + src/core/blockchain.go | 2 + src/core/coin.go | 2 + src/models/explorer/blocksModel.go | 37 +++++++++ src/models/explorer/explorerManager.go | 1 + src/ui/CustomMenuBar.qml | 10 +++ src/ui/Delegates/BlockListDelegate.qml | 80 +++++++++++++++++++ src/ui/ExplorerPage.qml | 102 +++++++++++++++++++++++++ src/ui/GeneralStackView.qml | 16 ++++ src/ui/main.qml | 17 +++++ 12 files changed, 336 insertions(+), 6 deletions(-) create mode 100644 src/models/explorer/blocksModel.go create mode 100644 src/models/explorer/explorerManager.go create mode 100644 src/ui/Delegates/BlockListDelegate.qml create mode 100644 src/ui/ExplorerPage.qml diff --git a/src/coin/skycoin/models/blockchain.go b/src/coin/skycoin/models/blockchain.go index ead24274..e23073f8 100644 --- a/src/coin/skycoin/models/blockchain.go +++ b/src/coin/skycoin/models/blockchain.go @@ -14,7 +14,7 @@ import ( var logBlockchain = logging.MustGetLogger("Skycoin Blockchain") -type SkycoinBlock struct { //implements core.Block interface +type SkycoinBlock struct { // implements core.Block interface Block *readable.Block } @@ -54,7 +54,7 @@ func (sb *SkycoinBlock) GetHeight() (uint64, error) { if sb.Block == nil { return 0, errors.ErrBlockNotSet } - return 0, nil //TODO ??? + return sb.Block.Head.BkSeq, nil } func (sb *SkycoinBlock) GetFee(ticker string) (uint64, error) { @@ -76,6 +76,25 @@ func (sb *SkycoinBlock) IsGenesisBlock() (bool, error) { return true, nil } +// GetTransactions return the transaction list of current block. +func (sb *SkycoinBlock) GetTransactions() ([]core.Transaction, error) { + logBlockchain.Info("Getting if is Genesis block") + if sb.Block == nil { + return nil, errors.ErrBlockNotSet + } + var txnsList []core.Transaction + for e := range sb.Block.Body.Transactions { + tx, err := GetSkycoinTransactionByTxId(sb.Block.Body.Transactions[e].Hash) + if err != nil { + logBlockchain.Error(err) + return nil, err + } + txnsList = append(txnsList, tx) + } + + return txnsList, nil +} + type SkycoinBlockchainInfo struct { LastBlockInfo *SkycoinBlock CurrentSkySupply uint64 @@ -85,8 +104,8 @@ type SkycoinBlockchainInfo struct { NumberOfBlocks *readable.BlockchainProgress } -type SkycoinBlockchain struct { //Implements BlockchainStatus interface - lastTimeStatusRequested uint64 //nolint structcheck TODO: Not used +type SkycoinBlockchain struct { // Implements BlockchainStatus interface + lastTimeStatusRequested uint64 // nolint structcheck TODO: Not used lastTimeSupplyRequested uint64 CacheTime uint64 cachedStatus *SkycoinBlockchainInfo @@ -119,7 +138,7 @@ func (ss *SkycoinBlockchain) GetCoinValue(coinvalue core.CoinValueMetric, ticker } return ss.cachedStatus.TotalCoinHourSupply, nil default: - return 0, errorTickerInvalid{} //TODO: Customize error + return 0, errorTickerInvalid{} // TODO: Customize error } } @@ -152,6 +171,27 @@ func (ss *SkycoinBlockchain) GetNumberOfBlocks() (uint64, error) { return ss.cachedStatus.NumberOfBlocks.Current, nil } +// GetRangeBlocks return a list of blocks between start and end range. +func (ss *SkycoinBlockchain) GetRangeBlocks(start, end uint64) ([]core.Block, error) { + logBlockchain.Info("Getting all blocks") + c, err := NewSkycoinApiClient(PoolSection) + if err != nil { + logBlockchain.Error(err) + return nil, err + } + blocks, err := c.BlocksInRange(start, end) + if err != nil { + logBlockchain.Error(err) + return nil, err + } + var skyBlocks []core.Block + for e := range blocks.Blocks { + skyBlocks = append(skyBlocks, &SkycoinBlock{Block: &blocks.Blocks[e]}) + } + + return skyBlocks, nil +} + func (ss *SkycoinBlockchain) SetCacheTime(time uint64) { logBlockchain.Info("Setting cache time") ss.CacheTime = time @@ -226,7 +266,6 @@ func (ss *SkycoinBlockchain) requestStatusInfo() error { } lastBlock := blocks.Blocks[len(blocks.Blocks)-1] ss.cachedStatus.LastBlockInfo = &SkycoinBlock{Block: &lastBlock} - progress, err := c.BlockchainProgress() if err != nil { return err diff --git a/src/coin/skycoin/models/coin.go b/src/coin/skycoin/models/coin.go index 5394b125..cc6403da 100644 --- a/src/coin/skycoin/models/coin.go +++ b/src/coin/skycoin/models/coin.go @@ -246,6 +246,28 @@ func (it *SkycoinTransactionIterator) HasNext() bool { return (it.Current + 1) < len(it.Transactions) } +func GetSkycoinTransactionByTxId(txId string) (core.Transaction, error) { + logCoin.Info("Getting a transaction by its transaction id") + c, err := NewSkycoinApiClient(PoolSection) + if err != nil { + logCoin.Error(err) + return nil, err + } + txVerb, err := c.TransactionVerbose(txId) + + if err != nil { + logCoin.Error(err) + return nil, err + } + + return &SkycoinTransaction{ + skyTxn: txVerb.Transaction, + status: 0, + inputs: nil, + outputs: nil, + }, nil +} + func NewSkycoinTransactionIterator(transactions []core.Transaction) *SkycoinTransactionIterator { return &SkycoinTransactionIterator{Transactions: transactions, Current: -1} } diff --git a/src/coin/skycoin/skytypes/api.go b/src/coin/skycoin/skytypes/api.go index fc99638b..0bc6a8ec 100644 --- a/src/coin/skycoin/skytypes/api.go +++ b/src/coin/skycoin/skytypes/api.go @@ -22,6 +22,8 @@ type SkycoinAPI interface { PendingTransactionsVerbose() ([]readable.UnconfirmedTransactionVerbose, error) // CoinSupply Determine coin supply CoinSupply() (*api.CoinSupply, error) + // BlocksInRange return a block between start and end range. + BlocksInRange(start, end uint64) (*readable.Blocks, error) // LastBlocks Get last N blocks LastBlocks(n uint64) (*readable.Blocks, error) // BlockchainProgress Get blockchain progress diff --git a/src/core/blockchain.go b/src/core/blockchain.go index 952e9ba7..3a1ff1a4 100644 --- a/src/core/blockchain.go +++ b/src/core/blockchain.go @@ -18,6 +18,8 @@ type BlockchainStatus interface { GetLastBlock() (Block, error) // GetNumberOfBlocks determine number of blocks in the blockchain GetNumberOfBlocks() (uint64, error) + + GetRangeBlocks(start, end uint64) ([]Block, error) } // BlockchainAPI abstract interface for transactions management and utility functions for specific blockchain. diff --git a/src/core/coin.go b/src/core/coin.go index ba03a961..f5e4f181 100644 --- a/src/core/coin.go +++ b/src/core/coin.go @@ -112,4 +112,6 @@ type Block interface { GetFee(ticker string) (uint64, error) // IsGenesisBlock determines whether this block starts blockchain sequence IsGenesisBlock() (bool, error) + + GetTransactions() ([]Transaction, error) } diff --git a/src/models/explorer/blocksModel.go b/src/models/explorer/blocksModel.go new file mode 100644 index 00000000..cccd73e0 --- /dev/null +++ b/src/models/explorer/blocksModel.go @@ -0,0 +1,37 @@ +package explorer + +import ( + "github.com/fibercrypto/fibercryptowallet/src/util/logging" + qtcore "github.com/therecipe/qt/core" +) + +var logExplorer = logging.MustGetLogger("Explorer") + +type BlocksModel struct { + qtcore.QAbstractListModel + _ int `property:"currentPage"` + _ func() `constructor:"init"` + _ func(pageNum int) `signal:"loadPage"` + _ func() `signal:"update,auto"` +} + +func (blocksM *BlocksModel) init() { +} + +func (blocksM *BlocksModel) update() { + // update info + if err := blocksM.updateInfo(); err != nil { + logExplorer.WithError(err).Warn("Couldn't update blockchain Info") + return + } + return +} + +// updateInfo request the needed information +func (blocksM *BlocksModel) updateInfo() error { + return nil +} + +func (blocksM *BlocksModel) loadPage(pageNum int) { + +} diff --git a/src/models/explorer/explorerManager.go b/src/models/explorer/explorerManager.go new file mode 100644 index 00000000..6128bbaa --- /dev/null +++ b/src/models/explorer/explorerManager.go @@ -0,0 +1 @@ +package explorer diff --git a/src/ui/CustomMenuBar.qml b/src/ui/CustomMenuBar.qml index 127d3d4c..1696a372 100755 --- a/src/ui/CustomMenuBar.qml +++ b/src/ui/CustomMenuBar.qml @@ -16,11 +16,13 @@ RowLayout { property alias enableBlockchain: menuItemBlockchain.enabled property alias enableNetworking: menuItemNetworking.enabled property alias enableSettings: menuItemSettings.enabled + property alias enableExplorer: menuItemExplorer.enabled // Signals signal outputsRequested() signal pendingTransactionsRequested() signal networkingRequested() + signal explorerRequested() signal settingsRequested() signal blockchainRequested() signal aboutRequested() @@ -70,6 +72,7 @@ RowLayout { enablePendingTransactions = true enableBlockchain = true enableNetworking = true + enableExplorer= true enableSettings = true } } @@ -128,6 +131,13 @@ RowLayout { onClicked: networkingRequested() } + CustomMenuItem { + id: menuItemExplorer + text: qsTr("&Explorer") + iconSource: "qrc:/images/resources/images/icons/networking.svg" + + onClicked: explorerRequested() + } CustomMenuItem { id: menuItemSettings text: qsTr("&Settings") diff --git a/src/ui/Delegates/BlockListDelegate.qml b/src/ui/Delegates/BlockListDelegate.qml new file mode 100644 index 00000000..52329d96 --- /dev/null +++ b/src/ui/Delegates/BlockListDelegate.qml @@ -0,0 +1,80 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 +import QtGraphicalEffects 1.12 + +// Resource imports +// import "qrc:/ui/src/ui/Dialogs" +import "../Dialogs/" // For quick UI development, switch back to resources when making a release + +Item { + id: root + + readonly property real delegateHeight: 30 + property bool emptyAddressVisible: true + property bool expanded: expand + // The following property is used to avoid a binding conflict with the `height` property. + // Also avoids a bug with the animation when collapsing a wallet + readonly property real finalViewHeight: expanded ? delegateHeight*(addressList.count) + 50 : 0 + + + width: blocksList.width + height: itemDelegateMainButton.height + (expanded ? finalViewHeight : 0) + + Behavior on height { NumberAnimation { duration: 250; easing.type: Easing.OutQuint } } + + ColumnLayout { + id: delegateColumnLayout + anchors.fill: parent + + ItemDelegate { + id: itemDelegateMainButton + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + font.bold: expanded + + RowLayout { + id: delegateRowLayout + anchors.fill: parent + anchors.leftMargin: listBlockLeftMargin + anchors.rightMargin: listBlockRightMargin + spacing: listBlockSpacing + + Label { + id: labelBlockTime + text: date // a role of the model + Layout.leftMargin: listBlocksLeftMargin + Layout.fillWidth: true + } + + Label { + id: labelBlockNumber + text: blockNumber // a role of the model + color: Material.accent + horizontalAlignment: Text.AlignLeft + Layout.preferredWidth: internalLabelsWidth + } + + Label { + id: labelTransactions + text: txNumber // a role of the model + horizontalAlignment: Text.AlignRight + Layout.rightMargin: listBlocksRightMargin + Layout.preferredWidth: internalLabelsWidth + } + + Label { + id: labelHash + text: hash // a role of the model + color: Material.accent + horizontalAlignment: Text.AlignRight + Layout.rightMargin: listBlocksRightMargin + visible:parent.width>750 + Layout.fillWidth: true + + } + } // RowLayout + } // ItemDelegate + } +} \ No newline at end of file diff --git a/src/ui/ExplorerPage.qml b/src/ui/ExplorerPage.qml new file mode 100644 index 00000000..5755ce0a --- /dev/null +++ b/src/ui/ExplorerPage.qml @@ -0,0 +1,102 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 + +import "Delegates/" // For quick UI development, switch back to resources when making a release + +Page { + id: explorer + readonly property real listBlocksLeftMargin: 20 + readonly property real listBlocksRightMargin: 50 + readonly property real listBlocksSpacing: 20 + readonly property real internalLabelsWidth: 70 + header: ColumnLayout { + + RowLayout { + spacing: listBlocksSpacing + Layout.topMargin: 30 + + Label { + text: qsTr("Time") + font.pointSize: 9 + Layout.leftMargin: listBlocksLeftMargin + Layout.fillWidth: true + } + Label { + text: qsTr("Block Number") + font.pointSize: 9 + horizontalAlignment: Text.AlignLeft + Layout.preferredWidth: internalLabelsWidth + } + Label { + text: qsTr("Transactions") + font.pointSize: 9 + horizontalAlignment: Text.AlignRight + Layout.rightMargin: listBlocksRightMargin + Layout.preferredWidth: internalLabelsWidth + } + Label { + text: qsTr(" Hash") + font.pointSize: 9 + visible:parent.width>750 + horizontalAlignment: Text.AlignRight + Layout.rightMargin: listBlocksRightMargin + Layout.fillWidth: true + } + + } // RowLayout + + Rectangle { + id: rect + Layout.fillWidth: true + height: 1 + color: "#DDDDDD" + } + } // ColumnLayout (header) + + +ScrollView { + id: scrollItem + + anchors.fill: parent + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + + ListView { + id: blocksList + anchors.fill: parent + clip: true // limit the painting to it's bounding rectangle + model: blockModel + delegate: BlockListDelegate{} + } + } +ListModel{ +id:blockModel +ListElement{ +date:"01/02/1990" +blockNumber:10000898 +txNumber:2 +hash:"9516639399ab2b02f6f0bc873f03f2f2ac1c94853dd031685de1021be78c71d7" +} +ListElement{ +date:"01/02/1990" +blockNumber:1 +txNumber:2 +hash:"9516639399ab2b02f6f0bc873f03f2f2ac1c94853dd031685de1021be78c71d7" + +} +ListElement{ +date:"01/02/1990" +blockNumber:2 +txNumber:1 +hash:"5ac7b5f4d170e606cc75e08c20b3ec216703e9ca77df178ea0130255bf20d5e5" +} +ListElement{ +date:"01/02/1990" +blockNumber:3 +txNumber:1 +hash:"5ac7b5f4d170e606cc75e08c20b3ec216703e9ca77df178ea0130255bf20d5e5" +} +}//listModel + +} diff --git a/src/ui/GeneralStackView.qml b/src/ui/GeneralStackView.qml index 66a605a3..59afed89 100644 --- a/src/ui/GeneralStackView.qml +++ b/src/ui/GeneralStackView.qml @@ -72,6 +72,14 @@ Item { } } + function openExplorerPage() { + if (stackView.depth > 1) { + stackView.replace(componentExplorer) + } else { + stackView.push(componentExplorer) + } + } + function openSettingsPage() { if (stackView.depth > 1) { stackView.replace(componentSettings) @@ -158,6 +166,14 @@ Item { } } + Component { + id: componentExplorer + + ExplorerPage { + id: explorer + } + } + Component { id: componentSettings diff --git a/src/ui/main.qml b/src/ui/main.qml index 8501f660..4eefc64a 100644 --- a/src/ui/main.qml +++ b/src/ui/main.qml @@ -44,6 +44,7 @@ ApplicationWindow { enableOutputs = false enablePendingTransactions = true enableBlockchain = true + enableExplorer = true enableNetworking = true enableSettings = true } @@ -59,6 +60,7 @@ ApplicationWindow { enableOutputs = true enablePendingTransactions = false enableBlockchain = true + enableExplorer = true enableNetworking = true enableSettings = true @@ -71,10 +73,23 @@ ApplicationWindow { enableOutputs = true enablePendingTransactions = true enableBlockchain = false + enableExplorer = true enableNetworking = true enableSettings = true } + onExplorerRequested: { + generalStackView.openExplorerPage() + customHeader.text = qsTr("Explorer") + + enableOutputs = true + enablePendingTransactions = true + enableBlockchain = true + enableNetworking = true + enableExplorer = false + enableSettings = true + } + onNetworkingRequested: { generalStackView.openNetworkingPage() customHeader.text = qsTr("Networking") @@ -82,6 +97,7 @@ ApplicationWindow { enableOutputs = true enablePendingTransactions = true enableBlockchain = true + enableExplorer = true enableNetworking = false enableSettings = true } @@ -93,6 +109,7 @@ ApplicationWindow { enableOutputs = true enablePendingTransactions = true enableBlockchain = true + enableExplorer = true enableNetworking = true enableSettings = false } From 963843794d4f5efbc3d9a5839b9945edb137ee11 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 26 Dec 2019 17:35:18 -0500 Subject: [PATCH 3/9] Auto stash before merge of "stdevAntiD2ta_t248_Blockchain_explorer_for_Skycoin" and "stdevHsequeda_t248_Explorer" --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0bab568b..805ca021 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ QTFILES := $(shell echo "$(QRCFILES) $(TSFILES) $(PLISTFILES) $(QTCONFFILE RESOURCEFILES := $(shell echo "$(SVGFILES) $(PNGFILES) $(OTFFILES) $(ICNSFILES) $(ICOFILES) $(RCFILES)") SRCFILES := $(shell echo "$(QTFILES) $(RESOURCEFILES) $(GOFILES)") -BINPATH_Linux := deploy/linux/FiberCryptoWallet +BINPATH_Linux := deploy/linux/fibercryptowallet BINPATH_Windows_NT := deploy/windows/FiberCryptoWallet.exe BINPATH_Darwin := deploy/darwin/fibercryptowallet.app/Contents/MacOS/fibercryptowallet BINPATH := $(BINPATH_$(UNAME_S)) From 8b281c2959d295893136c8e7e61a8608bdb5190d Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 26 Dec 2019 18:32:17 -0500 Subject: [PATCH 4/9] [explorer] refs #248 - Update BlocksModel Changes: - Add QBlock QObject that contains the block's data - Implement init - Add rowCount and roleNames methods - Implement data --- src/models/explorer/blocksModel.go | 129 ++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 4 deletions(-) diff --git a/src/models/explorer/blocksModel.go b/src/models/explorer/blocksModel.go index cccd73e0..fa58350a 100644 --- a/src/models/explorer/blocksModel.go +++ b/src/models/explorer/blocksModel.go @@ -5,17 +5,89 @@ import ( qtcore "github.com/therecipe/qt/core" ) +// Properties: + +// For blocks: +// - Time +// - Block number +// - Transactions (number of Transactions) +// - Blockhash + +// For block's details: +// - Height +// - Timestamp +// - Size +// - Hash +// - Parent Hash +// - Total Amount + +// - Next block +// - Previous block + +// For Transactions: +// - Inputs: +// * Coins +// * Initial hours +// * Final hours + +// - Outputs: +// * Coins +// * Hours + +// - Transaction fee (in hours) + +// - Links for the transactions + var logExplorer = logging.MustGetLogger("Explorer") +const ( + Time = int(qtcore.Qt__UserRole) + iota + 1 + BlockNumber + Transactions + Blockhash +) + +// BlocksModel List of Blocks to be show. type BlocksModel struct { qtcore.QAbstractListModel - _ int `property:"currentPage"` - _ func() `constructor:"init"` - _ func(pageNum int) `signal:"loadPage"` - _ func() `signal:"update,auto"` + + _ func() `constructor:"init"` + + _ int `property:"currentPage"` + _ map[int]*core.QByteArray `property:"roles"` + _ []*QBlock `property:"blocks"` + + _ func(pageNum int) `signal:"loadPage"` + _ func() `signal:"update,auto"` + + _ func([]*QBlock) `slot:"addBlocks"` + _ func([]*QBlock) `slot:"loadModel"` +} + +// QBlock Contains info about the block to be show. +type QBlock struct { + core.QObject + + _ string `property:"time"` + _ string `property:"blockNumber"` + _ string `property:"Transactions"` + _ string `property:"Blockhash"` } func (blocksM *BlocksModel) init() { + m.SetRoles(map[int]*core.QByteArray{ + QBlocks: core.NewQByteArray2("qblocks", -1), + }) + + m.ConnectRowCount(m.rowCount) + m.ConnectRoleNames(m.roleNames) + m.ConnectData(m.data) + m.ConnectAddBlocks(m.addBlocks) + m.ConnectInsertBlocks(m.insertBlocks) + m.ConnectLoadModel(m.loadModel) + m.ConnectCleanModel(m.cleanModel) + + m.loadModel() } func (blocksM *BlocksModel) update() { @@ -35,3 +107,52 @@ func (blocksM *BlocksModel) updateInfo() error { func (blocksM *BlocksModel) loadPage(pageNum int) { } + +func (m *BlocksModel) rowCount(*core.QModelIndex) int { + return len(m.Outputs()) +} + +func (m *BlocksModel) roleNames() map[int]*core.QByteArray { + return m.Roles() +} + +func (m *BlocksModel) data(index *core.QModelIndex, role int) *core.QVariant { + if !index.IsValid() || index.Row() >= len(m.Blocks()) { + return core.NewQVariant() + } + + qb := m.Blocks()[index.Row()] + + switch role { + case Time: + { + return core.NewQVariant1(qb.Time()) + } + case BlockNumber: + { + return core.NewQVariant1(qb.BlockNumber()) + } + case Transactions: + { + return core.NewQVariant1(qb.Transactions()) + } + case Blockhash: + { + return core.NewQVariant1(qb.Blockhash()) + } + default: + { + return core.NewQVariant() + } + } +} + +func (m *BlocksModel) addBlocks(qb []*QBlock) { + m.SetBlocks(qb) + m.insertRows(len(m.Blocks()), len(qb)) +} + +func (m *BlocksModel) loadModel() { + //m.SetOutputs(mo) +} + From bce016f99e1b75b1cb18527a5c17a99c7d4d9922 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Thu, 26 Dec 2019 18:32:52 -0500 Subject: [PATCH 5/9] [explorer] refs #248 - Delete modelExplorer.go --- src/models/modelExplorer.go | 85 ------------------------------------- 1 file changed, 85 deletions(-) delete mode 100644 src/models/modelExplorer.go diff --git a/src/models/modelExplorer.go b/src/models/modelExplorer.go deleted file mode 100644 index e6e57758..00000000 --- a/src/models/modelExplorer.go +++ /dev/null @@ -1,85 +0,0 @@ -package models - -import ( - "github.com/fibercrypto/FiberCryptoWallet/src/util/logging" - "github.com/therecipe/qt/core" - qtcore "github.com/therecipe/qt/core" -) - -// Properties: - -// For blocks: -// - Time -// - Block number -// - Transactions (number of Transactions) -// - Blockhash - -// For block's details: -// - Height -// - Timestamp -// - Size -// - Hash -// - Parent Hash -// - Total Amount - -// - Next block -// - Previous block - -// For Transactions: -// - Inputs: -// * Coins -// * Initial hours -// * Final hours - -// - Outputs: -// * Coins -// * Hours - -// - Transaction fee (in hours) - -// - Links for the transactions - -var logExplorer = logging.MustGetLogger("modelsExplorer") - -const ( - QBlocks = int(qtcore.Qt__UserRole) + iota + 1 -) - -// ModelBlocks List of Blocks to be show. -type ModelBlocks struct { - qtcore.QObject - _ func() `constructor:"init"` - - _ map[int]*core.QByteArray `property:"roles"` - _ []*QBlock `property:"outputs"` - - _ func([]*QBlock) `slot:"addBlocks"` - _ func([]*QBlock) `slot:"insertBlocks"` - _ func([]*QBlock) `slot:"loadModel"` - _ func() `slot:"cleanModel"` -} - -// QBlock Contains info about the block to be show. -type QBlock struct { - core.QObject - - _ string `property:"time"` - _ string `property:"blockNumber"` - _ string `property:"Transactions"` - _ string `property:"Blockhash"` -} - -func (m *ModelBlocks) init() { - m.SetRoles(map[int]*core.QByteArray{ - QBlocks: core.NewQByteArray2("qblocks", -1), - }) - - m.ConnectRowCount(m.rowCount) - m.ConnectRoleNames(m.roleNames) - m.ConnectData(m.data) - m.ConnectAddBlocks(m.addBlocks) - m.ConnectInsertBlocks(m.insertBlocks) - m.ConnectLoadModel(m.loadModel) - m.ConnectCleanModel(m.cleanModel) - -} From 5a11ce7f772578b6a5c9d40c35dd35d86eae6ba7 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Fri, 27 Dec 2019 02:07:30 -0500 Subject: [PATCH 6/9] [explorer] refs #248 - Install models for explorer --- main.go | 1 + src/models/explorer/blocksModel.go | 56 ++++++++++++++----------- src/models/explorer/explorerManager.go | 4 ++ src/models/models.go | 1 - src/ui/ExplorerPage.qml | 58 ++++++++++++++------------ 5 files changed, 68 insertions(+), 52 deletions(-) diff --git a/main.go b/main.go index e2958531..6b5de26b 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( _ "github.com/fibercrypto/fibercryptowallet/src/models" _ "github.com/fibercrypto/fibercryptowallet/src/models/history" _ "github.com/fibercrypto/fibercryptowallet/src/models/pending" + _ "github.com/fibercrypto/fibercryptowallet/src/models/explorer" "github.com/therecipe/qt/core" "github.com/therecipe/qt/gui" "github.com/therecipe/qt/qml" diff --git a/src/models/explorer/blocksModel.go b/src/models/explorer/blocksModel.go index fa58350a..550d9391 100644 --- a/src/models/explorer/blocksModel.go +++ b/src/models/explorer/blocksModel.go @@ -51,22 +51,22 @@ const ( type BlocksModel struct { qtcore.QAbstractListModel - _ func() `constructor:"init"` + _ func() `constructor:"init"` - _ int `property:"currentPage"` - _ map[int]*core.QByteArray `property:"roles"` - _ []*QBlock `property:"blocks"` + _ int `property:"currentPage"` + _ map[int]*qtcore.QByteArray `property:"roles"` + _ []*QBlock `property:"blocks"` - _ func(pageNum int) `signal:"loadPage"` - _ func() `signal:"update,auto"` + _ func(pageNum int) `signal:"loadPage"` + _ func() `signal:"update,auto"` - _ func([]*QBlock) `slot:"addBlocks"` - _ func([]*QBlock) `slot:"loadModel"` + _ func([]*QBlock) `slot:"addBlocks"` + _ func() `slot:"loadModel"` } // QBlock Contains info about the block to be show. type QBlock struct { - core.QObject + qtcore.QObject _ string `property:"time"` _ string `property:"blockNumber"` @@ -74,18 +74,19 @@ type QBlock struct { _ string `property:"Blockhash"` } -func (blocksM *BlocksModel) init() { - m.SetRoles(map[int]*core.QByteArray{ - QBlocks: core.NewQByteArray2("qblocks", -1), +func (m *BlocksModel) init() { + m.SetRoles(map[int]*qtcore.QByteArray{ + Time: qtcore.NewQByteArray2("time", -1), + BlockNumber: qtcore.NewQByteArray2("blockNumber", -1), + Transactions: qtcore.NewQByteArray2("transactions", -1), + Blockhash: qtcore.NewQByteArray2("blockhash", -1), }) m.ConnectRowCount(m.rowCount) m.ConnectRoleNames(m.roleNames) m.ConnectData(m.data) m.ConnectAddBlocks(m.addBlocks) - m.ConnectInsertBlocks(m.insertBlocks) m.ConnectLoadModel(m.loadModel) - m.ConnectCleanModel(m.cleanModel) m.loadModel() } @@ -108,17 +109,17 @@ func (blocksM *BlocksModel) loadPage(pageNum int) { } -func (m *BlocksModel) rowCount(*core.QModelIndex) int { - return len(m.Outputs()) +func (m *BlocksModel) rowCount(*qtcore.QModelIndex) int { + return len(m.Blocks()) } -func (m *BlocksModel) roleNames() map[int]*core.QByteArray { +func (m *BlocksModel) roleNames() map[int]*qtcore.QByteArray { return m.Roles() } -func (m *BlocksModel) data(index *core.QModelIndex, role int) *core.QVariant { +func (m *BlocksModel) data(index *qtcore.QModelIndex, role int) *qtcore.QVariant { if !index.IsValid() || index.Row() >= len(m.Blocks()) { - return core.NewQVariant() + return qtcore.NewQVariant() } qb := m.Blocks()[index.Row()] @@ -126,27 +127,33 @@ func (m *BlocksModel) data(index *core.QModelIndex, role int) *core.QVariant { switch role { case Time: { - return core.NewQVariant1(qb.Time()) + return qtcore.NewQVariant1(qb.Time()) } case BlockNumber: { - return core.NewQVariant1(qb.BlockNumber()) + return qtcore.NewQVariant1(qb.BlockNumber()) } case Transactions: { - return core.NewQVariant1(qb.Transactions()) + return qtcore.NewQVariant1(qb.Transactions()) } case Blockhash: { - return core.NewQVariant1(qb.Blockhash()) + return qtcore.NewQVariant1(qb.Blockhash()) } default: { - return core.NewQVariant() + return qtcore.NewQVariant() } } } +func (m *BlocksModel) insertRows(row int, count int) bool { + m.BeginInsertRows(qtcore.NewQModelIndex(), row, row+count) + m.EndInsertRows() + return true +} + func (m *BlocksModel) addBlocks(qb []*QBlock) { m.SetBlocks(qb) m.insertRows(len(m.Blocks()), len(qb)) @@ -155,4 +162,3 @@ func (m *BlocksModel) addBlocks(qb []*QBlock) { func (m *BlocksModel) loadModel() { //m.SetOutputs(mo) } - diff --git a/src/models/explorer/explorerManager.go b/src/models/explorer/explorerManager.go index 6128bbaa..559ae7ad 100644 --- a/src/models/explorer/explorerManager.go +++ b/src/models/explorer/explorerManager.go @@ -1 +1,5 @@ package explorer + +func init() { + BlocksModel_QmlRegisterType2("ExplorerModels", 1, 0, "QBlocks") +} diff --git a/src/models/models.go b/src/models/models.go index 6535bc43..a2f8449b 100644 --- a/src/models/models.go +++ b/src/models/models.go @@ -16,5 +16,4 @@ func init() { ModelOutputs_QmlRegisterType2("OutputsModels", 1, 0, "QOutputs") QTransaction_QmlRegisterType2("Transactions", 1, 0, "QTransaction") QBridge_QmlRegisterType2("Utils", 1, 0, "QBridge") - } diff --git a/src/ui/ExplorerPage.qml b/src/ui/ExplorerPage.qml index 5755ce0a..6123c0eb 100644 --- a/src/ui/ExplorerPage.qml +++ b/src/ui/ExplorerPage.qml @@ -2,6 +2,7 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Controls.Material 2.12 import QtQuick.Layouts 1.12 +import ExplorerModels 1.0 import "Delegates/" // For quick UI development, switch back to resources when making a release @@ -70,33 +71,38 @@ ScrollView { delegate: BlockListDelegate{} } } -ListModel{ -id:blockModel -ListElement{ -date:"01/02/1990" -blockNumber:10000898 -txNumber:2 -hash:"9516639399ab2b02f6f0bc873f03f2f2ac1c94853dd031685de1021be78c71d7" -} -ListElement{ -date:"01/02/1990" -blockNumber:1 -txNumber:2 -hash:"9516639399ab2b02f6f0bc873f03f2f2ac1c94853dd031685de1021be78c71d7" +QBlocks{ + id:blockModel } -ListElement{ -date:"01/02/1990" -blockNumber:2 -txNumber:1 -hash:"5ac7b5f4d170e606cc75e08c20b3ec216703e9ca77df178ea0130255bf20d5e5" -} -ListElement{ -date:"01/02/1990" -blockNumber:3 -txNumber:1 -hash:"5ac7b5f4d170e606cc75e08c20b3ec216703e9ca77df178ea0130255bf20d5e5" -} -}//listModel + +// ListModel{ +// id:blockModeld +// ListElement{ +// date:"01/02/1990" +// blockNumber:10000898 +// txNumber:2 +// hash:"9516639399ab2b02f6f0bc873f03f2f2ac1c94853dd031685de1021be78c71d7" +// } +// ListElement{ +// date:"01/02/1990" +// blockNumber:1 +// txNumber:2 +// hash:"9516639399ab2b02f6f0bc873f03f2f2ac1c94853dd031685de1021be78c71d7" + +// } +// ListElement{ +// date:"01/02/1990" +// blockNumber:2 +// txNumber:1 +// hash:"5ac7b5f4d170e606cc75e08c20b3ec216703e9ca77df178ea0130255bf20d5e5" +// } +// ListElement{ +// date:"01/02/1990" +// blockNumber:3 +// txNumber:1 +// hash:"5ac7b5f4d170e606cc75e08c20b3ec216703e9ca77df178ea0130255bf20d5e5" +// } +//}//listModel } From c9b73d647cd612caebfb26db99a1b768880f2159 Mon Sep 17 00:00:00 2001 From: stdevHsequeda Date: Sat, 28 Dec 2019 11:47:36 -0500 Subject: [PATCH 7/9] [explorer] refs 248 Added defer ReturnSkycoinClient sentence to GetSkycoinTransactionByTxId and GetRangeOfBlocks methods of SkycoinBlockchain and SkycoinBlock types in skycoin package. Completed the list of blocks with pagination in the UI. --- src/coin/skycoin/models/blockchain.go | 4 +- src/coin/skycoin/models/coin.go | 1 + src/coin/skycoin/models/network.go | 1 - src/models/explorer/blocksModel.go | 124 ++++++++++++++++++++----- src/models/explorer/explorerManager.go | 32 +++++++ src/ui/Delegates/BlockListDelegate.qml | 20 ++-- src/ui/ExplorerPage.qml | 43 ++++++++- 7 files changed, 186 insertions(+), 39 deletions(-) diff --git a/src/coin/skycoin/models/blockchain.go b/src/coin/skycoin/models/blockchain.go index e23073f8..5f046014 100644 --- a/src/coin/skycoin/models/blockchain.go +++ b/src/coin/skycoin/models/blockchain.go @@ -179,6 +179,9 @@ func (ss *SkycoinBlockchain) GetRangeBlocks(start, end uint64) ([]core.Block, er logBlockchain.Error(err) return nil, err } + + defer ReturnSkycoinClient(c) + blocks, err := c.BlocksInRange(start, end) if err != nil { logBlockchain.Error(err) @@ -188,7 +191,6 @@ func (ss *SkycoinBlockchain) GetRangeBlocks(start, end uint64) ([]core.Block, er for e := range blocks.Blocks { skyBlocks = append(skyBlocks, &SkycoinBlock{Block: &blocks.Blocks[e]}) } - return skyBlocks, nil } diff --git a/src/coin/skycoin/models/coin.go b/src/coin/skycoin/models/coin.go index cc6403da..7a14064e 100644 --- a/src/coin/skycoin/models/coin.go +++ b/src/coin/skycoin/models/coin.go @@ -253,6 +253,7 @@ func GetSkycoinTransactionByTxId(txId string) (core.Transaction, error) { logCoin.Error(err) return nil, err } + defer ReturnSkycoinClient(c) txVerb, err := c.TransactionVerbose(txId) if err != nil { diff --git a/src/coin/skycoin/models/network.go b/src/coin/skycoin/models/network.go index 2c67907c..a72f0629 100644 --- a/src/coin/skycoin/models/network.go +++ b/src/coin/skycoin/models/network.go @@ -51,7 +51,6 @@ func NewSkycoinApiClient(section string) (skytypes.SkycoinAPI, error) { } obj := pool.Get() - if err != nil { for _, ok := err.(core.NotAvailableObjectsError); ok; _, ok = err.(core.NotAvailableObjectsError) { if err == nil { diff --git a/src/models/explorer/blocksModel.go b/src/models/explorer/blocksModel.go index 550d9391..925bcd48 100644 --- a/src/models/explorer/blocksModel.go +++ b/src/models/explorer/blocksModel.go @@ -1,6 +1,10 @@ package explorer import ( + skycoin "github.com/fibercrypto/fibercryptowallet/src/coin/skycoin/models" + "github.com/fibercrypto/fibercryptowallet/src/core" + "github.com/fibercrypto/fibercryptowallet/src/params" + "github.com/fibercrypto/fibercryptowallet/src/util" "github.com/fibercrypto/fibercryptowallet/src/util/logging" qtcore "github.com/therecipe/qt/core" ) @@ -11,7 +15,7 @@ import ( // - Time // - Block number // - Transactions (number of Transactions) -// - Blockhash +// - BlockHash // For block's details: // - Height @@ -44,16 +48,18 @@ const ( Time = int(qtcore.Qt__UserRole) + iota + 1 BlockNumber Transactions - Blockhash + BlockHash ) // BlocksModel List of Blocks to be show. type BlocksModel struct { qtcore.QAbstractListModel - - _ func() `constructor:"init"` + blockChain core.BlockchainStatus + _ func() `constructor:"init"` _ int `property:"currentPage"` + _ int `property:"countPage"` + _ int `property:"blockNumber"` _ map[int]*qtcore.QByteArray `property:"roles"` _ []*QBlock `property:"blocks"` @@ -61,34 +67,37 @@ type BlocksModel struct { _ func() `signal:"update,auto"` _ func([]*QBlock) `slot:"addBlocks"` - _ func() `slot:"loadModel"` + _ func() `slot:"loadModel"` } // QBlock Contains info about the block to be show. type QBlock struct { qtcore.QObject - _ string `property:"time"` - _ string `property:"blockNumber"` - _ string `property:"Transactions"` - _ string `property:"Blockhash"` + _ qtcore.QDateTime `property:"time"` + _ uint64 `property:"blockNumber"` + _ int `property:"transactions"` + _ string `property:"blockHash"` } func (m *BlocksModel) init() { + logExplorer.Info("Init Explorer Model") m.SetRoles(map[int]*qtcore.QByteArray{ Time: qtcore.NewQByteArray2("time", -1), BlockNumber: qtcore.NewQByteArray2("blockNumber", -1), Transactions: qtcore.NewQByteArray2("transactions", -1), - Blockhash: qtcore.NewQByteArray2("blockhash", -1), + BlockHash: qtcore.NewQByteArray2("blockHash", -1), }) - m.ConnectRowCount(m.rowCount) m.ConnectRoleNames(m.roleNames) m.ConnectData(m.data) m.ConnectAddBlocks(m.addBlocks) m.ConnectLoadModel(m.loadModel) - - m.loadModel() + m.ConnectUpdate(m.update) + m.ConnectLoadPage(m.loadPage) + m.blockChain = skycoin.NewSkycoinBlockchain(params.DataRefreshTimeout) + m.update() + m.loadPage(1) } func (blocksM *BlocksModel) update() { @@ -102,11 +111,27 @@ func (blocksM *BlocksModel) update() { // updateInfo request the needed information func (blocksM *BlocksModel) updateInfo() error { + numberOfBlocks, err := blocksM.blockChain.GetNumberOfBlocks() + if err != nil { + logExplorer.WithError(err).Warn("Couldn't get the number of blocks") + return err + } + blocksM.SetCurrentPage(1) + blocksM.SetBlockNumber(int(numberOfBlocks)) + + if numberOfBlocks%10 != 0 { + blocksM.SetCountPage(int(numberOfBlocks/10) + 1) + } else { + blocksM.SetCountPage(int(numberOfBlocks / 10)) + } return nil } func (blocksM *BlocksModel) loadPage(pageNum int) { - + if pageNum > 0 && pageNum <= blocksM.CountPage() { + blocksM.SetCurrentPage(pageNum) + blocksM.loadModel() + } } func (m *BlocksModel) rowCount(*qtcore.QModelIndex) int { @@ -137,9 +162,9 @@ func (m *BlocksModel) data(index *qtcore.QModelIndex, role int) *qtcore.QVariant { return qtcore.NewQVariant1(qb.Transactions()) } - case Blockhash: + case BlockHash: { - return qtcore.NewQVariant1(qb.Blockhash()) + return qtcore.NewQVariant1(qb.BlockHash()) } default: { @@ -148,17 +173,68 @@ func (m *BlocksModel) data(index *qtcore.QModelIndex, role int) *qtcore.QVariant } } -func (m *BlocksModel) insertRows(row int, count int) bool { - m.BeginInsertRows(qtcore.NewQModelIndex(), row, row+count) - m.EndInsertRows() - return true -} - func (m *BlocksModel) addBlocks(qb []*QBlock) { + logExplorer.Info("Add Block") + m.BeginResetModel() m.SetBlocks(qb) - m.insertRows(len(m.Blocks()), len(qb)) + m.EndResetModel() + } func (m *BlocksModel) loadModel() { - //m.SetOutputs(mo) + logExplorer.Info("Loading Explorer") + + blocks, err := m.blockChain.GetRangeBlocks(uint64(m.BlockNumber()-(m.CurrentPage()*10))+1, + uint64((m.BlockNumber()-(m.CurrentPage()*10))+10)) + logExplorer.Info(len(blocks)) + if err != nil { + logExplorer.WithError(err).Error("Couldn't get the range of blocks") + } + logExplorer.Info(len(m.Blocks())) + var qBlocks []*QBlock + for e := range blocks { + h, _ := blocks[e].GetHeight() + logExplorer.Info(h) + qBlocks = append(qBlocks, blockToQBlock(blocks[e])) + } + m.addBlocks(qBlocks) +} + +func blockToQBlock(block core.Block) *QBlock { + var qBlock = NewQBlock(nil) + timestamp, err := block.GetTime() + + if err != nil { + logExplorer.WithError(err).Error("Couldn't get the time of block") + } + + year, month, day, h, m, s := util.ParseDate(int64(timestamp)) + qBlock.SetTime(qtcore.NewQDateTime3(qtcore.NewQDate3(year, month, day), + qtcore.NewQTime3(h, m, s, 0), qtcore.Qt__LocalTime)) + + hash, err := block.GetHash() + + if err != nil { + logExplorer.WithError(err).Error("Couldn't get the hash of block") + } + + qBlock.SetBlockHash(string(hash)) + + height, err := block.GetHeight() + + if err != nil { + logExplorer.WithError(err).Error("Couldn't get the height of block") + } + + qBlock.SetBlockNumber(height) + + transactions, err := block.GetTransactions() + + if err != nil { + logExplorer.Error(err) + } + + qBlock.SetTransactions(len(transactions)) + + return qBlock } diff --git a/src/models/explorer/explorerManager.go b/src/models/explorer/explorerManager.go index 559ae7ad..747294ca 100644 --- a/src/models/explorer/explorerManager.go +++ b/src/models/explorer/explorerManager.go @@ -1,5 +1,37 @@ package explorer +import ( +// skycoin "github.com/fibercrypto/fibercryptowallet/src/coin/skycoin/models" +// "github.com/fibercrypto/fibercryptowallet/src/core" +// "github.com/fibercrypto/fibercryptowallet/src/params" +// "github.com/fibercrypto/fibercryptowallet/src/util/logging" +// "github.com/fibercrypto/fibercryptowallet/src/core" +// qtCore "github.com/therecipe/qt/core" +) + +// var logExplorer = logging.MustGetLogger("Explorer") + func init() { BlocksModel_QmlRegisterType2("ExplorerModels", 1, 0, "QBlocks") } + +// +// type ExploreManager struct { +// qtCore.QObject +// // Blockchain core.BlockchainStatus +// BlockList BlocksModel +// _ int `property:"currentPage"` +// _ func() `constructor:"init"` +// _ func() `slot:"loadModel"` +// // _ func() []*QNetworking `slot:"getNetworks"` +// } +// +// func (explorer *ExploreManager) init() { +// // logExplorer.Info("Init explorerManager") +// // explorer.Blockchain = skycoin.NewSkycoinBlockchain(params.DataRefreshTimeout) +// // explorer.BlockList.init() +// } +// +// func (explorer *ExploreManager) loadModel() { +// // explorer.Blockchain = skycoin.NewSkycoinBlockchain(params.DataRefreshTimeout) +// } diff --git a/src/ui/Delegates/BlockListDelegate.qml b/src/ui/Delegates/BlockListDelegate.qml index 52329d96..74facea9 100644 --- a/src/ui/Delegates/BlockListDelegate.qml +++ b/src/ui/Delegates/BlockListDelegate.qml @@ -13,14 +13,14 @@ Item { readonly property real delegateHeight: 30 property bool emptyAddressVisible: true - property bool expanded: expand +// property bool expanded: expand // The following property is used to avoid a binding conflict with the `height` property. // Also avoids a bug with the animation when collapsing a wallet - readonly property real finalViewHeight: expanded ? delegateHeight*(addressList.count) + 50 : 0 +// readonly property real finalViewHeight: expanded ? delegateHeight*(addressList.count) + 50 : 0 width: blocksList.width - height: itemDelegateMainButton.height + (expanded ? finalViewHeight : 0) + height: itemDelegateMainButton.height Behavior on height { NumberAnimation { duration: 250; easing.type: Easing.OutQuint } } @@ -32,18 +32,18 @@ Item { id: itemDelegateMainButton Layout.fillWidth: true Layout.alignment: Qt.AlignTop - font.bold: expanded +// font.bold: expanded RowLayout { id: delegateRowLayout anchors.fill: parent - anchors.leftMargin: listBlockLeftMargin - anchors.rightMargin: listBlockRightMargin - spacing: listBlockSpacing + anchors.leftMargin: listBlocksLeftMargin + anchors.rightMargin: listBlocksRightMargin + spacing: listBlocksSpacing Label { id: labelBlockTime - text: date // a role of the model + text: Qt.formatDateTime(time, Qt.DefaultLocaleShortDate) // a role of the model Layout.leftMargin: listBlocksLeftMargin Layout.fillWidth: true } @@ -58,7 +58,7 @@ Item { Label { id: labelTransactions - text: txNumber // a role of the model + text: transactions // a role of the model horizontalAlignment: Text.AlignRight Layout.rightMargin: listBlocksRightMargin Layout.preferredWidth: internalLabelsWidth @@ -66,7 +66,7 @@ Item { Label { id: labelHash - text: hash // a role of the model + text: blockHash // a role of the model color: Material.accent horizontalAlignment: Text.AlignRight Layout.rightMargin: listBlocksRightMargin diff --git a/src/ui/ExplorerPage.qml b/src/ui/ExplorerPage.qml index 6123c0eb..4de2581d 100644 --- a/src/ui/ExplorerPage.qml +++ b/src/ui/ExplorerPage.qml @@ -12,6 +12,7 @@ Page { readonly property real listBlocksRightMargin: 50 readonly property real listBlocksSpacing: 20 readonly property real internalLabelsWidth: 70 + header: ColumnLayout { RowLayout { @@ -28,7 +29,8 @@ Page { text: qsTr("Block Number") font.pointSize: 9 horizontalAlignment: Text.AlignLeft - Layout.preferredWidth: internalLabelsWidth + Layout.preferredWidth: internalLabelsWidth+10 + Layout.rightMargin: 40 } Label { text: qsTr("Transactions") @@ -59,7 +61,10 @@ Page { ScrollView { id: scrollItem - + Component.onCompleted:{ + blockModel.update() + loader.running=false + } anchors.fill: parent ScrollBar.horizontal.policy: ScrollBar.AlwaysOff @@ -71,11 +76,43 @@ ScrollView { delegate: BlockListDelegate{} } } +footer: ColumnLayout{ +anchors.margins: 5 +RowLayout { + Layout.preferredHeight: 40 + Layout.alignment: Qt.AlignHCenter + Button { + text: "<" + onClicked: { + if(blockModel.currentPage > 1){ + blockModel.loadModel(blockModel.currentPage-- ) + } + } + } + Text { + text: blockModel.currentPage + " / " + blockModel.countPage + } + Button { + text: ">" + onClicked: { + if(blockModel.currentPage < blockModel.countPage){ + blockModel.loadModel(blockModel.currentPage++ ) + } + } + } + } +} QBlocks{ id:blockModel } +BusyIndicator { + id: loader + running: true + anchors.centerIn: parent + } + // ListModel{ // id:blockModeld // ListElement{ @@ -89,7 +126,7 @@ QBlocks{ // blockNumber:1 // txNumber:2 // hash:"9516639399ab2b02f6f0bc873f03f2f2ac1c94853dd031685de1021be78c71d7" - +// // } // ListElement{ // date:"01/02/1990" From 65f983ba2e965f9c9351dca5472efda405dda742 Mon Sep 17 00:00:00 2001 From: stdevHsequeda Date: Mon, 6 Jan 2020 11:06:00 -0500 Subject: [PATCH 8/9] [explorer] refs 248 Added BlockByHash method to SkycoinAPI interface in skycoin package. Added GetBlockByHash method to BlockchainStatus interface in core package. Added GetSize and GetTotalAmount method to Block interface in core package. Implemented GetSize and GetTotalAmount methods in SkycoinBlock type. Implemented GetBlockByHash method in SkycoinBlockchain type. Added TransactionLen, Blockhash, PrevBlockhash, Size, TotalAmount and TransactionList property to QBLock type in explorer package. Added loadBlockByHash method to BlockModel type in explorer package. Updated blockToQBlock method of BlockModel type in explorer package. Created BlckPage.qml and TransactionListDelegate.qml file in ui. Implemented block details page and transaction list delegate for each block in the UI. --- src/coin/mocks/Address.go | 66 ++++- src/coin/mocks/AddressIterator.go | 6 +- src/coin/mocks/AltcoinManager.go | 6 +- src/coin/mocks/AltcoinPlugin.go | 75 +++++- src/coin/mocks/Block.go | 71 +++++- src/coin/mocks/BlockchainSignService.go | 6 +- src/coin/mocks/BlockchainStatus.go | 52 +++- src/coin/mocks/BlockchainTransactionAPI.go | 6 +- src/coin/mocks/CryptoAccount.go | 6 +- src/coin/mocks/MultiPool.go | 6 +- src/coin/mocks/PEX.go | 6 +- src/coin/mocks/PexNodeIterator.go | 6 +- src/coin/mocks/PexNodeSet.go | 6 +- src/coin/mocks/Transaction.go | 6 +- src/coin/mocks/TransactionInput.go | 6 +- src/coin/mocks/TransactionInputIterator.go | 6 +- src/coin/mocks/TransactionIterator.go | 6 +- src/coin/mocks/TransactionOutput.go | 6 +- src/coin/mocks/TransactionOutputIterator.go | 6 +- src/coin/mocks/TxnSigner.go | 6 +- src/coin/mocks/TxnSignerIterator.go | 6 +- src/coin/mocks/Wallet.go | 6 +- src/coin/mocks/WalletAddress.go | 6 +- src/coin/mocks/WalletEnv.go | 6 +- src/coin/mocks/WalletIterator.go | 6 +- src/coin/mocks/WalletOutput.go | 6 +- src/coin/mocks/WalletSet.go | 50 +++- src/coin/mocks/WalletStorage.go | 6 +- src/coin/skycoin/models/blockchain.go | 57 ++++- src/coin/skycoin/skytypes/api.go | 2 + src/core/blockchain.go | 4 + src/core/coin.go | 6 +- src/models/explorer/blocksModel.go | 129 +++++++--- src/models/explorer/explorerManager.go | 34 +-- src/ui/BlockPage.qml | 248 +++++++++++++++++++ src/ui/Delegates/BlockListDelegate.qml | 19 +- src/ui/Delegates/TransactionListDelegate.qml | 190 ++++++++++++++ src/ui/ExplorerPage.qml | 35 ++- src/ui/GeneralStackView.qml | 19 ++ 39 files changed, 1058 insertions(+), 137 deletions(-) create mode 100644 src/ui/BlockPage.qml create mode 100644 src/ui/Delegates/TransactionListDelegate.qml diff --git a/src/coin/mocks/Address.go b/src/coin/mocks/Address.go index 124e4378..6fed5f8e 100644 --- a/src/coin/mocks/Address.go +++ b/src/coin/mocks/Address.go @@ -2,14 +2,48 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // Address is an autogenerated mock type for the Address type type Address struct { mock.Mock } +// Bytes provides a mock function with given fields: +func (_m *Address) Bytes() []byte { + ret := _m.Called() + + var r0 []byte + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + return r0 +} + +// Checksum provides a mock function with given fields: +func (_m *Address) Checksum() core.Checksum { + ret := _m.Called() + + var r0 core.Checksum + if rf, ok := ret.Get(0).(func() core.Checksum); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(core.Checksum) + } + } + + return r0 +} + // GetCryptoAccount provides a mock function with given fields: func (_m *Address) GetCryptoAccount() core.CryptoAccount { ret := _m.Called() @@ -40,6 +74,20 @@ func (_m *Address) IsBip32() bool { return r0 } +// Null provides a mock function with given fields: +func (_m *Address) Null() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + // String provides a mock function with given fields: func (_m *Address) String() string { ret := _m.Called() @@ -53,3 +101,17 @@ func (_m *Address) String() string { return r0 } + +// Verify provides a mock function with given fields: _a0 +func (_m *Address) Verify(_a0 core.PubKey) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(core.PubKey) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/src/coin/mocks/AddressIterator.go b/src/coin/mocks/AddressIterator.go index 25c9b98e..b85e922c 100644 --- a/src/coin/mocks/AddressIterator.go +++ b/src/coin/mocks/AddressIterator.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // AddressIterator is an autogenerated mock type for the AddressIterator type type AddressIterator struct { diff --git a/src/coin/mocks/AltcoinManager.go b/src/coin/mocks/AltcoinManager.go index de070fc3..2e24ebb2 100644 --- a/src/coin/mocks/AltcoinManager.go +++ b/src/coin/mocks/AltcoinManager.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // AltcoinManager is an autogenerated mock type for the AltcoinManager type type AltcoinManager struct { diff --git a/src/coin/mocks/AltcoinPlugin.go b/src/coin/mocks/AltcoinPlugin.go index 3c99d3d3..58bf3c60 100644 --- a/src/coin/mocks/AltcoinPlugin.go +++ b/src/coin/mocks/AltcoinPlugin.go @@ -2,14 +2,39 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // AltcoinPlugin is an autogenerated mock type for the AltcoinPlugin type type AltcoinPlugin struct { mock.Mock } +// AddressFromString provides a mock function with given fields: _a0 +func (_m *AltcoinPlugin) AddressFromString(_a0 string) (core.Address, error) { + ret := _m.Called(_a0) + + var r0 core.Address + if rf, ok := ret.Get(0).(func(string) core.Address); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(core.Address) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetDescription provides a mock function with given fields: func (_m *AltcoinPlugin) GetDescription() string { ret := _m.Called() @@ -155,7 +180,53 @@ func (_m *AltcoinPlugin) LoadWalletEnvs() []core.WalletEnv { return r0 } +// PubKeyFromBytes provides a mock function with given fields: _a0 +func (_m *AltcoinPlugin) PubKeyFromBytes(_a0 []byte) (core.PubKey, error) { + ret := _m.Called(_a0) + + var r0 core.PubKey + if rf, ok := ret.Get(0).(func([]byte) core.PubKey); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(core.PubKey) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // RegisterTo provides a mock function with given fields: manager func (_m *AltcoinPlugin) RegisterTo(manager core.AltcoinManager) { _m.Called(manager) } + +// SecKeyFromBytes provides a mock function with given fields: _a0 +func (_m *AltcoinPlugin) SecKeyFromBytes(_a0 []byte) (core.SecKey, error) { + ret := _m.Called(_a0) + + var r0 core.SecKey + if rf, ok := ret.Get(0).(func([]byte) core.SecKey); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(core.SecKey) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/src/coin/mocks/Block.go b/src/coin/mocks/Block.go index 01299560..1ebe496b 100644 --- a/src/coin/mocks/Block.go +++ b/src/coin/mocks/Block.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // Block is an autogenerated mock type for the Block type type Block struct { @@ -98,6 +100,27 @@ func (_m *Block) GetPrevHash() ([]byte, error) { return r0, r1 } +// GetSize provides a mock function with given fields: +func (_m *Block) GetSize() (uint64, error) { + ret := _m.Called() + + var r0 uint64 + if rf, ok := ret.Get(0).(func() uint64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(uint64) + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetTime provides a mock function with given fields: func (_m *Block) GetTime() (core.Timestamp, error) { ret := _m.Called() @@ -119,6 +142,50 @@ func (_m *Block) GetTime() (core.Timestamp, error) { return r0, r1 } +// GetTotalAmount provides a mock function with given fields: +func (_m *Block) GetTotalAmount() (uint64, error) { + ret := _m.Called() + + var r0 uint64 + if rf, ok := ret.Get(0).(func() uint64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(uint64) + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetTransactions provides a mock function with given fields: +func (_m *Block) GetTransactions() ([]core.Transaction, error) { + ret := _m.Called() + + var r0 []core.Transaction + if rf, ok := ret.Get(0).(func() []core.Transaction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]core.Transaction) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetVersion provides a mock function with given fields: func (_m *Block) GetVersion() (uint32, error) { ret := _m.Called() diff --git a/src/coin/mocks/BlockchainSignService.go b/src/coin/mocks/BlockchainSignService.go index 2fda5274..0b68ec7d 100644 --- a/src/coin/mocks/BlockchainSignService.go +++ b/src/coin/mocks/BlockchainSignService.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // BlockchainSignService is an autogenerated mock type for the BlockchainSignService type type BlockchainSignService struct { diff --git a/src/coin/mocks/BlockchainStatus.go b/src/coin/mocks/BlockchainStatus.go index e7081868..725d84f8 100644 --- a/src/coin/mocks/BlockchainStatus.go +++ b/src/coin/mocks/BlockchainStatus.go @@ -2,14 +2,39 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // BlockchainStatus is an autogenerated mock type for the BlockchainStatus type type BlockchainStatus struct { mock.Mock } +// GetBlockByHash provides a mock function with given fields: hash +func (_m *BlockchainStatus) GetBlockByHash(hash string) (core.Block, error) { + ret := _m.Called(hash) + + var r0 core.Block + if rf, ok := ret.Get(0).(func(string) core.Block); ok { + r0 = rf(hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(core.Block) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetCoinValue provides a mock function with given fields: coinvalue, ticker func (_m *BlockchainStatus) GetCoinValue(coinvalue core.CoinValueMetric, ticker string) (uint64, error) { ret := _m.Called(coinvalue, ticker) @@ -74,3 +99,26 @@ func (_m *BlockchainStatus) GetNumberOfBlocks() (uint64, error) { return r0, r1 } + +// GetRangeBlocks provides a mock function with given fields: start, end +func (_m *BlockchainStatus) GetRangeBlocks(start uint64, end uint64) ([]core.Block, error) { + ret := _m.Called(start, end) + + var r0 []core.Block + if rf, ok := ret.Get(0).(func(uint64, uint64) []core.Block); ok { + r0 = rf(start, end) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]core.Block) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(uint64, uint64) error); ok { + r1 = rf(start, end) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/src/coin/mocks/BlockchainTransactionAPI.go b/src/coin/mocks/BlockchainTransactionAPI.go index af133a91..581669bc 100644 --- a/src/coin/mocks/BlockchainTransactionAPI.go +++ b/src/coin/mocks/BlockchainTransactionAPI.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // BlockchainTransactionAPI is an autogenerated mock type for the BlockchainTransactionAPI type type BlockchainTransactionAPI struct { diff --git a/src/coin/mocks/CryptoAccount.go b/src/coin/mocks/CryptoAccount.go index eebf8290..59ae2674 100644 --- a/src/coin/mocks/CryptoAccount.go +++ b/src/coin/mocks/CryptoAccount.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // CryptoAccount is an autogenerated mock type for the CryptoAccount type type CryptoAccount struct { diff --git a/src/coin/mocks/MultiPool.go b/src/coin/mocks/MultiPool.go index 61d1b531..e24988f8 100644 --- a/src/coin/mocks/MultiPool.go +++ b/src/coin/mocks/MultiPool.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // MultiPool is an autogenerated mock type for the MultiPool type type MultiPool struct { diff --git a/src/coin/mocks/PEX.go b/src/coin/mocks/PEX.go index 1b8a5cd7..04141207 100644 --- a/src/coin/mocks/PEX.go +++ b/src/coin/mocks/PEX.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // PEX is an autogenerated mock type for the PEX type type PEX struct { diff --git a/src/coin/mocks/PexNodeIterator.go b/src/coin/mocks/PexNodeIterator.go index 80af2e0c..c12f6ecd 100644 --- a/src/coin/mocks/PexNodeIterator.go +++ b/src/coin/mocks/PexNodeIterator.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // PexNodeIterator is an autogenerated mock type for the PexNodeIterator type type PexNodeIterator struct { diff --git a/src/coin/mocks/PexNodeSet.go b/src/coin/mocks/PexNodeSet.go index 46a15cd3..29af5830 100644 --- a/src/coin/mocks/PexNodeSet.go +++ b/src/coin/mocks/PexNodeSet.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // PexNodeSet is an autogenerated mock type for the PexNodeSet type type PexNodeSet struct { diff --git a/src/coin/mocks/Transaction.go b/src/coin/mocks/Transaction.go index 8a847125..c5aa0213 100644 --- a/src/coin/mocks/Transaction.go +++ b/src/coin/mocks/Transaction.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // Transaction is an autogenerated mock type for the Transaction type type Transaction struct { diff --git a/src/coin/mocks/TransactionInput.go b/src/coin/mocks/TransactionInput.go index 1c92673d..4b805038 100644 --- a/src/coin/mocks/TransactionInput.go +++ b/src/coin/mocks/TransactionInput.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // TransactionInput is an autogenerated mock type for the TransactionInput type type TransactionInput struct { diff --git a/src/coin/mocks/TransactionInputIterator.go b/src/coin/mocks/TransactionInputIterator.go index 0f8edd84..240f99c2 100644 --- a/src/coin/mocks/TransactionInputIterator.go +++ b/src/coin/mocks/TransactionInputIterator.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // TransactionInputIterator is an autogenerated mock type for the TransactionInputIterator type type TransactionInputIterator struct { diff --git a/src/coin/mocks/TransactionIterator.go b/src/coin/mocks/TransactionIterator.go index 3fc64568..63588f57 100644 --- a/src/coin/mocks/TransactionIterator.go +++ b/src/coin/mocks/TransactionIterator.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // TransactionIterator is an autogenerated mock type for the TransactionIterator type type TransactionIterator struct { diff --git a/src/coin/mocks/TransactionOutput.go b/src/coin/mocks/TransactionOutput.go index cc048507..bf9c4252 100644 --- a/src/coin/mocks/TransactionOutput.go +++ b/src/coin/mocks/TransactionOutput.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // TransactionOutput is an autogenerated mock type for the TransactionOutput type type TransactionOutput struct { diff --git a/src/coin/mocks/TransactionOutputIterator.go b/src/coin/mocks/TransactionOutputIterator.go index 6ac35fb0..5360c696 100644 --- a/src/coin/mocks/TransactionOutputIterator.go +++ b/src/coin/mocks/TransactionOutputIterator.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // TransactionOutputIterator is an autogenerated mock type for the TransactionOutputIterator type type TransactionOutputIterator struct { diff --git a/src/coin/mocks/TxnSigner.go b/src/coin/mocks/TxnSigner.go index 1a6b3f40..237cf0db 100644 --- a/src/coin/mocks/TxnSigner.go +++ b/src/coin/mocks/TxnSigner.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // TxnSigner is an autogenerated mock type for the TxnSigner type type TxnSigner struct { diff --git a/src/coin/mocks/TxnSignerIterator.go b/src/coin/mocks/TxnSignerIterator.go index be49a8b4..1ae85e7c 100644 --- a/src/coin/mocks/TxnSignerIterator.go +++ b/src/coin/mocks/TxnSignerIterator.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // TxnSignerIterator is an autogenerated mock type for the TxnSignerIterator type type TxnSignerIterator struct { diff --git a/src/coin/mocks/Wallet.go b/src/coin/mocks/Wallet.go index d35a0180..aa056653 100644 --- a/src/coin/mocks/Wallet.go +++ b/src/coin/mocks/Wallet.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // Wallet is an autogenerated mock type for the Wallet type type Wallet struct { diff --git a/src/coin/mocks/WalletAddress.go b/src/coin/mocks/WalletAddress.go index 35a4fd3e..550cc205 100644 --- a/src/coin/mocks/WalletAddress.go +++ b/src/coin/mocks/WalletAddress.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // WalletAddress is an autogenerated mock type for the WalletAddress type type WalletAddress struct { diff --git a/src/coin/mocks/WalletEnv.go b/src/coin/mocks/WalletEnv.go index b3cc6724..efdcbc47 100644 --- a/src/coin/mocks/WalletEnv.go +++ b/src/coin/mocks/WalletEnv.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // WalletEnv is an autogenerated mock type for the WalletEnv type type WalletEnv struct { diff --git a/src/coin/mocks/WalletIterator.go b/src/coin/mocks/WalletIterator.go index 2ca3f3e4..951b7d69 100644 --- a/src/coin/mocks/WalletIterator.go +++ b/src/coin/mocks/WalletIterator.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // WalletIterator is an autogenerated mock type for the WalletIterator type type WalletIterator struct { diff --git a/src/coin/mocks/WalletOutput.go b/src/coin/mocks/WalletOutput.go index 58ac6516..e83a8175 100644 --- a/src/coin/mocks/WalletOutput.go +++ b/src/coin/mocks/WalletOutput.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // WalletOutput is an autogenerated mock type for the WalletOutput type type WalletOutput struct { diff --git a/src/coin/mocks/WalletSet.go b/src/coin/mocks/WalletSet.go index 9fb40e99..bbe61e9d 100644 --- a/src/coin/mocks/WalletSet.go +++ b/src/coin/mocks/WalletSet.go @@ -2,21 +2,23 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // WalletSet is an autogenerated mock type for the WalletSet type type WalletSet struct { mock.Mock } -// CreateWallet provides a mock function with given fields: name, seed, isEncryptrd, pwd, scanAddressesN -func (_m *WalletSet) CreateWallet(name string, seed string, isEncryptrd bool, pwd core.PasswordReader, scanAddressesN int) (core.Wallet, error) { - ret := _m.Called(name, seed, isEncryptrd, pwd, scanAddressesN) +// CreateWallet provides a mock function with given fields: name, seed, walletType, isEncryptrd, pwd, scanAddressesN +func (_m *WalletSet) CreateWallet(name string, seed string, walletType string, isEncryptrd bool, pwd core.PasswordReader, scanAddressesN int) (core.Wallet, error) { + ret := _m.Called(name, seed, walletType, isEncryptrd, pwd, scanAddressesN) var r0 core.Wallet - if rf, ok := ret.Get(0).(func(string, string, bool, core.PasswordReader, int) core.Wallet); ok { - r0 = rf(name, seed, isEncryptrd, pwd, scanAddressesN) + if rf, ok := ret.Get(0).(func(string, string, string, bool, core.PasswordReader, int) core.Wallet); ok { + r0 = rf(name, seed, walletType, isEncryptrd, pwd, scanAddressesN) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(core.Wallet) @@ -24,8 +26,8 @@ func (_m *WalletSet) CreateWallet(name string, seed string, isEncryptrd bool, pw } var r1 error - if rf, ok := ret.Get(1).(func(string, string, bool, core.PasswordReader, int) error); ok { - r1 = rf(name, seed, isEncryptrd, pwd, scanAddressesN) + if rf, ok := ret.Get(1).(func(string, string, string, bool, core.PasswordReader, int) error); ok { + r1 = rf(name, seed, walletType, isEncryptrd, pwd, scanAddressesN) } else { r1 = ret.Error(1) } @@ -33,6 +35,20 @@ func (_m *WalletSet) CreateWallet(name string, seed string, isEncryptrd bool, pw return r0, r1 } +// DefaultWalletType provides a mock function with given fields: +func (_m *WalletSet) DefaultWalletType() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + // GetWallet provides a mock function with given fields: id func (_m *WalletSet) GetWallet(id string) core.Wallet { ret := _m.Called(id) @@ -64,3 +80,19 @@ func (_m *WalletSet) ListWallets() core.WalletIterator { return r0 } + +// SupportedWalletTypes provides a mock function with given fields: +func (_m *WalletSet) SupportedWalletTypes() []string { + ret := _m.Called() + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + return r0 +} diff --git a/src/coin/mocks/WalletStorage.go b/src/coin/mocks/WalletStorage.go index 3802abb2..d84350db 100644 --- a/src/coin/mocks/WalletStorage.go +++ b/src/coin/mocks/WalletStorage.go @@ -2,8 +2,10 @@ package mocks -import core "github.com/fibercrypto/fibercryptowallet/src/core" -import mock "github.com/stretchr/testify/mock" +import ( + core "github.com/fibercrypto/fibercryptowallet/src/core" + mock "github.com/stretchr/testify/mock" +) // WalletStorage is an autogenerated mock type for the WalletStorage type type WalletStorage struct { diff --git a/src/coin/skycoin/models/blockchain.go b/src/coin/skycoin/models/blockchain.go index 5f046014..1b2200bf 100644 --- a/src/coin/skycoin/models/blockchain.go +++ b/src/coin/skycoin/models/blockchain.go @@ -68,6 +68,40 @@ func (sb *SkycoinBlock) GetFee(ticker string) (uint64, error) { return 0, nil } +// GetSize provides block size in bytes +func (sb *SkycoinBlock) GetSize() (uint64, error) { + logBlockchain.Info("Getting size") + if sb.Block == nil { + return 0, errors.ErrBlockNotSet + } + return uint64(sb.Block.Size), nil +} + +func (sb *SkycoinBlock) GetTotalAmount() (uint64, error) { + logBlockchain.Info("Getting size") + if sb.Block == nil { + return 0, errors.ErrBlockNotSet + } + txs, err := sb.GetTransactions() + if err != nil { + logBlockchain.Error(err) + return 0, err + } + + var totalAmount uint64 + for e := range txs { + for i := range txs[e].GetInputs() { + coins, err := txs[e].GetInputs()[i].GetCoins(Sky) + if err != nil { + logBlockchain.Error(err) + return 0, err + } + totalAmount += coins + } + } + return totalAmount, nil +} + func (sb *SkycoinBlock) IsGenesisBlock() (bool, error) { logBlockchain.Info("Getting if is Genesis block") if sb.Block == nil { @@ -171,9 +205,30 @@ func (ss *SkycoinBlockchain) GetNumberOfBlocks() (uint64, error) { return ss.cachedStatus.NumberOfBlocks.Current, nil } +// GetBlockByHash return a block by a hash +func (ss *SkycoinBlockchain) GetBlockByHash(hash string) (core.Block, error) { + logBlockchain.Info("Getting block by hash") + c, err := NewSkycoinApiClient(PoolSection) + if err != nil { + logBlockchain.Error(err) + return nil, err + } + + defer ReturnSkycoinClient(c) + + block, err := c.BlockByHash(hash) + + if err != nil { + logBlockchain.Error(err) + return nil, err + } + + return &SkycoinBlock{Block: block}, nil +} + // GetRangeBlocks return a list of blocks between start and end range. func (ss *SkycoinBlockchain) GetRangeBlocks(start, end uint64) ([]core.Block, error) { - logBlockchain.Info("Getting all blocks") + logBlockchain.Info("Getting range of blocks") c, err := NewSkycoinApiClient(PoolSection) if err != nil { logBlockchain.Error(err) diff --git a/src/coin/skycoin/skytypes/api.go b/src/coin/skycoin/skytypes/api.go index 0bc6a8ec..2d094290 100644 --- a/src/coin/skycoin/skytypes/api.go +++ b/src/coin/skycoin/skytypes/api.go @@ -24,6 +24,8 @@ type SkycoinAPI interface { CoinSupply() (*api.CoinSupply, error) // BlocksInRange return a block between start and end range. BlocksInRange(start, end uint64) (*readable.Blocks, error) + // BlockByHash makes a request to GET /api/v1/block?hash=xxx + BlockByHash(hash string) (*readable.Block, error) // LastBlocks Get last N blocks LastBlocks(n uint64) (*readable.Blocks, error) // BlockchainProgress Get blockchain progress diff --git a/src/core/blockchain.go b/src/core/blockchain.go index 3a1ff1a4..20baf7b5 100644 --- a/src/core/blockchain.go +++ b/src/core/blockchain.go @@ -19,6 +19,10 @@ type BlockchainStatus interface { // GetNumberOfBlocks determine number of blocks in the blockchain GetNumberOfBlocks() (uint64, error) + // GetBlockByHash return a block by a hash + GetBlockByHash(hash string) (Block, error) + + // GetRangeBlocks return a list of blocks between start and end range. GetRangeBlocks(start, end uint64) ([]Block, error) } diff --git a/src/core/coin.go b/src/core/coin.go index f5e4f181..d824fdce 100644 --- a/src/core/coin.go +++ b/src/core/coin.go @@ -110,8 +110,12 @@ type Block interface { GetHeight() (uint64, error) // GetFee computes block fee expressed in coins of asset identified by ticker GetFee(ticker string) (uint64, error) + // GetSize provides block size in bytes + GetSize() (uint64, error) // IsGenesisBlock determines whether this block starts blockchain sequence IsGenesisBlock() (bool, error) - + // GetTotalAmount provides the total of coin used in this block + GetTotalAmount() (uint64, error) + // GetTransactions provides a list of transactions of current block GetTransactions() ([]Transaction, error) } diff --git a/src/models/explorer/blocksModel.go b/src/models/explorer/blocksModel.go index 925bcd48..147ea7e5 100644 --- a/src/models/explorer/blocksModel.go +++ b/src/models/explorer/blocksModel.go @@ -3,9 +3,11 @@ package explorer import ( skycoin "github.com/fibercrypto/fibercryptowallet/src/coin/skycoin/models" "github.com/fibercrypto/fibercryptowallet/src/core" + "github.com/fibercrypto/fibercryptowallet/src/models" "github.com/fibercrypto/fibercryptowallet/src/params" "github.com/fibercrypto/fibercryptowallet/src/util" "github.com/fibercrypto/fibercryptowallet/src/util/logging" + "github.com/skycoin/skycoin/src/util/droplet" qtcore "github.com/therecipe/qt/core" ) @@ -47,8 +49,12 @@ var logExplorer = logging.MustGetLogger("Explorer") const ( Time = int(qtcore.Qt__UserRole) + iota + 1 BlockNumber - Transactions - BlockHash + TransactionLen + Blockhash + PrevBlockhash + Size + TotalAmount + TransactionList ) // BlocksModel List of Blocks to be show. @@ -62,9 +68,10 @@ type BlocksModel struct { _ int `property:"blockNumber"` _ map[int]*qtcore.QByteArray `property:"roles"` _ []*QBlock `property:"blocks"` - - _ func(pageNum int) `signal:"loadPage"` - _ func() `signal:"update,auto"` + _ *QBlock `property:"blockDetail"` + _ func(pageNum int) `signal:"loadPage"` + _ func() `signal:"update,auto"` + _ func(hash string) `signal:"loadBlockByHash"` _ func([]*QBlock) `slot:"addBlocks"` _ func() `slot:"loadModel"` @@ -74,30 +81,35 @@ type BlocksModel struct { type QBlock struct { qtcore.QObject - _ qtcore.QDateTime `property:"time"` - _ uint64 `property:"blockNumber"` - _ int `property:"transactions"` - _ string `property:"blockHash"` + _ []*models.QTransaction `property:"transactionList"` + _ qtcore.QDateTime `property:"time"` + _ string `property:"blockhash"` + _ string `property:"prevBlockhash"` + _ string `property:"totalAmount"` + _ uint64 `property:"blockNumber"` + _ uint64 `property:"size"` + _ int `property:"transactionLen"` } func (m *BlocksModel) init() { logExplorer.Info("Init Explorer Model") m.SetRoles(map[int]*qtcore.QByteArray{ - Time: qtcore.NewQByteArray2("time", -1), - BlockNumber: qtcore.NewQByteArray2("blockNumber", -1), - Transactions: qtcore.NewQByteArray2("transactions", -1), - BlockHash: qtcore.NewQByteArray2("blockHash", -1), + Time: qtcore.NewQByteArray2("time", -1), + BlockNumber: qtcore.NewQByteArray2("blockNumber", -1), + TransactionLen: qtcore.NewQByteArray2("transactionLen", -1), + Blockhash: qtcore.NewQByteArray2("blockhash", -1), + PrevBlockhash: qtcore.NewQByteArray2("prevBlockhash", -1), + Size: qtcore.NewQByteArray2("size", -1), + TotalAmount: qtcore.NewQByteArray2("totalAmount", -1), + TransactionList: qtcore.NewQByteArray2("transactionList", -1), }) m.ConnectRowCount(m.rowCount) m.ConnectRoleNames(m.roleNames) m.ConnectData(m.data) - m.ConnectAddBlocks(m.addBlocks) - m.ConnectLoadModel(m.loadModel) m.ConnectUpdate(m.update) m.ConnectLoadPage(m.loadPage) + m.ConnectLoadBlockByHash(m.loadBlockByHash) m.blockChain = skycoin.NewSkycoinBlockchain(params.DataRefreshTimeout) - m.update() - m.loadPage(1) } func (blocksM *BlocksModel) update() { @@ -128,6 +140,7 @@ func (blocksM *BlocksModel) updateInfo() error { } func (blocksM *BlocksModel) loadPage(pageNum int) { + logExplorer.Info("Load page ", pageNum) if pageNum > 0 && pageNum <= blocksM.CountPage() { blocksM.SetCurrentPage(pageNum) blocksM.loadModel() @@ -158,13 +171,29 @@ func (m *BlocksModel) data(index *qtcore.QModelIndex, role int) *qtcore.QVariant { return qtcore.NewQVariant1(qb.BlockNumber()) } - case Transactions: + case TransactionLen: + { + return qtcore.NewQVariant1(qb.TransactionLen()) + } + case Blockhash: + { + return qtcore.NewQVariant1(qb.Blockhash()) + } + case PrevBlockhash: + { + return qtcore.NewQVariant1(qb.PrevBlockhash()) + } + case Size: + { + return qtcore.NewQVariant1(qb.Size()) + } + case TotalAmount: { - return qtcore.NewQVariant1(qb.Transactions()) + return qtcore.NewQVariant1(qb.TotalAmount()) } - case BlockHash: + case TransactionList: { - return qtcore.NewQVariant1(qb.BlockHash()) + return qtcore.NewQVariant1(qb.TransactionList()) } default: { @@ -200,41 +229,85 @@ func (m *BlocksModel) loadModel() { m.addBlocks(qBlocks) } +func (m *BlocksModel) loadBlockByHash(hash string) { + logExplorer.Info("Loading block details") + logExplorer.Info(hash) + block, err := m.blockChain.GetBlockByHash(hash) + if err != nil { + logExplorer.WithError(err).Errorf("Couldn't get the detail of block with hash %s", hash) + } + + m.SetBlockDetail(blockToQBlock(block)) +} + func blockToQBlock(block core.Block) *QBlock { var qBlock = NewQBlock(nil) - timestamp, err := block.GetTime() + timestamp, err := block.GetTime() if err != nil { logExplorer.WithError(err).Error("Couldn't get the time of block") } year, month, day, h, m, s := util.ParseDate(int64(timestamp)) + qBlock.SetTime(qtcore.NewQDateTime3(qtcore.NewQDate3(year, month, day), qtcore.NewQTime3(h, m, s, 0), qtcore.Qt__LocalTime)) hash, err := block.GetHash() - if err != nil { logExplorer.WithError(err).Error("Couldn't get the hash of block") } - qBlock.SetBlockHash(string(hash)) + qBlock.SetBlockhash(string(hash)) height, err := block.GetHeight() - if err != nil { logExplorer.WithError(err).Error("Couldn't get the height of block") } qBlock.SetBlockNumber(height) - transactions, err := block.GetTransactions() + prevHash, err := block.GetPrevHash() + if err != nil { + logExplorer.WithError(err).Error("Couldn't get the prevBlockHash of block") + } + + qBlock.SetPrevBlockhash(string(prevHash)) + + blockSize, err := block.GetSize() + if err != nil { + logExplorer.WithError(err).Error("Couldn't get the size of block") + } + + qBlock.SetSize(blockSize) + totalAmount, err := block.GetTotalAmount() if err != nil { - logExplorer.Error(err) + logExplorer.WithError(err).Error("Couldn't get the total amount of block") } - qBlock.SetTransactions(len(transactions)) + totalAmountStr, err := droplet.ToString(totalAmount) + if err != nil { + logExplorer.WithError(err).Error("Couldn't get the total amount of block") + } + qBlock.SetTotalAmount(totalAmountStr) + + transactionList, err := block.GetTransactions() + if err != nil { + logExplorer.WithError(err).Error("Couldn't get the transactions list of block") + } + var qTransactionsList []*models.QTransaction + for e := range transactionList { + + qTransaction, err := models.NewQTransactionFromTransaction(transactionList[e]) + if err != nil { + logExplorer.WithError(err).Error("Couldn't get the transactions list of block") + } + qTransactionsList = append(qTransactionsList, qTransaction) + } + logExplorer.Info("Transaction Count: ", len(qTransactionsList)) + qBlock.SetTransactionList(qTransactionsList) + qBlock.SetTransactionLen(len(qTransactionsList)) return qBlock } diff --git a/src/models/explorer/explorerManager.go b/src/models/explorer/explorerManager.go index 747294ca..9e86d625 100644 --- a/src/models/explorer/explorerManager.go +++ b/src/models/explorer/explorerManager.go @@ -1,37 +1,25 @@ package explorer -import ( -// skycoin "github.com/fibercrypto/fibercryptowallet/src/coin/skycoin/models" -// "github.com/fibercrypto/fibercryptowallet/src/core" -// "github.com/fibercrypto/fibercryptowallet/src/params" -// "github.com/fibercrypto/fibercryptowallet/src/util/logging" -// "github.com/fibercrypto/fibercryptowallet/src/core" -// qtCore "github.com/therecipe/qt/core" -) - -// var logExplorer = logging.MustGetLogger("Explorer") - +// import ( +// qtCore "github.com/therecipe/qt/core" +// ) +// func init() { BlocksModel_QmlRegisterType2("ExplorerModels", 1, 0, "QBlocks") + // BlocksModel_QmlRegisterType2("ExplorerModels", 1, 0, "ExplorerManager") } // // type ExploreManager struct { // qtCore.QObject -// // Blockchain core.BlockchainStatus -// BlockList BlocksModel -// _ int `property:"currentPage"` -// _ func() `constructor:"init"` -// _ func() `slot:"loadModel"` -// // _ func() []*QNetworking `slot:"getNetworks"` +// Blockchain core.BlockchainStatus +// _ func() `constructor:"init"` +// +// _ func(hash string) `slot:"loadBlockByHash"` // } // // func (explorer *ExploreManager) init() { -// // logExplorer.Info("Init explorerManager") -// // explorer.Blockchain = skycoin.NewSkycoinBlockchain(params.DataRefreshTimeout) -// // explorer.BlockList.init() -// } +// logExplorer.Info("Init explorerManager") // -// func (explorer *ExploreManager) loadModel() { -// // explorer.Blockchain = skycoin.NewSkycoinBlockchain(params.DataRefreshTimeout) // } +// diff --git a/src/ui/BlockPage.qml b/src/ui/BlockPage.qml new file mode 100644 index 00000000..f7fcefe9 --- /dev/null +++ b/src/ui/BlockPage.qml @@ -0,0 +1,248 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 +import ExplorerModels 1.0 + +import "Delegates/" + +Page { + id: blockPage + property string hash; + property alias blockhash : textInputHashCurrentBlock.text + property alias prevhash : textInputHashPrevBlock.text + property alias time : labelTimestampLastBlock.text + property alias blockNum : laberHeightOfBlock.text + property alias totalAmount : labelTotalAmountBlock.text + property alias blockSize : labelBlockSize.text + property alias transactionList : blocksList.model + QBlocks{ + id:explorerManager + } + + Component.onCompleted:{ + explorerManager.loadBlockByHash(hash) + blockhash = explorerManager.blockDetail.blockhash + time = Qt.formatDateTime(explorerManager.blockDetail.time, Qt.DefaultLocaleShortDate) + blockNum = explorerManager.blockDetail.blockNumber + prevhash = explorerManager.blockDetail.prevBlockhash + blockSize = explorerManager.blockDetail.size + totalAmount = explorerManager.blockDetail.totalAmount + transactionList=explorerManager.blockDetail.transactionList + } + +// ColumnLayout { +// id: columnLayoutRoot +// anchors.top: parent.top +// anchors.left: parent.left +// anchors.right: parent.right +// anchors.margins: 20 +// spacing: 20 + + GroupBox { + id: groupBoxBlockDetails + title: qsTr("Block Details") + clip: true + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + + ColumnLayout { + id: columnLayoutBlockDetails + anchors.fill: parent + spacing: 20 + RowLayout { + spacing: 82 + + ColumnLayout { + width:parent.width/2 + Label { + text: qsTr("Height") + font.bold: true + } + Label { + id: laberHeightOfBlock + } + } + ColumnLayout { + Layout.fillWidth: true + Label { + text: qsTr("Parent Hash") + font.bold: true + + ToolButton { + id: toolButtonCopyPrevHash + anchors.left: parent.right + anchors.verticalCenter: parent.verticalCenter + icon.source: "qrc:/images/resources/images/icons/copy.svg" +// visible: textInputHashLastBlock.text + ToolTip.text: qsTr("Copy to clipboard") + ToolTip.visible: hovered // TODO: pressed when mobile? + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + + Image { + id: imageCopiedPrevHash + anchors.centerIn: parent + source: "qrc:/images/resources/images/icons/check-simple.svg" + fillMode: Image.PreserveAspectFit + sourceSize: Qt.size(toolButtonCopyPrevHash.icon.width*1.5, toolButtonCopyPrevHash.icon.height*1.5) + z: 1 + + opacity: 0.0 + } + + onClicked: { + if (textInputHashLastBlock.text) { + textInputHashLastBlock.selectAll() + textInputHashLastBlock.copy() + textInputHashLastBlock.deselect() + if (copyAnimationPrevHash.running) { + copyAnimationPrevHash.restart() + } else { + copyAnimationPrevHash.start() + } + } + } + + SequentialAnimation { + id: copyAnimationPrevHash + NumberAnimation { target: imageCopiedPrevHash; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic } + PauseAnimation { duration: 1000 } + NumberAnimation { target: imageCopiedPrevHash; property: "opacity"; to: 0.0; easing.type: Easing.OutCubic } + } + } // ToolButton + } // Label + TextInput { + anchors.right:parent.right + id: textInputHashPrevBlock + Layout.fillWidth: true + readOnly: true + font.family: "Code New Roman" + wrapMode: Label.WrapAnywhere + } + } // ColumnLayout + } + RowLayout { + spacing: 20 + + ColumnLayout { + + Label { + text: qsTr("Block Timestamp") + font.bold: true + } + Label { + id: labelTimestampLastBlock + } + } + + ColumnLayout { + Layout.fillWidth: true + Label { + text: qsTr("Block Hash") + font.bold: true + + ToolButton { + id: toolButtonCopyCurrentHash + anchors.left: parent.right + anchors.verticalCenter: parent.verticalCenter + icon.source: "qrc:/images/resources/images/icons/copy.svg" +// visible: textInputHashLastBlock.text + ToolTip.text: qsTr("Copy to clipboard") + ToolTip.visible: hovered // TODO: pressed when mobile? + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + + Image { + id: imageCopiedCurrentHash + anchors.centerIn: parent + source: "qrc:/images/resources/images/icons/check-simple.svg" + fillMode: Image.PreserveAspectFit + sourceSize: Qt.size(toolButtonCopyCurrentHash.icon.width*1.5, toolButtonCopyCurrentHash.icon.height*1.5) + z: 1 + + opacity: 0.0 + } + + onClicked: { + if (textInputHashLastBlock.text) { + textInputHashLastBlock.selectAll() + textInputHashLastBlock.copy() + textInputHashLastBlock.deselect() + if (copyAnimationCurrentHash.running) { + copyAnimationCurrentHash.restart() + } else { + copyAnimationCurrentHash.start() + } + } + } + + SequentialAnimation { + id: copyAnimationCurrentHash + NumberAnimation { target: imageCopiedCurrentHash; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic } + PauseAnimation { duration: 1000 } + NumberAnimation { target: imageCopiedCurrentHash; property: "opacity"; to: 0.0; easing.type: Easing.OutCubic } + } + } // ToolButton + } // Label + TextInput { + id: textInputHashCurrentBlock + Layout.fillWidth: true + readOnly: true + font.family: "Code New Roman" + wrapMode: Label.WrapAnywhere + } + } // ColumnLayout + } // RowLayout + RowLayout { + spacing: 60 + + ColumnLayout { + + Label { + text: qsTr("Block Size") + font.bold: true + } + Label { + id: labelBlockSize + } + } + + ColumnLayout { + Label { + text: qsTr("Total Amount") + font.bold: true + } // Label + Label { + id: labelTotalAmountBlock + Layout.fillWidth: true + } + } // ColumnLayout + } // RowLayout + } // ColumnLayout (block details) + } // GroupBox (block details) + + + GroupBox { + anchors.bottom:parent.bottom + id: groupBoxSkyDetails + + anchors.margins: 30 + title: qsTr("Transactions") + height:250 + width: groupBoxBlockDetails.width + clip: true + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + + ScrollView{ + anchors.fill: parent + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ListView { + id: blocksList + anchors.fill: parent + clip: true + delegate: TransactionListDelegate{} + } + } + + } // GroupBox (sky details) +// } // ColumnLayout (root) +} diff --git a/src/ui/Delegates/BlockListDelegate.qml b/src/ui/Delegates/BlockListDelegate.qml index 74facea9..7cc579e9 100644 --- a/src/ui/Delegates/BlockListDelegate.qml +++ b/src/ui/Delegates/BlockListDelegate.qml @@ -12,12 +12,6 @@ Item { id: root readonly property real delegateHeight: 30 - property bool emptyAddressVisible: true -// property bool expanded: expand - // The following property is used to avoid a binding conflict with the `height` property. - // Also avoids a bug with the animation when collapsing a wallet -// readonly property real finalViewHeight: expanded ? delegateHeight*(addressList.count) + 50 : 0 - width: blocksList.width height: itemDelegateMainButton.height @@ -29,10 +23,16 @@ Item { anchors.fill: parent ItemDelegate { +// Component.onCompleted:{ +// console.log(modelData.transactionList) +// } id: itemDelegateMainButton Layout.fillWidth: true Layout.alignment: Qt.AlignTop -// font.bold: expanded + + onClicked:{ + generalStackView.openBlockPage(blockhash) + } RowLayout { id: delegateRowLayout @@ -58,7 +58,7 @@ Item { Label { id: labelTransactions - text: transactions // a role of the model + text: transactionLen // a role of the model horizontalAlignment: Text.AlignRight Layout.rightMargin: listBlocksRightMargin Layout.preferredWidth: internalLabelsWidth @@ -66,13 +66,12 @@ Item { Label { id: labelHash - text: blockHash // a role of the model + text: blockhash // a role of the model color: Material.accent horizontalAlignment: Text.AlignRight Layout.rightMargin: listBlocksRightMargin visible:parent.width>750 Layout.fillWidth: true - } } // RowLayout } // ItemDelegate diff --git a/src/ui/Delegates/TransactionListDelegate.qml b/src/ui/Delegates/TransactionListDelegate.qml new file mode 100644 index 00000000..009b60da --- /dev/null +++ b/src/ui/Delegates/TransactionListDelegate.qml @@ -0,0 +1,190 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 +import QtGraphicalEffects 1.12 + +Item{ + id: root + + readonly property real delegateHeight: 80 + property alias inputsModel: inputList.model + property alias outputsModel: outputList.model + readonly property real finalViewHeight: delegateHeight + 400 + + width: parent.width + height: itemDelegateMainButton.height+inputList.height+ 30 + outputList.height+30 + + ColumnLayout { + id: delegateColumnLayout + anchors.fill: parent + + ItemDelegate { + id: itemDelegateMainButton + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + Component.onCompleted:{ + inputsModel = modelData.inputs + outputsModel = modelData.outputs + } + + RowLayout { + id: delegateRowLayout + anchors.fill: parent + + + Label { + font.bold:true + text: qsTr("Transaction ID:") // a role of the model + Layout.fillWidth: true + } + + Label { + text: modelData.transactionId + horizontalAlignment: Text.AlignLeft + } + } // RowLayout + } // ItemDelegate + RowLayout{ + Label{ + text:qsTr("Input") + font.bold:true + } + } + RowLayout{ + Rectangle { + id: rectInput + Layout.fillWidth: true + height: 1 + color: "#DDDDDD" + } + } + ListView { + id: inputList + implicitHeight: delegateHeight*(inputList.count) + 20 + clip: true + interactive: false + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + Behavior on implicitHeight { NumberAnimation { duration: 250; easing.type: Easing.OutQuint } } + Behavior on opacity { NumberAnimation { duration: expanded ? 250 : 1000; easing.type: Easing.OutQuint } } + + delegate: ItemDelegate { + Component.onCompleted:{ + console.log("delegate: "+this.width) + } + height: delegateHeight + ColumnLayout{ + Layout.fillWidth:true + RowLayout{ + Label{ + width:parent.width + text:address + } + } + RowLayout{ + Label{ + width:parent.width + font.bold:true + text:qsTr("Coins: ") + } + Label{ + width:parent.width + Component.onCompleted:{ + console.log("label width: "+this.width) + } + text:addressSky + } + } + RowLayout{ + Label{ + width:parent.width + + font.bold:true + text:qsTr("CoinsHours: ") + } + Label{ + width:parent.width + Component.onCompleted:{ + console.log("label width: "+this.width) + } + text:addressCoinHours + } + } + } + } + + } // ListView + RowLayout{ + Label{ + text:qsTr("Outputs") + font.bold:true + } + } + RowLayout{ + Rectangle { + id: rectOutput + Layout.fillWidth: true + height: 1 + color: "#DDDDDD" + } + } + ListView { + id: outputList + implicitHeight: delegateHeight*(outputList.count) + 20 + clip: true + interactive: false + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + Behavior on implicitHeight { NumberAnimation { duration: 250; easing.type: Easing.OutQuint } } + Behavior on opacity { NumberAnimation { duration: expanded ? 250 : 1000; easing.type: Easing.OutQuint } } + + delegate: ItemDelegate { + + height: delegateHeight + ColumnLayout{ + Layout.fillWidth:true + RowLayout{ + Label{ + width:parent.width + text:address + } + } + RowLayout{ + Label{ + width:parent.width + font.bold:true + text:qsTr("Coins: ") + } + Label{ + width:parent.width + Component.onCompleted:{ + console.log("label width: "+this.width) + } + text:addressSky + } + } + RowLayout{ + Label{ + width:parent.width + + font.bold:true + text:qsTr("CoinsHours: ") + } + Label{ + width:parent.width + Component.onCompleted:{ + console.log("label width: "+this.width) + } + text:addressCoinHours + } + } + } + } + + } // ListView + } // ColumnLayout + +} diff --git a/src/ui/ExplorerPage.qml b/src/ui/ExplorerPage.qml index 4de2581d..929acfa5 100644 --- a/src/ui/ExplorerPage.qml +++ b/src/ui/ExplorerPage.qml @@ -12,7 +12,10 @@ Page { readonly property real listBlocksRightMargin: 50 readonly property real listBlocksSpacing: 20 readonly property real internalLabelsWidth: 70 - +Component.onCompleted:{ +blockModel.update() +blockModel.loadPage(1) +} header: ColumnLayout { RowLayout { @@ -59,7 +62,7 @@ Page { } // ColumnLayout (header) -ScrollView { + ScrollView { id: scrollItem Component.onCompleted:{ blockModel.update() @@ -76,16 +79,17 @@ ScrollView { delegate: BlockListDelegate{} } } -footer: ColumnLayout{ -anchors.margins: 5 -RowLayout { + footer: ColumnLayout{ + anchors.margins: 5 + RowLayout { Layout.preferredHeight: 40 Layout.alignment: Qt.AlignHCenter Button { text: "<" onClicked: { if(blockModel.currentPage > 1){ - blockModel.loadModel(blockModel.currentPage-- ) + blockModel.currentPage-=1 + blockModel.loadPage(blockModel.currentPage ) } } } @@ -96,23 +100,32 @@ RowLayout { text: ">" onClicked: { if(blockModel.currentPage < blockModel.countPage){ - blockModel.loadModel(blockModel.currentPage++ ) + blockModel.currentPage+=1 + blockModel.loadPage(blockModel.currentPage ) } } } } } -QBlocks{ - id:blockModel -} + QBlocks{ + id:blockModel + } -BusyIndicator { + BusyIndicator { id: loader running: true anchors.centerIn: parent } + + +// BlockPage{ +// id:blockPage +// width:parent.width +// height:parent.height +// } + // ListModel{ // id:blockModeld // ListElement{ diff --git a/src/ui/GeneralStackView.qml b/src/ui/GeneralStackView.qml index 59afed89..78682414 100644 --- a/src/ui/GeneralStackView.qml +++ b/src/ui/GeneralStackView.qml @@ -2,12 +2,14 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import BlockchainModels 1.0 import WalletsManager 1.0 +import ExplorerModels 1.0 Item { id: generalStackView property alias depth: stackView.depth property alias busy: stackView.busy + property string hash; function openOutputsPage() { if (stackView.depth > 1) { @@ -88,6 +90,14 @@ Item { } } + function openBlockPage(hash) { + generalStackView.hash=hash + if (stackView.depth > 1) { + stackView.replace(componentBlockPage) + } else { + stackView.push(componentBlockPage) + } + } function pop() { stackView.pop() } @@ -182,5 +192,14 @@ Item { } } + Component { + id: componentBlockPage + + BlockPage { + id: blockPage + hash:generalStackView.hash + } + } + } From 85707503cd89e7c56e4aa17887a76d271e2e1956 Mon Sep 17 00:00:00 2001 From: stdevHsequeda Date: Thu, 9 Jan 2020 18:48:11 -0500 Subject: [PATCH 9/9] [explorer] refs #248 - Changed from []QTransaction to []TransactionDetails type of TransactionList parameter in BlockModel in explorer package. Added - TransactionTypeGeneric to transaction types in transactions package. - Added NewTransactionDetailFromCoreTransaction method in transaction package. - Added TransactionDetails UI to Explorer UI. --- src/models/explorer/blocksModel.go | 22 ++-- src/models/explorer/explorerManager.go | 24 ++-- src/models/transactions/transactions.go | 124 ++++++++++++++++++- src/ui/BlockPage.qml | 2 +- src/ui/Delegates/TransactionListDelegate.qml | 49 ++++++-- src/ui/TransactionDetails.qml | 5 +- 6 files changed, 189 insertions(+), 37 deletions(-) diff --git a/src/models/explorer/blocksModel.go b/src/models/explorer/blocksModel.go index 147ea7e5..f8511dc1 100644 --- a/src/models/explorer/blocksModel.go +++ b/src/models/explorer/blocksModel.go @@ -3,7 +3,7 @@ package explorer import ( skycoin "github.com/fibercrypto/fibercryptowallet/src/coin/skycoin/models" "github.com/fibercrypto/fibercryptowallet/src/core" - "github.com/fibercrypto/fibercryptowallet/src/models" + "github.com/fibercrypto/fibercryptowallet/src/models/transactions" "github.com/fibercrypto/fibercryptowallet/src/params" "github.com/fibercrypto/fibercryptowallet/src/util" "github.com/fibercrypto/fibercryptowallet/src/util/logging" @@ -81,14 +81,14 @@ type BlocksModel struct { type QBlock struct { qtcore.QObject - _ []*models.QTransaction `property:"transactionList"` - _ qtcore.QDateTime `property:"time"` - _ string `property:"blockhash"` - _ string `property:"prevBlockhash"` - _ string `property:"totalAmount"` - _ uint64 `property:"blockNumber"` - _ uint64 `property:"size"` - _ int `property:"transactionLen"` + _ []*transactions.TransactionDetails `property:"transactionList"` + _ qtcore.QDateTime `property:"time"` + _ string `property:"blockhash"` + _ string `property:"prevBlockhash"` + _ string `property:"totalAmount"` + _ uint64 `property:"blockNumber"` + _ uint64 `property:"size"` + _ int `property:"transactionLen"` } func (m *BlocksModel) init() { @@ -296,10 +296,10 @@ func blockToQBlock(block core.Block) *QBlock { if err != nil { logExplorer.WithError(err).Error("Couldn't get the transactions list of block") } - var qTransactionsList []*models.QTransaction + var qTransactionsList []*transactions.TransactionDetails for e := range transactionList { - qTransaction, err := models.NewQTransactionFromTransaction(transactionList[e]) + qTransaction, err := transactions.NewTransactionDetailFromCoreTransaction(transactionList[e], -1) if err != nil { logExplorer.WithError(err).Error("Couldn't get the transactions list of block") } diff --git a/src/models/explorer/explorerManager.go b/src/models/explorer/explorerManager.go index 9e86d625..9aad71aa 100644 --- a/src/models/explorer/explorerManager.go +++ b/src/models/explorer/explorerManager.go @@ -1,25 +1,25 @@ package explorer -// import ( -// qtCore "github.com/therecipe/qt/core" -// ) -// func init() { BlocksModel_QmlRegisterType2("ExplorerModels", 1, 0, "QBlocks") // BlocksModel_QmlRegisterType2("ExplorerModels", 1, 0, "ExplorerManager") } -// -// type ExploreManager struct { +// type ExplorerManager struct { // qtCore.QObject -// Blockchain core.BlockchainStatus -// _ func() `constructor:"init"` -// -// _ func(hash string) `slot:"loadBlockByHash"` +// _ func() `constructor:"init"` + +// _ func() []*transactions.TransactionDetails `slot:"loadHistoryWithFilters"` +// _ func() []*transactions.TransactionDetails `slot:"loadHistory"` +// _ func(string) `slot:"addFilter"` +// _ func(string) `slot:"removeFilter"` +// walletEnv core.WalletEnv // } // -// func (explorer *ExploreManager) init() { -// logExplorer.Info("Init explorerManager") +// func (em *ExplorerManager) init() { // // } // +// func (em *ExplorerManager) GetTransactionDetails(txId string) *transactions.TransactionDetails { +// +// } diff --git a/src/models/transactions/transactions.go b/src/models/transactions/transactions.go index d13cf90c..bc25ced2 100644 --- a/src/models/transactions/transactions.go +++ b/src/models/transactions/transactions.go @@ -1,16 +1,25 @@ package transactions import ( + coin "github.com/fibercrypto/fibercryptowallet/src/coin/skycoin/models" + "github.com/fibercrypto/fibercryptowallet/src/coin/skycoin/params" + "github.com/fibercrypto/fibercryptowallet/src/core" "github.com/fibercrypto/fibercryptowallet/src/models/address" - qtcore "github.com/therecipe/qt/core" + "github.com/fibercrypto/fibercryptowallet/src/util" + "github.com/fibercrypto/fibercryptowallet/src/util/logging" + qtCore "github.com/therecipe/qt/core" + "strconv" + "time" ) +var logTransactionDetails = logging.MustGetLogger("TransactionDetails") + func init() { TransactionDetails_QmlRegisterType2("HistoryModels", 1, 0, "QTransactionDetail") } const ( - Date = int(qtcore.Qt__UserRole) + 1<= 0 { + txnDetails.SetType(txType) + } else { + txnDetails.SetType(TransactionTypeGeneric) + } + + inputs := address.NewAddressList(nil) + outputs := address.NewAddressList(nil) + + txnIns := transaction.GetInputs() + + for e := range txnIns { + + qIn := address.NewAddressDetails(nil) + qIn.SetAddress(txnIns[e].GetSpentOutput().GetAddress().String()) + + // TODO Do this generic for all coins + skyUint64, err := txnIns[e].GetCoins(params.SkycoinTicker) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't get Skycoins balance") + continue + } + accuracy, err := util.AltcoinQuotient(params.SkycoinTicker) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't get Skycoins quotient") + continue + } + skyFloat := float64(skyUint64) / float64(accuracy) + qIn.SetAddressSky(strconv.FormatFloat(skyFloat, 'f', -1, 64)) + chUint64, err := txnIns[e].GetCoins(params.CoinHoursTicker) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't get Coin Hours balance") + continue + } + accuracy, err = util.AltcoinQuotient(params.CoinHoursTicker) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't get Coin Hours quotient") + continue + } + qIn.SetAddressCoinHours(util.FormatCoins(chUint64, accuracy)) + inputs.AddAddress(qIn) + } + + txnDetails.SetInputs(inputs) + + for _, out := range transaction.GetOutputs() { + sky, err := out.GetCoins(params.SkycoinTicker) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't get Skycoins balance") + continue + } + qOu := address.NewAddressDetails(nil) + qOu.SetAddress(out.GetAddress().String()) + accuracy, err := util.AltcoinQuotient(params.SkycoinTicker) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't get Skycoins quotient") + continue + } + qOu.SetAddressSky(util.FormatCoins(sky, accuracy)) + val, err := out.GetCoins(params.CoinHoursTicker) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't get Coin Hours balance") + continue + } + accuracy, err = util.AltcoinQuotient(coin.CoinHour) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't get Coin Hours quotient") + continue + } + qOu.SetAddressCoinHours(util.FormatCoins(val, accuracy)) + outputs.AddAddress(qOu) + } + + txnDetails.SetOutputs(outputs) + + fee, err := transaction.ComputeFee(params.CoinHoursTicker) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't compute fee of the operation") + return nil, err + } + accuracy, err := util.AltcoinQuotient(coin.CoinHoursTicker) + if err != nil { + logTransactionDetails.WithError(err).Warn("Couldn't get " + coin.CoinHoursTicker + " coins quotient") + } + txnDetails.SetHoursBurned(util.FormatCoins(fee, accuracy)) + + return txnDetails, nil +} diff --git a/src/ui/BlockPage.qml b/src/ui/BlockPage.qml index f7fcefe9..a15a87f1 100644 --- a/src/ui/BlockPage.qml +++ b/src/ui/BlockPage.qml @@ -43,7 +43,7 @@ Page { id: groupBoxBlockDetails title: qsTr("Block Details") clip: true - Layout.fillWidth: true + width:parent.width Layout.alignment: Qt.AlignTop | Qt.AlignHCenter ColumnLayout { diff --git a/src/ui/Delegates/TransactionListDelegate.qml b/src/ui/Delegates/TransactionListDelegate.qml index 009b60da..e6693af5 100644 --- a/src/ui/Delegates/TransactionListDelegate.qml +++ b/src/ui/Delegates/TransactionListDelegate.qml @@ -4,6 +4,7 @@ import QtQuick.Controls.Material 2.12 import QtQuick.Layouts 1.12 import QtGraphicalEffects 1.12 +import "../Dialogs" Item{ id: root @@ -13,14 +14,14 @@ Item{ readonly property real finalViewHeight: delegateHeight + 400 width: parent.width - height: itemDelegateMainButton.height+inputList.height+ 30 + outputList.height+30 + height: itemDelegateTransaction.height+inputList.height+ 30 + outputList.height+30 ColumnLayout { id: delegateColumnLayout anchors.fill: parent ItemDelegate { - id: itemDelegateMainButton + id: itemDelegateTransaction Layout.fillWidth: true Layout.alignment: Qt.AlignTop Component.onCompleted:{ @@ -32,7 +33,6 @@ Item{ id: delegateRowLayout anchors.fill: parent - Label { font.bold:true text: qsTr("Transaction ID:") // a role of the model @@ -40,10 +40,24 @@ Item{ } Label { - text: modelData.transactionId + text: modelData.transactionID horizontalAlignment: Text.AlignLeft } } // RowLayout + onClicked:{ + + dialogTransactionDetails.date = modelData.date + dialogTransactionDetails.status = modelData.status + dialogTransactionDetails.type = modelData.type + dialogTransactionDetails.hoursReceived = modelData.hoursTraspassed + dialogTransactionDetails.hoursBurned = modelData.hoursBurned + dialogTransactionDetails.transactionID = modelData.transactionID + dialogTransactionDetails.modelInputs = modelData.inputs + dialogTransactionDetails.modelOutputs = modelData.outputs + + + dialogTransactionDetails.open() + } } // ItemDelegate RowLayout{ Label{ @@ -71,9 +85,6 @@ Item{ Behavior on opacity { NumberAnimation { duration: expanded ? 250 : 1000; easing.type: Easing.OutQuint } } delegate: ItemDelegate { - Component.onCompleted:{ - console.log("delegate: "+this.width) - } height: delegateHeight ColumnLayout{ Layout.fillWidth:true @@ -187,4 +198,28 @@ Item{ } // ListView } // ColumnLayout +DialogTransactionDetails { + id: dialogTransactionDetails + + readonly property real maxHeight: expanded ? 590 : 370 + + anchors.centerIn: Overlay.overlay + width: applicationWindow.width > 640 ? 640 - 40 : applicationWindow.width - 40 + height: applicationWindow.height > maxHeight ? maxHeight - 40 : applicationWindow.height - 40 + Behavior on height { NumberAnimation { duration: 1000; easing.type: Easing.OutQuint } } + + modal: true + focus: true + + date: listTransactions.currentItem !== null ? listTransactions.currentItem.modelDate : "" + status: listTransactions.currentItem !== null ? listTransactions.currentItem.modelStatus : 0 + type: listTransactions.currentItem !== null ? listTransactions.currentItem.modelType : 0 + amount: modelData.amount !== null ? modelData.amount : "" + hoursReceived: listTransactions.currentItem !== null ? listTransactions.currentItem.modelHoursReceived : 1 + hoursBurned: listTransactions.currentItem !== null ? listTransactions.currentItem.modelHoursBurned : 1 + transactionID: modelData.transactionId !== null ? modelData.transactionId : "" + modelInputs: listTransactions.currentItem !== null ? listTransactions.currentItem.modelInputs : null + modelOutputs: listTransactions.currentItem !== null ? listTransactions.currentItem.modelOutputs : null + } + } diff --git a/src/ui/TransactionDetails.qml b/src/ui/TransactionDetails.qml index 5908ef7e..7b7a4005 100644 --- a/src/ui/TransactionDetails.qml +++ b/src/ui/TransactionDetails.qml @@ -35,7 +35,8 @@ Item { enum Type { Send, Receive, - Internal + Internal, + Generic } implicitHeight: 80 + rowLayoutBasicDetails.height + (expanded ? rowLayoutMoreDetails.height : 0) @@ -122,7 +123,7 @@ Item { Layout.topMargin: -10 Layout.rightMargin: 20 Image { - source: "qrc:/images/resources/images/icons/send-" + (type === TransactionDetails.Type.Receive ? "blue" : "amber") + ".svg" + source: "qrc:/images/resources/images/icons/send-" + (type === TransactionDetails.Type.Receive || type === TransactionDetails.Type.Generic ? "blue" : "amber") + ".svg" sourceSize: "72x72" fillMode: Image.PreserveAspectFit mirror: type === TransactionDetails.Type.Receive