From bc74724672459005bf1afb5d56262b4549525b6e Mon Sep 17 00:00:00 2001 From: Noelia Date: Wed, 3 Jul 2024 22:46:00 +0200 Subject: [PATCH] refactor(Wallet/SendModal): Created `WalletAccountsAdaptor` to be used in `SendModal` - Created data transformation file called `WalletAccountsAdaptor` for `SendModal`. Now it contains the owned wallet accounts model data transformations. - Added common formatting method for short chain ids to some utils and partially removed backend dependency. --- .../main/wallet_section/networks/model.nim | 1 + storybook/pages/DAppsWorkflowPage.qml | 3 +- .../qmlTests/tests/tst_DAppsWorkflow.qml | 3 +- storybook/qmlTests/tests/tst_SwapModal.qml | 2 +- .../AppLayouts/Wallet/stores/RootStore.qml | 8 ++ .../stubs/AppLayouts/Wallet/stores/qmldir | 2 +- ui/app/AppLayouts/Wallet/WalletUtils.qml | 46 ++++++++++- .../Wallet/adaptors/WalletAccountsAdaptor.qml | 80 +++++++++++++++++++ ui/app/AppLayouts/Wallet/adaptors/qmldir | 1 + .../Wallet/popups/swap/SwapModalAdaptor.qml | 16 +--- .../services/dapps/WalletConnectService.qml | 3 +- ui/imports/shared/popups/send/SendModal.qml | 22 ++--- 12 files changed, 150 insertions(+), 37 deletions(-) create mode 100644 ui/app/AppLayouts/Wallet/adaptors/WalletAccountsAdaptor.qml diff --git a/src/app/modules/main/wallet_section/networks/model.nim b/src/app/modules/main/wallet_section/networks/model.nim index 6acab13c92..5f8cd93972 100644 --- a/src/app/modules/main/wallet_section/networks/model.nim +++ b/src/app/modules/main/wallet_section/networks/model.nim @@ -170,6 +170,7 @@ QtObject: return (chainIds, enable) + # TODO: To be removed once all qml calls to this method are removed. Normally the formatting methods should live in the qml project. proc getNetworkShortNames*(self: Model, preferredNetworks: string, areTestNetworksEnabled: bool): string = var networkString = "" let networks = preferredNetworks.split(":") diff --git a/storybook/pages/DAppsWorkflowPage.qml b/storybook/pages/DAppsWorkflowPage.qml index e26c26db99..f278db1b70 100644 --- a/storybook/pages/DAppsWorkflowPage.qml +++ b/storybook/pages/DAppsWorkflowPage.qml @@ -18,7 +18,6 @@ import Models 1.0 import Storybook 1.0 import AppLayouts.Wallet.controls 1.0 -import AppLayouts.Wallet.stores 1.0 as WalletStores import AppLayouts.Wallet.services.dapps 1.0 import SortFilterProxyModel 0.2 @@ -373,7 +372,7 @@ Item { } } - walletRootStore: WalletStores.RootStore { + walletRootStore: QObject { property string selectedAddress: "" property var filteredFlatModel: SortFilterProxyModel { sourceModel: NetworksModel.flatNetworks diff --git a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml index 7e72901d9d..d7daed93fc 100644 --- a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml +++ b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml @@ -14,7 +14,6 @@ import AppLayouts.Wallet.services.dapps 1.0 import AppLayouts.Wallet.services.dapps.types 1.0 import AppLayouts.Profile.stores 1.0 import AppLayouts.Wallet.panels 1.0 -import AppLayouts.Wallet.stores 1.0 as WalletStores import shared.stores 1.0 @@ -121,7 +120,7 @@ Item { Component { id: walletStoreComponent - WalletStores.RootStore { + QtObject { property string selectedAddress: "" readonly property ListModel filteredFlatModel: ListModel { ListElement { chainId: 1 } diff --git a/storybook/qmlTests/tests/tst_SwapModal.qml b/storybook/qmlTests/tests/tst_SwapModal.qml index e5e8a8624e..43ec27e102 100644 --- a/storybook/qmlTests/tests/tst_SwapModal.qml +++ b/storybook/qmlTests/tests/tst_SwapModal.qml @@ -261,7 +261,7 @@ Item { mouseMove(delegateUnderTest, delegateUnderTest.width/2, delegateUnderTest.height/2) verify(delegateUnderTest.sensor.containsMouse) compare(delegateUnderTest.title, swapAdaptor.nonWatchAccounts.get(i).name) - compare(delegateUnderTest.subTitle, WalletUtils.colorizedChainPrefix(root.swapAdaptor.getNetworkShortNames(swapAdaptor.nonWatchAccounts.get(i).preferredSharingChainIds)), "Randomly failing locally. Add a bug if you see this failing in CI") + compare(delegateUnderTest.subTitle, WalletUtils.colorizedChainPrefix(WalletUtils.getNetworkShortNames(swapAdaptor.nonWatchAccounts.get(i).preferredSharingChainIds, root.swapStore.flatNetworks)), "Randomly failing locally. Add a bug if you see this failing in CI") verify(delegateUnderTest.color, Theme.palette.baseColor2) } diff --git a/storybook/stubs/AppLayouts/Wallet/stores/RootStore.qml b/storybook/stubs/AppLayouts/Wallet/stores/RootStore.qml index c80bf1275e..2b096ec61a 100644 --- a/storybook/stubs/AppLayouts/Wallet/stores/RootStore.qml +++ b/storybook/stubs/AppLayouts/Wallet/stores/RootStore.qml @@ -1,5 +1,13 @@ +// This should not be a singleton. TODO: Remove it once the "real" Wallet root store is not a singleton anymore. +pragma Singleton + import QtQml 2.15 QtObject { id: root + + // TODO: Remove this. This stub should be empty. The color transformation should be done in adaptors or in the first model transformation steps. + function colorForChainShortName(chainShortName) { + return "#FF0000" // Just some random testing color + } } diff --git a/storybook/stubs/AppLayouts/Wallet/stores/qmldir b/storybook/stubs/AppLayouts/Wallet/stores/qmldir index 2ef284383d..ff6a31b584 100644 --- a/storybook/stubs/AppLayouts/Wallet/stores/qmldir +++ b/storybook/stubs/AppLayouts/Wallet/stores/qmldir @@ -1,6 +1,6 @@ ActivityFiltersStore 1.0 ActivityFiltersStore.qml CollectiblesStore 1.0 CollectiblesStore.qml -RootStore 1.0 RootStore.qml +singleton RootStore 1.0 RootStore.qml SwapStore 1.0 SwapStore.qml TokensStore 1.0 TokensStore.qml WalletAssetsStore 1.0 WalletAssetsStore.qml diff --git a/ui/app/AppLayouts/Wallet/WalletUtils.qml b/ui/app/AppLayouts/Wallet/WalletUtils.qml index f26652a425..2f28f43652 100644 --- a/ui/app/AppLayouts/Wallet/WalletUtils.qml +++ b/ui/app/AppLayouts/Wallet/WalletUtils.qml @@ -7,9 +7,39 @@ import StatusQ.Core.Theme 0.1 import StatusQ.Core.Utils 0.1 as StatusQUtils -import "stores" as WalletStores +import AppLayouts.Wallet.stores 1.0 as WalletStores QtObject { + + function colorizedChainPrefixNew(chainColors, prefix) { + if (!prefix) + return "" + + const prefixes = prefix.split(":").filter(Boolean) + let prefixStr = "" + const lastPrefixEndsWithColumn = prefix.endsWith(":") + const defaultColor = Theme.palette.baseColor1 + + for (let i in prefixes) { + const pref = prefixes[i] + let col = chainColors[pref] + if (!col) + col = defaultColor + + prefixStr += Utils.richColorText(pref, col) + // Avoid adding ":" if it was not there for the last prefix, + // because when user manually edits the address, it breaks editing + if (!(i === (prefixes.length - 1) && !lastPrefixEndsWithColumn)) { + prefixStr += Utils.richColorText(":", Theme.palette.baseColor1) + } + } + + return prefixStr + } + + // TODO: Remove dependency to RootStore by requesting model or chainColors as a parameter. Indeed, this + // method should be just replaced by `colorizedChainPrefixNew` + // Issue #15494 function colorizedChainPrefix(prefix) { if (!prefix) return "" @@ -28,7 +58,7 @@ QtObject { prefixStr += Utils.richColorText(pref, col) // Avoid adding ":" if it was not there for the last prefix, // because when user manually edits the address, it breaks editing - if (!(i == (prefixes.length - 1) && !lastPrefixEndsWithColumn)) { + if (!(i === (prefixes.length - 1) && !lastPrefixEndsWithColumn)) { prefixStr += Utils.richColorText(":", Theme.palette.baseColor1) } } @@ -88,4 +118,16 @@ QtObject { return qsTr("> 5 minutes") } } + + // Where: chainIds [string] - separated by `:`, e.g "42161:10:1" + function getNetworkShortNames(chainIds: string, flatNetworksModel) { + let networkString = "" + const chainIdsArray = chainIds.split(":") + for (let i = 0; i < chainIdsArray.length; i++) { + const nwShortName = StatusQUtils.ModelUtils.getByKey(flatNetworksModel, "chainId", Number(chainIdsArray[i]), "shortName") + if (!!nwShortName) + networkString = networkString + nwShortName + ':' + } + return networkString + } } diff --git a/ui/app/AppLayouts/Wallet/adaptors/WalletAccountsAdaptor.qml b/ui/app/AppLayouts/Wallet/adaptors/WalletAccountsAdaptor.qml new file mode 100644 index 0000000000..e69376bde2 --- /dev/null +++ b/ui/app/AppLayouts/Wallet/adaptors/WalletAccountsAdaptor.qml @@ -0,0 +1,80 @@ +import QtQml 2.15 + +import AppLayouts.Wallet 1.0 + +import StatusQ 0.1 +import StatusQ.Core.Utils 0.1 + +import SortFilterProxyModel 0.2 + +QObject { + id: root + + // Input parameters: + /** + Expected model structure: + address [string] - wallet account address, e.g "0x10b...eaf" + name [string] - wallet account name, e.g "Status account" + keyUid [string] - unique identifier, e.g "0x79e07.....006" + currencyBalance [var] - CurrencyAmount type + amount [string] + symbol [string] + displayDecimals [int] + stripTrailingZeroes [bool] + emoji [string] - custom emoji + walletType [string] - e.g "generated" + colorId [string] - e.g "YELLOW" + preferredSharingChainIds [string] - separated by `:`, e.g "42161:10:1" + position [int] - visual order, e.g: "1" + **/ + property var accountsModel + + property var flatNetworksModel + property bool areTestNetworksEnabled + + // Output parameters: + /** + Model structure: + + All roles from the source model are passed directly to the output model, + additionally: + colorizedChainShortNames [string] - build from `preferredSharingChainIds` adding different colors to different network short names + **/ + readonly property alias model: sfpm + + readonly property SortFilterProxyModel filteredFlatNetworksModel: SortFilterProxyModel { + sourceModel: root.flatNetworksModel + filters: ValueFilter { roleName: "isTest"; value: root.areTestNetworksEnabled } + } + + QtObject { + id: d + + property var chainColors: ({}) + + function initChainColors() { + for (let i = 0; i < root.flatNetworksModel.count; i++) { + const item = ModelUtils.get(root.flatNetworksModel, i) + chainColors[item.shortName] = item.chainColor + } + } + } + + SortFilterProxyModel { + id: sfpm + sourceModel: root.accountsModel ?? null + + proxyRoles: FastExpressionRole { + function getChainShortNames(preferredSharingChainIds){ + const chainShortNames = WalletUtils.getNetworkShortNames(preferredSharingChainIds, root.flatNetworksModel) + return WalletUtils.colorizedChainPrefixNew(d.chainColors, chainShortNames) + } + + name: "colorizedChainShortNames" + expectedRoles: ["preferredSharingChainIds"] + expression: getChainShortNames(model.preferredSharingChainIds) + } + } + + onFlatNetworksModelChanged: d.initChainColors() +} diff --git a/ui/app/AppLayouts/Wallet/adaptors/qmldir b/ui/app/AppLayouts/Wallet/adaptors/qmldir index ed8eee3848..23c26df886 100644 --- a/ui/app/AppLayouts/Wallet/adaptors/qmldir +++ b/ui/app/AppLayouts/Wallet/adaptors/qmldir @@ -1,2 +1,3 @@ CollectiblesSelectionAdaptor 1.0 CollectiblesSelectionAdaptor.qml TokenSelectorViewAdaptor 1.0 TokenSelectorViewAdaptor.qml +WalletAccountsAdaptor 1.0 WalletAccountsAdaptor.qml diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml index ba949eeac8..f2ab3c811d 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml @@ -33,6 +33,8 @@ QObject { readonly property string uuid: d.uuid + // TO REVIEW: It has been created a `WalletAccountsAdaptor.qml` file. + // Probably this data transformation should live there since they have common base. readonly property var nonWatchAccounts: SortFilterProxyModel { sourceModel: root.swapStore.accounts filters: ValueFilter { @@ -62,7 +64,7 @@ QObject { FastExpressionRole { name: "colorizedChainPrefixes" function getChainShortNames(chainIds) { - const chainShortNames = root.getNetworkShortNames(chainIds) + const chainShortNames = WalletUtils.getNetworkShortNames(chainIds, root.filteredFlatNetworksModel) return WalletUtils.colorizedChainPrefix(chainShortNames) } expression: getChainShortNames(model.preferredSharingChainIds) @@ -217,18 +219,6 @@ QObject { d.txHash = "" } - function getNetworkShortNames(chainIds) { - var networkString = "" - let chainIdsArray = chainIds.split(":") - for (let i = 0; i< chainIdsArray.length; i++) { - let nwShortName = ModelUtils.getByKey(root.filteredFlatNetworksModel, "chainId", Number(chainIdsArray[i]), "shortName") - if(!!nwShortName) { - networkString = networkString + nwShortName + ':' - } - } - return networkString - } - function formatCurrencyAmount(balance, symbol, options = null, locale = null) { return root.currencyStore.formatCurrencyAmount(balance, symbol, options, locale) } diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml index 299682629b..488d1a9f09 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml @@ -5,7 +5,6 @@ import StatusQ.Core.Theme 0.1 import StatusQ.Core.Utils 0.1 import AppLayouts.Wallet 1.0 -import AppLayouts.Wallet.stores 1.0 as WalletStores import AppLayouts.Wallet.services.dapps 1.0 import AppLayouts.Wallet.services.dapps.types 1.0 import AppLayouts.Profile.stores 1.0 @@ -31,7 +30,7 @@ QObject { required property WalletConnectSDKBase wcSDK required property DAppsStore store - required property WalletStores.RootStore walletRootStore + required property var walletRootStore readonly property string selectedAccountAddress: walletRootStore.selectedAddress diff --git a/ui/imports/shared/popups/send/SendModal.qml b/ui/imports/shared/popups/send/SendModal.qml index 46ec8ef1b1..693ce5024f 100644 --- a/ui/imports/shared/popups/send/SendModal.qml +++ b/ui/imports/shared/popups/send/SendModal.qml @@ -21,6 +21,7 @@ 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 shared.popups.send.panels 1.0 import "./controls" @@ -80,6 +81,12 @@ StatusDialog { QtObject { id: d + readonly property WalletAccountsAdaptor accountsAdaptor: WalletAccountsAdaptor { + accountsModel: popup.store.accounts + flatNetworksModel: popup.store.flatNetworksModel + areTestNetworksEnabled: popup.store.areTestNetworksEnabled + } + property bool ensOrStickersPurpose: popup.preSelectedSendType === Constants.SendType.ENSRegister || popup.preSelectedSendType === Constants.SendType.ENSRelease || popup.preSelectedSendType === Constants.SendType.ENSSetPubKey || @@ -470,20 +477,7 @@ StatusDialog { visible: !recipientInputLoader.ready && !d.isBridgeTx && !!d.selectedHolding savedAddressesModel: popup.store.savedAddressesModel - myAccountsModel: SortFilterProxyModel { - sourceModel: popup.store.accounts - - proxyRoles: FastExpressionRole { - function getColorizedChainShortNames(preferredSharingChainIds, address){ - const chainShortNames = popup.store.getNetworkShortNames(preferredSharingChainIds) - return WalletUtils.colorizedChainPrefix(chainShortNames) + address - } - - name: "colorizedChainShortNames" - expectedRoles: ["preferredSharingChainIds", "address"] - expression: getColorizedChainShortNames(model.preferredSharingChainIds, model.address) - } - } + myAccountsModel: d.accountsAdaptor.model recentRecipientsModel: popup.store.tempActivityController1Model // Use Layer1 controller since this could go on top of other activity lists onRecipientSelected: {