diff --git a/main.go b/main.go index 043247a9..d597b496 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( _ "github.com/fibercrypto/fibercryptowallet/src/models/addressBook" _ "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/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 ead24274..1b2200bf 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) { @@ -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 { @@ -76,6 +110,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 +138,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 +172,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 +205,50 @@ 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 range of blocks") + c, err := NewSkycoinApiClient(PoolSection) + if err != nil { + logBlockchain.Error(err) + return nil, err + } + + defer ReturnSkycoinClient(c) + + 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 +323,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..7a14064e 100644 --- a/src/coin/skycoin/models/coin.go +++ b/src/coin/skycoin/models/coin.go @@ -246,6 +246,29 @@ 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 + } + defer ReturnSkycoinClient(c) + 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/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/coin/skycoin/skytypes/api.go b/src/coin/skycoin/skytypes/api.go index fc99638b..2d094290 100644 --- a/src/coin/skycoin/skytypes/api.go +++ b/src/coin/skycoin/skytypes/api.go @@ -22,6 +22,10 @@ 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) + // 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 952e9ba7..20baf7b5 100644 --- a/src/core/blockchain.go +++ b/src/core/blockchain.go @@ -18,6 +18,12 @@ type BlockchainStatus interface { GetLastBlock() (Block, error) // 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) } // 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..d824fdce 100644 --- a/src/core/coin.go +++ b/src/core/coin.go @@ -110,6 +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 new file mode 100644 index 00000000..f8511dc1 --- /dev/null +++ b/src/models/explorer/blocksModel.go @@ -0,0 +1,313 @@ +package explorer + +import ( + skycoin "github.com/fibercrypto/fibercryptowallet/src/coin/skycoin/models" + "github.com/fibercrypto/fibercryptowallet/src/core" + "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" + "github.com/skycoin/skycoin/src/util/droplet" + 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 + TransactionLen + Blockhash + PrevBlockhash + Size + TotalAmount + TransactionList +) + +// BlocksModel List of Blocks to be show. +type BlocksModel struct { + qtcore.QAbstractListModel + blockChain core.BlockchainStatus + _ func() `constructor:"init"` + + _ int `property:"currentPage"` + _ int `property:"countPage"` + _ int `property:"blockNumber"` + _ map[int]*qtcore.QByteArray `property:"roles"` + _ []*QBlock `property:"blocks"` + _ *QBlock `property:"blockDetail"` + _ func(pageNum int) `signal:"loadPage"` + _ func() `signal:"update,auto"` + _ func(hash string) `signal:"loadBlockByHash"` + + _ func([]*QBlock) `slot:"addBlocks"` + _ func() `slot:"loadModel"` +} + +// QBlock Contains info about the block to be show. +type QBlock struct { + qtcore.QObject + + _ []*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() { + logExplorer.Info("Init Explorer Model") + m.SetRoles(map[int]*qtcore.QByteArray{ + 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.ConnectUpdate(m.update) + m.ConnectLoadPage(m.loadPage) + m.ConnectLoadBlockByHash(m.loadBlockByHash) + m.blockChain = skycoin.NewSkycoinBlockchain(params.DataRefreshTimeout) +} + +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 { + 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) { + logExplorer.Info("Load page ", pageNum) + if pageNum > 0 && pageNum <= blocksM.CountPage() { + blocksM.SetCurrentPage(pageNum) + blocksM.loadModel() + } +} + +func (m *BlocksModel) rowCount(*qtcore.QModelIndex) int { + return len(m.Blocks()) +} + +func (m *BlocksModel) roleNames() map[int]*qtcore.QByteArray { + return m.Roles() +} + +func (m *BlocksModel) data(index *qtcore.QModelIndex, role int) *qtcore.QVariant { + if !index.IsValid() || index.Row() >= len(m.Blocks()) { + return qtcore.NewQVariant() + } + + qb := m.Blocks()[index.Row()] + + switch role { + case Time: + { + return qtcore.NewQVariant1(qb.Time()) + } + case BlockNumber: + { + return qtcore.NewQVariant1(qb.BlockNumber()) + } + 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.TotalAmount()) + } + case TransactionList: + { + return qtcore.NewQVariant1(qb.TransactionList()) + } + default: + { + return qtcore.NewQVariant() + } + } +} + +func (m *BlocksModel) addBlocks(qb []*QBlock) { + logExplorer.Info("Add Block") + m.BeginResetModel() + m.SetBlocks(qb) + m.EndResetModel() + +} + +func (m *BlocksModel) loadModel() { + 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 (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() + 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) + + 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.WithError(err).Error("Couldn't get the total amount of block") + } + + 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 []*transactions.TransactionDetails + for e := range transactionList { + + qTransaction, err := transactions.NewTransactionDetailFromCoreTransaction(transactionList[e], -1) + 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 new file mode 100644 index 00000000..9aad71aa --- /dev/null +++ b/src/models/explorer/explorerManager.go @@ -0,0 +1,25 @@ +package explorer + +func init() { + BlocksModel_QmlRegisterType2("ExplorerModels", 1, 0, "QBlocks") + // BlocksModel_QmlRegisterType2("ExplorerModels", 1, 0, "ExplorerManager") +} + +// type ExplorerManager struct { +// qtCore.QObject +// _ 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 (em *ExplorerManager) init() { +// +// } +// +// func (em *ExplorerManager) GetTransactionDetails(txId string) *transactions.TransactionDetails { +// +// } 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/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 new file mode 100644 index 00000000..a15a87f1 --- /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 + width:parent.width + 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/CustomMenuBar.qml b/src/ui/CustomMenuBar.qml index ff7e4cd1..4b0e72ad 100755 --- a/src/ui/CustomMenuBar.qml +++ b/src/ui/CustomMenuBar.qml @@ -17,11 +17,13 @@ RowLayout { property alias enableNetworking: menuItemNetworking.enabled property alias enableSettings: menuItemSettings.enabled property alias enableAddrsBook: menuItemAddressBook.enabled + property alias enableExplorer: menuItemExplorer.enabled // Signals signal outputsRequested() signal pendingTransactionsRequested() signal networkingRequested() + signal explorerRequested() signal settingsRequested() signal blockchainRequested() signal aboutRequested() @@ -73,6 +75,7 @@ RowLayout { enablePendingTransactions = true enableBlockchain = true enableNetworking = true + enableExplorer= true enableSettings = true enableAddrsBook = true } @@ -132,6 +135,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..7cc579e9 --- /dev/null +++ b/src/ui/Delegates/BlockListDelegate.qml @@ -0,0 +1,79 @@ +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 + + width: blocksList.width + height: itemDelegateMainButton.height + + Behavior on height { NumberAnimation { duration: 250; easing.type: Easing.OutQuint } } + + ColumnLayout { + id: delegateColumnLayout + anchors.fill: parent + + ItemDelegate { +// Component.onCompleted:{ +// console.log(modelData.transactionList) +// } + id: itemDelegateMainButton + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + onClicked:{ + generalStackView.openBlockPage(blockhash) + } + + RowLayout { + id: delegateRowLayout + anchors.fill: parent + anchors.leftMargin: listBlocksLeftMargin + anchors.rightMargin: listBlocksRightMargin + spacing: listBlocksSpacing + + Label { + id: labelBlockTime + text: Qt.formatDateTime(time, Qt.DefaultLocaleShortDate) // 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: transactionLen // a role of the model + horizontalAlignment: Text.AlignRight + Layout.rightMargin: listBlocksRightMargin + Layout.preferredWidth: internalLabelsWidth + } + + Label { + id: labelHash + 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 + } +} \ No newline at end of file diff --git a/src/ui/Delegates/TransactionListDelegate.qml b/src/ui/Delegates/TransactionListDelegate.qml new file mode 100644 index 00000000..e6693af5 --- /dev/null +++ b/src/ui/Delegates/TransactionListDelegate.qml @@ -0,0 +1,225 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 +import QtGraphicalEffects 1.12 + +import "../Dialogs" +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: itemDelegateTransaction.height+inputList.height+ 30 + outputList.height+30 + + ColumnLayout { + id: delegateColumnLayout + anchors.fill: parent + + ItemDelegate { + id: itemDelegateTransaction + 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 + 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{ + 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 { + 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 + +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/ExplorerPage.qml b/src/ui/ExplorerPage.qml new file mode 100644 index 00000000..929acfa5 --- /dev/null +++ b/src/ui/ExplorerPage.qml @@ -0,0 +1,158 @@ +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 + +Page { + id: explorer + readonly property real listBlocksLeftMargin: 20 + 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 { + 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+10 + Layout.rightMargin: 40 + } + 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 + Component.onCompleted:{ + blockModel.update() + loader.running=false + } + 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{} + } + } + footer: ColumnLayout{ + anchors.margins: 5 + RowLayout { + Layout.preferredHeight: 40 + Layout.alignment: Qt.AlignHCenter + Button { + text: "<" + onClicked: { + if(blockModel.currentPage > 1){ + blockModel.currentPage-=1 + blockModel.loadPage(blockModel.currentPage ) + } + } + } + Text { + text: blockModel.currentPage + " / " + blockModel.countPage + } + Button { + text: ">" + onClicked: { + if(blockModel.currentPage < blockModel.countPage){ + blockModel.currentPage+=1 + blockModel.loadPage(blockModel.currentPage ) + } + } + } + } +} + + QBlocks{ + id:blockModel + } + + BusyIndicator { + id: loader + running: true + anchors.centerIn: parent + } + + + +// BlockPage{ +// id:blockPage +// width:parent.width +// height:parent.height +// } + +// 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 + +} diff --git a/src/ui/GeneralStackView.qml b/src/ui/GeneralStackView.qml index 17172063..61d3e4ab 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) { @@ -72,6 +74,14 @@ Item { } } + function openExplorerPage() { + if (stackView.depth > 1) { + stackView.replace(componentExplorer) + } else { + stackView.push(componentExplorer) + } + } + function openSettingsPage() { if (stackView.depth > 1) { stackView.replace(componentSettings) @@ -88,6 +98,14 @@ Item { } } + function openBlockPage(hash) { + generalStackView.hash=hash + if (stackView.depth > 1) { + stackView.replace(componentBlockPage) + } else { + stackView.push(componentBlockPage) + } + } function pop() { stackView.pop() } @@ -166,6 +184,14 @@ Item { } } + Component { + id: componentExplorer + + ExplorerPage { + id: explorer + } + } + Component { id: componentSettings @@ -174,6 +200,15 @@ Item { } } + Component { + id: componentBlockPage + + BlockPage { + id: blockPage + hash:generalStackView.hash + } + } + Component { id: componentAddressBook @@ -182,5 +217,5 @@ Item { } } - + } 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 diff --git a/src/ui/main.qml b/src/ui/main.qml index 19173a38..2a66ee33 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 enableAddrsBook = true @@ -61,6 +62,7 @@ ApplicationWindow { enableOutputs = true enablePendingTransactions = false enableBlockchain = true + enableExplorer = true enableNetworking = true enableSettings = true enableAddrsBook = true @@ -74,12 +76,25 @@ ApplicationWindow { enableOutputs = true enablePendingTransactions = true enableBlockchain = false + enableExplorer = true enableNetworking = true enableSettings = true enableAddrsBook = 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") @@ -87,6 +102,7 @@ ApplicationWindow { enableOutputs = true enablePendingTransactions = true enableBlockchain = true + enableExplorer = true enableNetworking = false enableSettings = true enableAddrsBook = true @@ -100,6 +116,7 @@ ApplicationWindow { enableOutputs = true enablePendingTransactions = true enableBlockchain = true + enableExplorer = true enableNetworking = true enableSettings = false enableAddrsBook = true