From dfd1008a4cb51d364f7aa0ada458eae17ff0745c Mon Sep 17 00:00:00 2001 From: Anastasiya Date: Mon, 20 Jul 2026 15:56:01 +0300 Subject: [PATCH] tests(@qml): cover wallet account reordering --- .qmlls.ini | 2 + .../qmlTests/tests/tst_AccountOrderSync.qml | 197 ++++++++++++++++++ .../qmlTests/tests/tst_AccountOrderView.qml | 167 +++++++++++++++ storybook/qmlTests/tests/tst_LeftTabView.qml | 127 +++++++++++ .../stubs/nim/sectionmocks/WalletSection.qml | 2 + .../AppLayouts/Profile/views/WalletView.qml | 4 +- .../Profile/views/wallet/AccountOrderView.qml | 17 +- ui/app/AppLayouts/Wallet/WalletLayout.qml | 59 +++++- .../AppLayouts/Wallet/views/LeftTabView.qml | 103 ++++----- .../Wallet/views/LeftTabViewState.qml | 17 ++ ui/app/AppLayouts/Wallet/views/qmldir | 2 + 11 files changed, 622 insertions(+), 75 deletions(-) create mode 100644 .qmlls.ini create mode 100644 storybook/qmlTests/tests/tst_AccountOrderSync.qml create mode 100644 storybook/qmlTests/tests/tst_AccountOrderView.qml create mode 100644 storybook/qmlTests/tests/tst_LeftTabView.qml create mode 100644 ui/app/AppLayouts/Wallet/views/LeftTabViewState.qml diff --git a/.qmlls.ini b/.qmlls.ini new file mode 100644 index 0000000000..53feadb7f2 --- /dev/null +++ b/.qmlls.ini @@ -0,0 +1,2 @@ +[General] +AdditionalImportPaths=ui,ui/imports,ui/app,ui/StatusQ/src,storybook/stubs diff --git a/storybook/qmlTests/tests/tst_AccountOrderSync.qml b/storybook/qmlTests/tests/tst_AccountOrderSync.qml new file mode 100644 index 0000000000..bdf277b0d5 --- /dev/null +++ b/storybook/qmlTests/tests/tst_AccountOrderSync.qml @@ -0,0 +1,197 @@ +import QtQuick +import QtTest + +import AppLayouts.Profile.views.wallet +import AppLayouts.Wallet.views + +import StatusQ.Core.Theme + +import utils + +Item { + id: root + + width: 800 + height: 600 + + ListModel { + id: accountsModel + } + + LeftTabViewState { + id: panelState + + accountsModel: accountsModel + totalCurrencyBalance: ({ + amount: 0, + symbol: "USD", + displayDecimals: 2, + stripTrailingZeroes: false + }) + balanceLoading: false + selectedAddress: "" + } + + Component { + id: componentUnderTest + + Item { + id: wrapper + + property var accountsModel + + width: parent.width + height: parent.height + + AccountOrderView { + id: accountOrderView + objectName: "accountOrderView" + + anchors.top: parent.top + anchors.left: parent.left + width: parent.width - walletView.width + accountsModel: wrapper.accountsModel + } + + LeftTabView { + id: walletView + objectName: "walletLeftTab" + + anchors { + top: parent.top + bottom: parent.bottom + right: parent.right + } + width: 300 + viewState: panelState + } + } + } + + TestCase { + name: "AccountOrderSync" + when: windowShown + + property var controlUnderTest: null + + function account(name, emoji, colorId, position) { + return { + name, + address: "0x%1".arg(position.toString().padStart(40, "0")), + emoji, + colorId, + position, + walletType: "", + migratedToColdWallet: false, + currencyBalance: ({ + amount: 0, + symbol: "USD", + displayDecimals: 2, + stripTrailingZeroes: false + }), + assetsLoading: false, + hideFromTotalBalance: false + } + } + + function syncPositions() { + for (let i = 0; i < accountsModel.count; ++i) + accountsModel.setProperty(i, "position", i) + } + + function accountOrderView() { + return findChild(controlUnderTest, "accountOrderView") + } + + function walletView() { + return findChild(controlUnderTest, "walletLeftTab") + } + + function delegateAt(index) { + return findChild(accountOrderView(), "accountOrderDelegate-%1".arg(index)) + } + + function verifyWalletOrder(expectedTitles) { + const listView = findChild(walletView(), "walletAccountsListView") + verify(!!listView) + waitForRendering(listView) + tryVerify(() => { + if (listView.count !== expectedTitles.length) + return false + for (let i = 0; i < expectedTitles.length; ++i) { + const item = listView.itemAtIndex(i) + if (!item || item.title !== expectedTitles[i]) + return false + } + return true + }) + } + + function verifyOrder(expectedTitles) { + for (let i = 0; i < expectedTitles.length; ++i) + tryCompare(delegateAt(i), "title", expectedTitles[i]) + verifyWalletOrder(expectedTitles) + } + + function cleanup() { + if (!!controlUnderTest) + controlUnderTest.destroy() + controlUnderTest = null + } + + function createView(accountData) { + cleanup() + + accountsModel.clear() + for (let i = 0; i < accountData.length; ++i) + accountsModel.append(accountData[i]) + + controlUnderTest = createTemporaryObject(componentUnderTest, root, { + width: root.width, + height: root.height, + accountsModel: accountsModel + }) + verify(!!controlUnderTest) + + const orderView = accountOrderView() + verify(!!orderView) + + orderView.moveAccountRequested.connect(function(from, to) { + accountsModel.move(from, to, 1) + }) + orderView.moveAccountFinallyRequested.connect(function() { + syncPositions() + }) + + waitForRendering(controlUnderTest) + + const accountsList = findChild(orderView, "accountOrderList") + verify(!!accountsList) + tryCompare(accountsList, "count", accountData.length) + } + + function test_dragInAccountOrderViewReflectsInLeftTabView() { + createView([ + account("Account 1", "😀", Constants.walletAccountColors.primary, 0), + account("Generated 1", "😎", Constants.walletAccountColors.army, 1), + account("Generated 2", "👍", Constants.walletAccountColors.magenta, 2) + ]) + + verifyOrder(["Account 1", "Generated 1", "Generated 2"]) + + let delegate = delegateAt(0) + waitForRendering(delegate) + mouseDrag(delegate, delegate.width / 2, delegate.height / 2, + 0, delegate.height * 2) + + verifyOrder(["Generated 1", "Generated 2", "Account 1"]) + + delegate = delegateAt(1) + waitForRendering(delegate) + mouseDrag(delegate, delegate.width / 2, delegate.height / 2, + 0, -delegate.height) + + verifyOrder(["Generated 2", "Generated 1", "Account 1"]) + } + } +} diff --git a/storybook/qmlTests/tests/tst_AccountOrderView.qml b/storybook/qmlTests/tests/tst_AccountOrderView.qml new file mode 100644 index 0000000000..96478fecfa --- /dev/null +++ b/storybook/qmlTests/tests/tst_AccountOrderView.qml @@ -0,0 +1,167 @@ +import QtQuick +import QtTest + +import AppLayouts.Profile.views.wallet + +import StatusQ.Core.Theme + +import utils + +Item { + id: root + + width: 600 + height: 600 + + ListModel { + id: accountsModel + } + + Component { + id: componentUnderTest + + AccountOrderView { + objectName: "accountOrderView" + } + } + + SignalSpy { + id: moveAccountRequestedSpy + signalName: "moveAccountRequested" + } + + SignalSpy { + id: moveAccountFinallyRequestedSpy + signalName: "moveAccountFinallyRequested" + } + + TestCase { + name: "AccountOrderView" + when: windowShown + + property var controlUnderTest: null + + readonly property string lonelyText: + "This account looks a little lonely. Add another account to enable re-ordering." + + function account(name, emoji, colorId, position) { + return { + name, + address: "0x%1".arg(position.toString().padStart(40, "0")), + emoji, + colorId, + position, + walletType: "", + migratedToColdWallet: false + } + } + + function cleanup() { + if (!!controlUnderTest) + controlUnderTest.destroy() + controlUnderTest = null + moveAccountRequestedSpy.clear() + moveAccountFinallyRequestedSpy.clear() + } + + function verifyOrder(expectedTitles) { + for (let i = 0; i < expectedTitles.length; ++i) + tryCompare(delegateAt(i), "title", expectedTitles[i]) + } + + function delegateAt(index) { + return findChild(controlUnderTest, "accountOrderDelegate-%1".arg(index)) + } + + function createView(accountData) { + cleanup() + + accountsModel.clear() + for (let i = 0; i < accountData.length; ++i) + accountsModel.append(accountData[i]) + + controlUnderTest = createTemporaryObject(componentUnderTest, root, { + width: root.width, + accountsModel: accountsModel + }) + controlUnderTest.moveAccountRequested.connect(function(from, to) { + accountsModel.move(from, to, 1) + }) + verify(!!controlUnderTest) + + moveAccountRequestedSpy.target = controlUnderTest + moveAccountFinallyRequestedSpy.target = controlUnderTest + + waitForRendering(controlUnderTest) + + compare(accountsModel.count, accountData.length) + + const accountsList = findChild(controlUnderTest, "accountOrderList") + verify(!!accountsList) + tryCompare(accountsList, "count", accountData.length) + } + + function test_changeAccountOrderByDragAndDrop() { + createView([ + account("Account 1", "😀", Constants.walletAccountColors.primary, 0), + account("Generated 1", "😎", Constants.walletAccountColors.army, 1), + account("Generated 2", "👍", Constants.walletAccountColors.magenta, 2) + ]) + + verifyOrder(["Account 1", "Generated 1", "Generated 2"]) + compare(delegateAt(1).icon.name, "😎") + compare(delegateAt(2).icon.name, "👍") + compare(delegateAt(1).icon.color, + Utils.getColorForId(Theme.palette, Constants.walletAccountColors.army)) + compare(delegateAt(2).icon.color, + Utils.getColorForId(Theme.palette, Constants.walletAccountColors.magenta)) + + let delegate = delegateAt(0) + waitForRendering(delegate) + mouseDrag(delegate, delegate.width / 2, delegate.height / 2, + 0, delegate.height * 2) + + verifyOrder(["Generated 1", "Generated 2", "Account 1"]) + verify(moveAccountRequestedSpy.count > 0) + compare(moveAccountFinallyRequestedSpy.count, 1) + compare(moveAccountFinallyRequestedSpy.signalArguments[0][0], 0) + compare(moveAccountFinallyRequestedSpy.signalArguments[0][1], 2) + + const moveCallsAfterFirstDrag = moveAccountRequestedSpy.count + moveAccountFinallyRequestedSpy.clear() + + delegate = delegateAt(1) + waitForRendering(delegate) + mouseDrag(delegate, delegate.width / 2, delegate.height / 2, + 0, -delegate.height) + + verifyOrder(["Generated 2", "Generated 1", "Account 1"]) + verify(moveAccountRequestedSpy.count > moveCallsAfterFirstDrag) + compare(moveAccountFinallyRequestedSpy.count, 1) + compare(moveAccountFinallyRequestedSpy.signalArguments[0][0], 1) + compare(moveAccountFinallyRequestedSpy.signalArguments[0][1], 0) + } + + function test_changeAccountOrderNotPossible() { + createView([ + account("Account 1", "😀", Constants.walletAccountColors.primary, 0) + ]) + + const recommendation = findChild(controlUnderTest, "accountOrderRecommendation") + verify(!!recommendation) + compare(recommendation.text, lonelyText) + + const delegate = delegateAt(0) + verify(!!delegate) + compare(delegate.title, "Account 1") + compare(delegate.draggable, false) + + mouseDrag(delegate, delegate.width / 2, delegate.height / 2, + 0, delegate.height) + + compare(delegateAt(0).title, "Account 1") + compare(moveAccountRequestedSpy.count, 0) + compare(moveAccountFinallyRequestedSpy.count, 0) + } + } +} diff --git a/storybook/qmlTests/tests/tst_LeftTabView.qml b/storybook/qmlTests/tests/tst_LeftTabView.qml new file mode 100644 index 0000000000..af7cea53d4 --- /dev/null +++ b/storybook/qmlTests/tests/tst_LeftTabView.qml @@ -0,0 +1,127 @@ +import QtQuick +import QtTest + +import AppLayouts.Wallet.views + +import utils + +Item { + id: root + + width: 600 + height: 600 + + ListModel { + id: accountsModel + } + + LeftTabViewState { + id: leftTabViewState + + accountsModel: accountsModel + totalCurrencyBalance: ({ + amount: 0, + symbol: "USD", + displayDecimals: 2, + stripTrailingZeroes: false + }) + balanceLoading: false + selectedAddress: "" + } + + Component { + id: componentUnderTest + + LeftTabView { + objectName: "walletLeftTab" + + anchors.fill: parent + viewState: leftTabViewState + } + } + + TestCase { + name: "LeftTabView" + when: windowShown + + property var controlUnderTest: null + + function account(name, emoji, colorId, position) { + return { + name, + address: "0x%1".arg(position.toString().padStart(40, "0")), + emoji, + colorId, + position, + walletType: "", + migratedToColdWallet: false, + currencyBalance: ({ + amount: 0, + symbol: "USD", + displayDecimals: 2, + stripTrailingZeroes: false + }), + assetsLoading: false, + hideFromTotalBalance: false + } + } + + function cleanup() { + if (!!controlUnderTest) { + controlUnderTest.destroy() + controlUnderTest = null + } + } + + function syncPositions() { + for (let i = 0; i < accountsModel.count; ++i) + accountsModel.setProperty(i, "position", i) + } + + function verifyWalletOrder(expectedTitles) { + const listView = findChild(controlUnderTest, "walletAccountsListView") + verify(!!listView) + waitForRendering(listView) + tryVerify(() => { + if (listView.count !== expectedTitles.length) + return false + for (let i = 0; i < expectedTitles.length; ++i) { + const item = listView.itemAtIndex(i) + if (!item || item.title !== expectedTitles[i]) + return false + } + return true + }) + } + + function createView(accountData) { + cleanup() + + accountsModel.clear() + for (let i = 0; i < accountData.length; ++i) + accountsModel.append(accountData[i]) + + controlUnderTest = createTemporaryObject(componentUnderTest, root) + verify(!!controlUnderTest) + waitForRendering(controlUnderTest) + } + + function test_reflectsAccountsModelOrder() { + createView([ + account("Account 1", "😀", Constants.walletAccountColors.primary, 0), + account("Generated 1", "😎", Constants.walletAccountColors.army, 1), + account("Generated 2", "👍", Constants.walletAccountColors.magenta, 2) + ]) + + verifyWalletOrder(["Account 1", "Generated 1", "Generated 2"]) + + accountsModel.move(0, 2, 1) + syncPositions() + verifyWalletOrder(["Generated 1", "Generated 2", "Account 1"]) + + accountsModel.move(1, 0, 1) + syncPositions() + verifyWalletOrder(["Generated 2", "Generated 1", "Account 1"]) + } + } +} diff --git a/storybook/stubs/nim/sectionmocks/WalletSection.qml b/storybook/stubs/nim/sectionmocks/WalletSection.qml index 9788bc7ade..8ea8beae74 100644 --- a/storybook/stubs/nim/sectionmocks/WalletSection.qml +++ b/storybook/stubs/nim/sectionmocks/WalletSection.qml @@ -16,5 +16,7 @@ Item { } readonly property ListModel mixedcaseAddress: ListModel {} + signal displayAddAccountPopup + signal destroyAddAccountPopup signal walletAccountRemoved(string address) } \ No newline at end of file diff --git a/ui/app/AppLayouts/Profile/views/WalletView.qml b/ui/app/AppLayouts/Profile/views/WalletView.qml index f1038e5cd9..a157d368e0 100644 --- a/ui/app/AppLayouts/Profile/views/WalletView.qml +++ b/ui/app/AppLayouts/Profile/views/WalletView.qml @@ -344,7 +344,9 @@ SettingsContentBase { Layout.fillWidth: true Layout.leftMargin: Theme.padding Layout.rightMargin: Theme.padding - walletStore: root.walletStore + accountsModel: root.walletStore.accounts + onMoveAccountRequested: (from, to) => root.walletStore.moveAccount(from, to) + onMoveAccountFinallyRequested: (from, to) => root.walletStore.moveAccountFinally(from, to) onGoBack: priv.navigateToDetails(root.mainViewIndex) } diff --git a/ui/app/AppLayouts/Profile/views/wallet/AccountOrderView.qml b/ui/app/AppLayouts/Profile/views/wallet/AccountOrderView.qml index e9aaf1d528..c6dead576a 100644 --- a/ui/app/AppLayouts/Profile/views/wallet/AccountOrderView.qml +++ b/ui/app/AppLayouts/Profile/views/wallet/AccountOrderView.qml @@ -14,15 +14,16 @@ import AppLayouts.Wallet import utils -import "../../stores" import "../../controls" ColumnLayout { id: root - property WalletStore walletStore + required property var accountsModel signal goBack + signal moveAccountRequested(int from, int to) + signal moveAccountFinallyRequested(int from, int to) spacing: Theme.padding @@ -35,6 +36,8 @@ ColumnLayout { } StatusBaseText { + objectName: "accountOrderRecommendation" + Layout.fillWidth: true text: accountsList.count > 1? qsTr("Move your most frequently used accounts to the top of your wallet list") : qsTr("This account looks a little lonely. Add another account to enable re-ordering.") @@ -43,10 +46,12 @@ ColumnLayout { StatusListView { id: accountsList + objectName: "accountOrderList" + Layout.fillWidth: true Layout.preferredHeight: contentHeight interactive: false - model: walletStore.accounts + model: root.accountsModel displaced: Transition { NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad } @@ -70,12 +75,14 @@ ColumnLayout { if (d.indexMoveFrom === -1) d.indexMoveFrom = from d.indexMoveTo = to - root.walletStore.moveAccount(from, to) + root.moveAccountRequested(from, to) drag.accept() } StatusDraggableListItem { id: draggableDelegate + objectName: "accountOrderDelegate-%1".arg(index) + anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter width: parent.width @@ -100,7 +107,7 @@ ColumnLayout { let to = d.indexMoveTo d.indexMoveFrom = -1 d.indexMoveTo = -1 - root.walletStore.moveAccountFinally(from, to) + root.moveAccountFinallyRequested(from, to) } } } diff --git a/ui/app/AppLayouts/Wallet/WalletLayout.qml b/ui/app/AppLayouts/Wallet/WalletLayout.qml index 7ba61252ac..6531421171 100644 --- a/ui/app/AppLayouts/Wallet/WalletLayout.qml +++ b/ui/app/AppLayouts/Wallet/WalletLayout.qml @@ -10,6 +10,7 @@ import StatusQ.Core.Theme import utils import shared.controls +import shared.popups.addaccount import shared.popups.keypairimport import shared.stores as SharedStores @@ -106,6 +107,14 @@ Item { function onDestroyKeypairImportPopup() { keypairImport.active = false } + + function onDisplayAddAccountPopup() { + addAccount.active = true + } + + function onDestroyAddAccountPopup() { + addAccount.active = false + } } enum LeftPanelSelection { @@ -329,6 +338,19 @@ Item { } } + LeftTabViewState { + id: leftPanelState + + accountsModel: root.walletRootStore.accounts + selectedAddress: root.walletRootStore.selectedAddress + showSavedAddresses: root.walletRootStore.showSavedAddresses + showFollowingAddresses: root.walletRootStore.showFollowingAddresses + totalCurrencyBalance: root.walletRootStore.totalCurrencyBalance + balanceLoading: root.walletRootStore.balanceLoading + accountBalanceNotAvailable: root.networkConnectionStore.accountBalanceNotAvailable + accountBalanceNotAvailableText: root.networkConnectionStore.accountBalanceNotAvailableText + } + StatusSectionLayout { id: walletSectionLayout currentIndex: 1 @@ -351,23 +373,31 @@ Item { leftPanel: LeftTabView { id: leftTab anchors.fill: parent - emojiPopup: root.emojiPopup - networkConnectionStore: root.networkConnectionStore - isKeycardEnabled: root.isKeycardEnabled + viewState: leftPanelState - changeSelectedAccount: function(address) { + onAddAccountPopupRequested: root.walletRootStore.runAddAccountPopup() + onAddWatchOnlyAccountPopupRequested: root.walletRootStore.runAddWatchOnlyAccountPopup() + onEditAccountPopupRequested: address => root.walletRootStore.runEditAccountPopup(address) + onWatchAccountHiddenFromTotalBalanceUpdated: (address, hideFromTotalBalance) => + root.walletRootStore.updateWatchAccountHiddenFromTotalBalance(address, hideFromTotalBalance) + onAccountDeletionRequested: (address, password) => + root.walletRootStore.deleteAccount(address, password) + onUserAuthenticationRequested: requestedBy => + root.walletRootStore.authenticateLoggedInUser(requestedBy) + + onAccountSelected: address => { walletSectionLayout.goToNextPanel() d.displayAddress(address) } - selectAllAccounts: function() { + onAllAccountsSelected: { walletSectionLayout.goToNextPanel() d.displayAllAddresses() } - selectSavedAddresses: function() { + onSavedAddressesSelected: { walletSectionLayout.goToNextPanel() d.displaySavedAddresses() } - selectFollowingAddresses: function() { + onFollowingAddressesSelected: { walletSectionLayout.goToNextPanel() d.displayFollowingAddresses() } @@ -475,6 +505,21 @@ Item { leftPanelWidthOverride: root.leftPanelWidthOverride } + Loader { + id: addAccount + active: false + + sourceComponent: AddAccountPopup { + isKeycardEnabled: root.isKeycardEnabled + store.emojiPopup: root.emojiPopup + store.addAccountModule: walletSection.addAccountModule + } + + onLoaded: { + addAccount.item.open() + } + } + Loader { id: keypairImport active: false diff --git a/ui/app/AppLayouts/Wallet/views/LeftTabView.qml b/ui/app/AppLayouts/Wallet/views/LeftTabView.qml index 49b7d25d7c..877bd9ba9b 100644 --- a/ui/app/AppLayouts/Wallet/views/LeftTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/LeftTabView.qml @@ -16,10 +16,6 @@ import shared import shared.panels import shared.controls import shared.popups -import shared.popups.addaccount -import shared.stores - -import AppLayouts.Wallet import "../controls" import "../popups" @@ -29,14 +25,21 @@ Rectangle { id: root objectName: "walletLeftTab" - property NetworkConnectionStore networkConnectionStore - property var selectAllAccounts: function(){} - property var changeSelectedAccount: function(){} - property var selectSavedAddresses: function(){} - property var selectFollowingAddresses: function(){} - property var emojiPopup: null + required property LeftTabViewState viewState - property bool isKeycardEnabled: true + // Child -> parent: user actions that require store/backend handling. + signal addAccountPopupRequested() + signal addWatchOnlyAccountPopupRequested() + signal editAccountPopupRequested(string address) + signal watchAccountHiddenFromTotalBalanceUpdated(string address, bool hideFromTotalBalance) + signal accountDeletionRequested(string address, string password) + signal userAuthenticationRequested(string requestedBy) + + // Child -> parent: navigation. + signal allAccountsSelected() + signal accountSelected(string address) + signal savedAddressesSelected() + signal followingAddressesSelected() color: Theme.palette.secondaryMenuBackground @@ -54,21 +57,6 @@ Rectangle { readonly property real footerHeight: footer.height + followingAddressesFooter.height } - Loader { - id: addAccount - active: false - - sourceComponent: AddAccountPopup { - isKeycardEnabled: root.isKeycardEnabled - store.emojiPopup: root.emojiPopup - store.addAccountModule: walletSection.addAccountModule - } - - onLoaded: { - addAccount.item.open() - } - } - Loader { id: walletAccountContextMenu active: false @@ -89,17 +77,17 @@ Rectangle { } onAddNewAccountClicked: { - RootStore.runAddAccountPopup() + root.addAccountPopupRequested() } onAddWatchOnlyAccountClicked: { - RootStore.runAddWatchOnlyAccountPopup() + root.addWatchOnlyAccountPopupRequested() } onEditAccountClicked: { if (!account) return - RootStore.runEditAccountPopup(account.address) + root.editAccountPopupRequested(account.address) } onDeleteAccountClicked: { @@ -118,7 +106,7 @@ Rectangle { onHideFromTotalBalanceClicked: function (hideFromTotalBalance) { if (!account) return - RootStore.updateWatchAccountHiddenFromTotalBalance(account.address, hideFromTotalBalance) + root.watchAccountHiddenFromTotalBalanceUpdated(account.address, hideFromTotalBalance) } } } @@ -146,7 +134,7 @@ Rectangle { function doDeletion(password) { close() - RootStore.deleteAccount(removeAccountConfirmation.accountAddress, password) + root.accountDeletionRequested(removeAccountConfirmation.accountAddress, password) } onClosed: { @@ -159,7 +147,7 @@ Rectangle { doDeletion("") return } - RootStore.authenticateLoggedInUser(d.removeAccountIdentifier) + root.userAuthenticationRequested(d.removeAccountIdentifier) } Connections { @@ -180,17 +168,6 @@ Rectangle { } } - Connections { - target: walletSection - - function onDisplayAddAccountPopup() { - addAccount.active = true - } - function onDestroyAddAccountPopup() { - addAccount.active = false - } - } - StatusMouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton @@ -244,7 +221,7 @@ Rectangle { icon.height: 24 color: hovered || highlighted ? Theme.palette.primaryColor3 : "transparent" - onClicked: RootStore.runAddAccountPopup() + onClicked: root.addAccountPopupRequested() } } @@ -294,7 +271,7 @@ Rectangle { objectName: "walletAccountListItem" readonly property bool itemLoaded: !model.assetsLoading // needed for e2e tests width: ListView.view.width - Theme.padding * 2 - highlighted: RootStore.selectedAddress.toLowerCase() === model.address.toLowerCase() + highlighted: viewState.selectedAddress.toLowerCase() === model.address.toLowerCase() onHighlightedChanged: { if (highlighted) ListView.view.currentIndex = index @@ -313,9 +290,9 @@ Rectangle { statusListItemTitle.font.weight: Font.Medium color: sensor.containsMouse || highlighted ? Theme.palette.baseColor3 : "transparent" statusListItemSubTitle.loading: !!model.assetsLoading - errorMode: networkConnectionStore.accountBalanceNotAvailable + errorMode: viewState.accountBalanceNotAvailable errorIcon.tooltip.maxWidth: 300 - errorIcon.tooltip.text: networkConnectionStore.accountBalanceNotAvailableText + errorIcon.tooltip.text: viewState.accountBalanceNotAvailableText onClicked: function(itemId, mouse) { if (mouse.button === Qt.RightButton) { walletAccountContextMenu.active = true @@ -323,7 +300,7 @@ Rectangle { walletAccountContextMenu.item.popup(this, mouse.x, mouse.y) return } - changeSelectedAccount(model.address) + root.accountSelected(model.address) } components: [ StatusIcon { @@ -345,7 +322,7 @@ Rectangle { id: header verticalPadding: Theme.padding horizontalPadding: Theme.padding - highlighted: RootStore.showAllAccounts + highlighted: viewState.showAllAccounts objectName: "allAccountsBtn" leftInset: Theme.padding @@ -359,7 +336,7 @@ Rectangle { implicitWidth: parent.ListView.view.width - Theme.padding * 2 } - onClicked: root.selectAllAccounts() + onClicked: root.allAccountsSelected() contentItem: ColumnLayout { spacing: 0 @@ -378,24 +355,26 @@ Rectangle { id: walletAmountValue objectName: "walletLeftListAmountValue" customColor: Theme.palette.textColor - text: LocaleUtils.currencyAmountToLocaleString(RootStore.totalCurrencyBalance, {noSymbol: true}) + text: viewState.totalCurrencyBalance + ? LocaleUtils.currencyAmountToLocaleString(viewState.totalCurrencyBalance, {noSymbol: true}) + : "" font.pixelSize: Theme.fontSize(22) - loading: RootStore.balanceLoading + loading: viewState.balanceLoading lineHeightMode: Text.FixedHeight lineHeight: 36 verticalAlignment: Text.AlignVCenter } StatusTextWithLoadingState { customColor: Theme.palette.textColor - text: RootStore.totalCurrencyBalance.symbol + text: viewState.totalCurrencyBalance ? viewState.totalCurrencyBalance.symbol : "" font.pixelSize: Theme.additionalTextSize - loading: RootStore.balanceLoading + loading: viewState.balanceLoading font.weight: Font.Medium lineHeightMode: Text.FixedHeight lineHeight: 22 verticalAlignment: Text.AlignBottom } - visible: !networkConnectionStore.accountBalanceNotAvailable + visible: !viewState.accountBalanceNotAvailable } StatusFlatRoundButton { id: errorIcon @@ -405,15 +384,15 @@ Rectangle { icon.height: 14 icon.name: "tiny/warning" icon.color: Theme.palette.dangerColor1 - tooltip.text: networkConnectionStore.accountBalanceNotAvailableText + tooltip.text: viewState.accountBalanceNotAvailableText tooltip.maxWidth: 200 - visible: networkConnectionStore.accountBalanceNotAvailable + visible: viewState.accountBalanceNotAvailable } } } model: SortFilterProxyModel { - sourceModel: RootStore.accounts + sourceModel: viewState.accountsModel sorters: RoleSorter { roleName: "position"; sortOrder: Qt.AscendingOrder } } } @@ -457,7 +436,7 @@ Rectangle { contentItem: StatusFlatButton { objectName: "savedAddressesBtn" - highlighted: RootStore.showSavedAddresses + highlighted: viewState.showSavedAddresses hoverColor: Theme.palette.backgroundHover asset.bgColor: Theme.palette.primaryColor3 text: qsTr("Saved addresses") @@ -469,7 +448,7 @@ Rectangle { textColor: Theme.palette.directColor1 textFillWidth: true spacing: walletAccountsListView.firstItem?.statusListItemTitleArea.anchors.leftMargin ?? Theme.padding - onClicked: root.selectSavedAddresses() + onClicked: root.savedAddressesSelected() } } @@ -488,7 +467,7 @@ Rectangle { contentItem: StatusFlatButton { objectName: "followingAddressesBtn" - highlighted: RootStore.showFollowingAddresses + highlighted: viewState.showFollowingAddresses hoverColor: Theme.palette.backgroundHover asset.bgColor: Theme.palette.primaryColor3 text: qsTr("Onchain friends") @@ -500,7 +479,7 @@ Rectangle { textColor: Theme.palette.directColor1 textFillWidth: true spacing: walletAccountsListView.firstItem?.statusListItemTitleArea.anchors.leftMargin ?? Theme.padding - onClicked: root.selectFollowingAddresses() + onClicked: root.followingAddressesSelected() } } } diff --git a/ui/app/AppLayouts/Wallet/views/LeftTabViewState.qml b/ui/app/AppLayouts/Wallet/views/LeftTabViewState.qml new file mode 100644 index 0000000000..aa16170b6a --- /dev/null +++ b/ui/app/AppLayouts/Wallet/views/LeftTabViewState.qml @@ -0,0 +1,17 @@ +import QtQml + +QtObject { + id: root + + required property var accountsModel + property string selectedAddress: "" + property bool showSavedAddresses: false + property bool showFollowingAddresses: false + property var totalCurrencyBalance + property bool balanceLoading: false + property bool accountBalanceNotAvailable: false + property string accountBalanceNotAvailableText: "" + + readonly property bool showAllAccounts: + !showSavedAddresses && !showFollowingAddresses && !selectedAddress +} diff --git a/ui/app/AppLayouts/Wallet/views/qmldir b/ui/app/AppLayouts/Wallet/views/qmldir index e091842d04..4196b3800c 100644 --- a/ui/app/AppLayouts/Wallet/views/qmldir +++ b/ui/app/AppLayouts/Wallet/views/qmldir @@ -2,6 +2,8 @@ AssetsDetailView 1.0 AssetsDetailView.qml CollectiblesView 1.0 CollectiblesView.qml FollowingAddresses 1.0 FollowingAddresses.qml FollowingAddressesView 1.0 FollowingAddressesView.qml +LeftTabView 1.0 LeftTabView.qml +LeftTabViewState 1.0 LeftTabViewState.qml NetworkSelectorView 1.0 NetworkSelectorView.qml SavedAddresses 1.0 SavedAddresses.qml TokenSelectorAssetDelegate 1.0 TokenSelectorAssetDelegate.qml