Wallet(SendModal): New tokens selector intially integrated
Closes: #15512
This commit is contained in:
parent
baa65de1ae
commit
f6320f69cb
|
@ -4,9 +4,11 @@ import QtQuick.Layouts 1.15
|
|||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
|
||||
import Storybook 1.0
|
||||
import Models 1.0
|
||||
import utils 1.0
|
||||
|
||||
import shared.popups.send 1.0
|
||||
|
@ -22,18 +24,68 @@ SplitView {
|
|||
orientation: Qt.Horizontal
|
||||
|
||||
property WalletAssetsStore walletAssetStore: WalletAssetsStore {
|
||||
assetsWithFilteredBalances: root.assetsWithFilteredBalances
|
||||
|
||||
// Workaround to satisfy stub which is not empty (but should be)
|
||||
assetsWithFilteredBalances: ListModel {}
|
||||
|
||||
property var groupedAccountAssetsModel: ListModel {
|
||||
Component.onCompleted: {
|
||||
const data = [
|
||||
{
|
||||
tokensKey: "key_eth",
|
||||
name: "Ethereum",
|
||||
symbol: "ETH",
|
||||
decimals: 18,
|
||||
communityId: "",
|
||||
balances: [
|
||||
{
|
||||
chainId: "1",
|
||||
balance: "122082928968121891",
|
||||
account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240",
|
||||
},
|
||||
{
|
||||
chainId: "420",
|
||||
balance: "559133758939097000",
|
||||
account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240"
|
||||
}
|
||||
],
|
||||
currentCurrencyBalance: 234.234,
|
||||
marketDetails: {
|
||||
currencyPrice: {
|
||||
amount: 12234.23,
|
||||
displayDecimals: true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
tokensKey: "key_dai",
|
||||
name: "DAI",
|
||||
symbol: "DAI",
|
||||
decimals: 18,
|
||||
communityId: "",
|
||||
balances: [
|
||||
{
|
||||
chainId: "420",
|
||||
balance: "1142155111",
|
||||
account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240"
|
||||
},
|
||||
{
|
||||
chainId: "1",
|
||||
balance: "4411211243121551121",
|
||||
account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240"
|
||||
}
|
||||
],
|
||||
currentCurrencyBalance: 234.234,
|
||||
marketDetails: {
|
||||
currencyPrice: {
|
||||
amount: 234.23,
|
||||
displayDecimals: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
property SubmodelProxyModel assetsWithFilteredBalances: SubmodelProxyModel {
|
||||
sourceModel: root.walletAssetStore.groupedAccountsAssetsModel
|
||||
submodelRoleName: "balances"
|
||||
delegateModel: SortFilterProxyModel {
|
||||
sourceModel: submodel
|
||||
filters: FastExpressionFilter {
|
||||
expression: txStore.selectedSenderAccountAddress === model.account
|
||||
expectedRoles: ["account"]
|
||||
append(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,16 @@ import SortFilterProxyModel 0.2
|
|||
|
||||
import AppLayouts.Wallet.stores 1.0
|
||||
|
||||
// TODO: This store, as all other stores should be empty QtObject {}.
|
||||
// All mocking should be done in place in Storybook pages and unit tests.
|
||||
// If it's necessary to share mocks between tests/pages, such mock can be
|
||||
// created by deriving from empty stub and putting in mocks dir.
|
||||
// Stores itself should be simple, thin layers over functionality exposed from
|
||||
// the backend. No additional logic should there. Data transformation logic
|
||||
// should be delegated to adaptors, stateles helpers to proper utility singletons.
|
||||
//
|
||||
// PLEASE DO NOT ADD ANY NEW CONTENT HERE
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
|
|
|
@ -53,6 +53,9 @@ QObject {
|
|||
|
||||
// output model
|
||||
readonly property SortFilterProxyModel outputAssetsModel: SortFilterProxyModel {
|
||||
|
||||
objectName: "TokenSelectorViewAdaptor_outputAssetsModel"
|
||||
|
||||
sourceModel: showAllTokens && !!plainTokensBySymbolModel ? concatModel : assetsObjectProxyModel
|
||||
|
||||
proxyRoles: [
|
||||
|
@ -135,6 +138,7 @@ QObject {
|
|||
}
|
||||
expression: isPresentOnEnabledNetworks(model.addressPerChain)
|
||||
expectedRoles: ["addressPerChain"]
|
||||
enabled: root.enabledChainIds.length
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -203,6 +207,8 @@ QObject {
|
|||
id: assetsObjectProxyModel
|
||||
sourceModel: root.assetsModel
|
||||
|
||||
objectName: "TokenSelectorViewAdaptor_assetsObjectProxyModel"
|
||||
|
||||
delegate: SortFilterProxyModel {
|
||||
id: delegateRoot
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ QtObject {
|
|||
}
|
||||
|
||||
readonly property var collectiblesController: ManageTokensController {
|
||||
sourceModel: _jointCollectiblesBySymbolModel
|
||||
sourceModel: root.jointCollectiblesBySymbolModel
|
||||
settingsKey: "WalletCollectibles"
|
||||
serializeAsCollectibles: true
|
||||
|
||||
|
@ -85,8 +85,8 @@ QtObject {
|
|||
]
|
||||
}
|
||||
|
||||
/* PRIVATE: This model joins the "Tokens By Symbol Model" and "Communities Model" by communityId */
|
||||
property LeftJoinModel _jointCollectiblesBySymbolModel: LeftJoinModel {
|
||||
/* TODO: move all transformations to a dedicated adaptors */
|
||||
readonly property LeftJoinModel jointCollectiblesBySymbolModel: LeftJoinModel {
|
||||
objectName: "jointCollectiblesBySymbolModel"
|
||||
|
||||
leftModel: allCollectiblesModel
|
||||
|
|
|
@ -1522,8 +1522,12 @@ Item {
|
|||
|
||||
sourceComponent: SendPopups.SendModal {
|
||||
onlyAssets: sendModal.onlyAssets
|
||||
store: appMain.transactionStore
|
||||
|
||||
loginType: appMain.rootStore.loginType
|
||||
|
||||
store: appMain.transactionStore
|
||||
collectiblesStore: appMain.walletCollectiblesStore
|
||||
|
||||
onClosed: {
|
||||
sendModal.closed()
|
||||
sendModal.preSelectedSendType = Constants.SendType.Unknown
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Dialogs 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import SortFilterProxyModel 0.2
|
||||
|
@ -20,8 +20,10 @@ import StatusQ.Core.Theme 0.1
|
|||
import StatusQ.Core.Utils 0.1
|
||||
import StatusQ.Popups.Dialog 0.1
|
||||
|
||||
import AppLayouts.Wallet.controls 1.0
|
||||
import AppLayouts.Wallet.adaptors 1.0
|
||||
import AppLayouts.Wallet.controls 1.0
|
||||
import AppLayouts.Wallet.panels 1.0
|
||||
import AppLayouts.Wallet.stores 1.0 as WalletStores
|
||||
|
||||
import shared.popups.send.panels 1.0
|
||||
import "./controls"
|
||||
|
@ -48,7 +50,8 @@ StatusDialog {
|
|||
property alias modalHeader: modalHeader.text
|
||||
|
||||
required property TransactionStore store
|
||||
property var nestedCollectiblesModel: store.nestedCollectiblesModel
|
||||
property WalletStores.CollectiblesStore collectiblesStore
|
||||
|
||||
property var bestRoutes
|
||||
property bool isLoading: false
|
||||
property int loginType
|
||||
|
@ -117,40 +120,6 @@ StatusDialog {
|
|||
property var hoveredHoldingType: Constants.TokenType.Unknown
|
||||
readonly property bool isHoveredHoldingValidAsset: !!hoveredHolding && hoveredHoldingType === Constants.TokenType.ERC20
|
||||
|
||||
function getHolding(holdingId, holdingType) {
|
||||
if (holdingType === Constants.TokenType.ERC20) {
|
||||
return store.getAsset(assetsAdaptor.model, holdingId)
|
||||
} else if (holdingType === Constants.TokenType.ERC721 || holdingType === Constants.TokenType.ERC1155) {
|
||||
return store.getCollectible(holdingId)
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
function setSelectedHoldingId(holdingId, holdingType) {
|
||||
let holding = getHolding(holdingId, holdingType)
|
||||
setSelectedHolding(holding, holdingType)
|
||||
}
|
||||
|
||||
function setSelectedHolding(holding, holdingType) {
|
||||
d.selectedHoldingType = holdingType
|
||||
d.selectedHolding = holding
|
||||
let selectorHolding = store.holdingToSelectorHolding(holding, holdingType)
|
||||
holdingSelector.setSelectedItem(selectorHolding, holdingType)
|
||||
}
|
||||
|
||||
function setHoveredHoldingId(holdingId, holdingType) {
|
||||
let holding = getHolding(holdingId, holdingType)
|
||||
setHoveredHolding(holding, holdingType)
|
||||
}
|
||||
|
||||
function setHoveredHolding(holding, holdingType) {
|
||||
d.hoveredHoldingType = holdingType
|
||||
d.hoveredHolding = holding
|
||||
let selectorHolding = store.holdingToSelectorHolding(holding, holdingType)
|
||||
holdingSelector.setHoveredItem(selectorHolding, holdingType)
|
||||
}
|
||||
|
||||
onSelectedHoldingChanged: {
|
||||
if (d.selectedHoldingType === Constants.TokenType.ERC20) {
|
||||
if(!d.ensOrStickersPurpose && store.sendType !== Constants.SendType.Bridge)
|
||||
|
@ -173,19 +142,6 @@ StatusDialog {
|
|||
}
|
||||
}
|
||||
|
||||
SendModalAssetsAdaptor {
|
||||
id: assetsAdaptor
|
||||
|
||||
controller: popup.store.walletAssetStore.assetsController
|
||||
showCommunityAssets: popup.store.tokensStore.showCommunityAssetsInSend
|
||||
tokensModel: popup.store.walletAssetStore.groupedAccountAssetsModel
|
||||
account: popup.store.selectedSenderAccountAddress
|
||||
marketValueThreshold:
|
||||
popup.store.tokensStore.displayAssetsBelowBalance
|
||||
? popup.store.tokensStore.getDisplayAssetsBelowBalanceThresholdDisplayAmount()
|
||||
: 0
|
||||
}
|
||||
|
||||
LeftJoinModel {
|
||||
id: fromNetworksRouteModel
|
||||
leftModel: popup.store.fromNetworksRouteModel
|
||||
|
@ -219,11 +175,35 @@ StatusDialog {
|
|||
if(popup.preSelectedSendType !== Constants.SendType.Unknown) {
|
||||
store.setSendType(popup.preSelectedSendType)
|
||||
}
|
||||
if ((popup.preSelectedHoldingType > Constants.TokenType.Native) &&
|
||||
(popup.preSelectedHoldingType < Constants.TokenType.Unknown)) {
|
||||
tokenListRect.browsingHoldingType = popup.preSelectedHoldingType
|
||||
if (!!popup.preSelectedHoldingID) {
|
||||
d.setSelectedHoldingId(popup.preSelectedHoldingID, popup.preSelectedHoldingType)
|
||||
if (!!popup.preSelectedHoldingID
|
||||
&& popup.preSelectedHoldingType > Constants.TokenType.Native
|
||||
&& popup.preSelectedHoldingType < Constants.TokenType.Unknown) {
|
||||
|
||||
if (popup.preSelectedHoldingType === Constants.TokenType.ERC20) {
|
||||
const entry = ModelUtils.getByKey(
|
||||
assetsAdaptor.outputAssetsModel, "tokensKey",
|
||||
popup.preSelectedHoldingID)
|
||||
d.selectedHoldingType = Constants.TokenType.ERC20
|
||||
d.selectedHolding = entry
|
||||
|
||||
holdingSelector.setCustom(entry.symbol, entry.iconSource,
|
||||
popup.preSelectedHoldingID)
|
||||
holdingSelector.selectedItem = entry
|
||||
} else {
|
||||
const entry = ModelUtils.getByKey(
|
||||
popup.store.collectiblesModel,
|
||||
"uid", popup.preSelectedHoldingID)
|
||||
|
||||
d.selectedHoldingType = entry.tokenType
|
||||
d.selectedHolding = entry
|
||||
|
||||
const id = entry.communityId ? entry.collectionUid : entry.uid
|
||||
|
||||
holdingSelector.setCustom(entry.name,
|
||||
entry.imageUrl || entry.mediaUrl,
|
||||
id)
|
||||
holdingSelector.selectedItem = entry
|
||||
holdingSelector.currentTab = TokenSelectorPanel.Tabs.Collectibles
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,9 +247,13 @@ StatusDialog {
|
|||
selectedAddress: !!popup.preSelectedAccount && !!popup.preSelectedAccount.address ? popup.preSelectedAccount.address : ""
|
||||
onCurrentAccountAddressChanged: {
|
||||
store.setSenderAccount(currentAccountAddress)
|
||||
|
||||
if (d.isSelectedHoldingValidAsset) {
|
||||
d.setSelectedHoldingId(d.selectedHolding.symbol, d.selectedHoldingType)
|
||||
d.selectedHolding = ModelUtils.getByKey(
|
||||
holdingSelector.assetsModel, "tokensKey",
|
||||
d.selectedHolding.tokensKey)
|
||||
}
|
||||
|
||||
popup.recalculateRoutesAndFees()
|
||||
}
|
||||
}
|
||||
|
@ -316,25 +300,69 @@ StatusDialog {
|
|||
text: d.isBridgeTx ? qsTr("Bridge") : qsTr("Send")
|
||||
}
|
||||
|
||||
HoldingSelector {
|
||||
TokenSelectorNew {
|
||||
id: holdingSelector
|
||||
|
||||
property var selectedItem
|
||||
property bool onlyAssets: false
|
||||
|
||||
assetsModel: assetsAdaptor.outputAssetsModel
|
||||
collectiblesModel: collectiblesAdaptorLoader.active
|
||||
? collectiblesAdaptorLoader.item.model : null
|
||||
|
||||
TokenSelectorViewAdaptor {
|
||||
id: assetsAdaptor
|
||||
|
||||
assetsModel: popup.store.walletAssetStore.groupedAccountAssetsModel
|
||||
|
||||
flatNetworksModel: popup.store.flatNetworksModel
|
||||
currentCurrency: popup.store.currencyStore.currentCurrency
|
||||
accountAddress: popup.preSelectedAccount ? popup.preSelectedAccount.address : ""
|
||||
showCommunityAssets: popup.store.tokensStore.showCommunityAssetsInSend
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: collectiblesAdaptorLoader
|
||||
|
||||
active: !d.isBridgeTx
|
||||
|
||||
sourceComponent: CollectiblesSelectionAdaptor {
|
||||
accountKey: popup.preSelectedAccount ? popup.preSelectedAccount.address : ""
|
||||
collectiblesModel: collectiblesStore
|
||||
? collectiblesStore.jointCollectiblesBySymbolModel
|
||||
: null
|
||||
}
|
||||
}
|
||||
|
||||
onAssetSelected: {
|
||||
const entry = ModelUtils.getByKey(
|
||||
assetsModel, "tokensKey", key)
|
||||
d.selectedHoldingType = Constants.TokenType.ERC20
|
||||
d.selectedHolding = entry
|
||||
selectedItem = entry
|
||||
}
|
||||
|
||||
onCollectibleSelected: {
|
||||
const entry = ModelUtils.getByKey(
|
||||
popup.store.collectiblesModel,
|
||||
"uid", key)
|
||||
d.selectedHoldingType = entry.tokenType
|
||||
d.selectedHolding = entry
|
||||
selectedItem = entry
|
||||
}
|
||||
|
||||
onCollectionSelected: {
|
||||
const entry = ModelUtils.getByKey(
|
||||
popup.store.collectiblesModel,
|
||||
"collectionUid", key)
|
||||
d.selectedHoldingType = entry.tokenType
|
||||
d.selectedHolding = entry
|
||||
selectedItem = entry
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
assetsModel: assetsAdaptor.model
|
||||
collectiblesModel: popup.preSelectedAccount ? popup.nestedCollectiblesModel : null
|
||||
networksModel: popup.store.flatNetworksModel
|
||||
visible: (!!d.selectedHolding && d.selectedHoldingType !== Constants.TokenType.Unknown) ||
|
||||
(!!d.hoveredHolding && d.hoveredHoldingType !== Constants.TokenType.Unknown)
|
||||
onItemSelected: {
|
||||
d.setSelectedHoldingId(holdingId, holdingType)
|
||||
}
|
||||
onSearchTextChanged: assetsAdaptor.assetSearchString = assetSearchString
|
||||
formatCurrentCurrencyAmount: function(balance){
|
||||
return popup.store.currencyStore.formatCurrencyAmount(balance, popup.store.currencyStore.currentCurrency)
|
||||
}
|
||||
formatCurrencyAmountFromBigInt: function(balance, symbol, decimals){
|
||||
return popup.store.formatCurrencyAmountFromBigInt(balance, symbol, decimals, {noSymbol: true})
|
||||
}
|
||||
}
|
||||
|
||||
MaxSendButton {
|
||||
|
@ -404,7 +432,7 @@ StatusDialog {
|
|||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
visible: !d.isBridgeTx && !!d.selectedHolding
|
||||
visible: !d.isBridgeTx
|
||||
StatusBaseText {
|
||||
id: label
|
||||
elide: Text.ElideRight
|
||||
|
@ -429,39 +457,6 @@ StatusDialog {
|
|||
}
|
||||
}
|
||||
|
||||
TokenListView {
|
||||
id: tokenListRect
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.current.padding
|
||||
Layout.leftMargin: Style.current.xlPadding
|
||||
Layout.rightMargin: Style.current.xlPadding
|
||||
Layout.bottomMargin: Style.current.xlPadding + Style.current.padding
|
||||
visible: !d.selectedHolding
|
||||
|
||||
assets: assetsAdaptor.model
|
||||
collectibles: popup.preSelectedAccount ? popup.nestedCollectiblesModel : null
|
||||
networksModel: popup.store.flatNetworksModel
|
||||
onlyAssets: holdingSelector.onlyAssets
|
||||
onTokenSelected: function (symbolOrTokenKey, holdingType) {
|
||||
d.setSelectedHoldingId(symbolOrTokenKey, holdingType)
|
||||
}
|
||||
onTokenHovered: {
|
||||
if(hovered) {
|
||||
d.setHoveredHoldingId(symbol, holdingType)
|
||||
} else {
|
||||
d.setHoveredHoldingId("", Constants.TokenType.Unknown)
|
||||
}
|
||||
}
|
||||
onAssetSearchStringChanged: assetsAdaptor.assetSearchString = assetSearchString
|
||||
formatCurrentCurrencyAmount: function(balance){
|
||||
return popup.store.currencyStore.formatCurrencyAmount(balance, popup.store.currencyStore.currentCurrency)
|
||||
}
|
||||
formatCurrencyAmountFromBigInt: function(balance, symbol, decimals) {
|
||||
return popup.store.formatCurrencyAmountFromBigInt(balance, symbol, decimals, {noSymbol: true})
|
||||
}
|
||||
}
|
||||
|
||||
RecipientSelectorPanel {
|
||||
id: recipientsPanel
|
||||
|
||||
|
@ -472,9 +467,7 @@ StatusDialog {
|
|||
Layout.rightMargin: Style.current.xlPadding
|
||||
Layout.bottomMargin: Style.current.padding
|
||||
|
||||
// TODO: To be removed after all other refactors done (initial tokens selector page removed, bridge modal separated)
|
||||
// This panel must be shown by default if no recipient already selected, otherwise, hidden
|
||||
visible: !recipientInputLoader.ready && !d.isBridgeTx && !!d.selectedHolding
|
||||
visible: !recipientInputLoader.ready && !d.isBridgeTx
|
||||
|
||||
savedAddressesModel: popup.store.savedAddressesModel
|
||||
myAccountsModel: d.accountsAdaptor.model
|
||||
|
@ -513,7 +506,8 @@ StatusDialog {
|
|||
|
||||
contentWidth: availableWidth
|
||||
|
||||
visible: recipientInputLoader.ready && !!d.selectedHolding && (amountToSendInput.inputNumberValid || d.isCollectiblesTransfer)
|
||||
visible: recipientInputLoader.ready &&
|
||||
(amountToSendInput.inputNumberValid || d.isCollectiblesTransfer)
|
||||
|
||||
objectName: "sendModalScroll"
|
||||
|
||||
|
|
|
@ -83,6 +83,8 @@ QObject {
|
|||
ObjectProxyModel {
|
||||
id: proxyModel
|
||||
|
||||
objectName: "sendModalAssetsAdaptor_proxyModel"
|
||||
|
||||
sourceModel: root.tokensModel ?? null
|
||||
|
||||
delegate: QObject {
|
||||
|
|
|
@ -180,7 +180,7 @@ Loader {
|
|||
SendRecipientInput {
|
||||
width: parent.width
|
||||
height: visible ? implicitHeight: 0
|
||||
visible: !root.isBridgeTx && !!root.selectedAsset
|
||||
visible: !root.isBridgeTx
|
||||
text: root.addressText
|
||||
|
||||
function validateInput() {
|
||||
|
|
Loading…
Reference in New Issue