From e2fa5b756ae6844b8e3700a7bdeb58d0e7d2a08c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tinkl?= Date: Mon, 18 Dec 2023 11:12:57 +0100 Subject: [PATCH] feat(CollectiblesView): Add combobox/popup with custom filtering options .. to filter by community or collection name - make the HistoryView own filter button look like the other combos - fix some cosmetic issues for StatusCombo in small/secondary mode - fix StatusBaseInput bg color in dark mode (was invisible) - update CollectiblesViewPage with options to include regular and/or community collectibles Fixes #12969 Fixes #12948 --- storybook/pages/AssetsViewPage.qml | 27 +- storybook/pages/CollectiblesViewPage.qml | 18 +- .../tests/tst_ManageCollectiblesPanel.qml | 16 +- .../src/Models/ManageCollectiblesModel.qml | 90 +++-- .../stubs/shared/stores/CurrenciesStore.qml | 20 + .../src/StatusQ/Controls/StatusBaseInput.qml | 2 +- .../src/StatusQ/Controls/StatusComboBox.qml | 9 +- .../src/StatusQ/Core/StatusAssetSettings.qml | 2 +- ui/StatusQ/src/concatmodel.cpp | 6 +- .../src/wallet/managetokenscontroller.cpp | 47 +++ .../src/wallet/managetokenscontroller.h | 9 +- ui/StatusQ/src/wallet/managetokensmodel.cpp | 3 +- ui/StatusQ/tests/tst_ConcatModel.cpp | 4 +- .../Wallet/controls/FilterComboBox.qml | 369 ++++++++++++++++++ ui/app/AppLayouts/Wallet/controls/qmldir | 1 + .../Wallet/panels/ActivityFilterPanel.qml | 16 +- .../Wallet/views/CollectiblesView.qml | 44 ++- .../AppLayouts/Wallet/views/RightTabView.qml | 1 + ui/imports/shared/controls/TokenDelegate.qml | 4 +- ui/imports/shared/views/AssetsView.qml | 48 ++- ui/imports/shared/views/HistoryView.qml | 6 +- .../shared/views/chat/ProfileContextMenu.qml | 2 +- 22 files changed, 626 insertions(+), 118 deletions(-) create mode 100644 ui/app/AppLayouts/Wallet/controls/FilterComboBox.qml diff --git a/storybook/pages/AssetsViewPage.qml b/storybook/pages/AssetsViewPage.qml index 19258c9033..25c5c0afc7 100644 --- a/storybook/pages/AssetsViewPage.qml +++ b/storybook/pages/AssetsViewPage.qml @@ -12,6 +12,7 @@ import mainui 1.0 import utils 1.0 import shared.views 1.0 +import shared.stores 1.0 import Storybook 1.0 import Models 1.0 @@ -53,29 +54,11 @@ SplitView { return supportedAddresses } - readonly property var currencyStore: QtObject { - property string currentCurrency: "USD" - function getCurrencyAmount(amount, symbol) { - return ({ - amount: amount, - symbol: symbol.toUpperCase(), - displayDecimals: 2, - stripTrailingZeroes: false - }) - } - function getCurrentCurrencyAmount(amount) { - return ({ - amount: amount, - symbol: "USD", - displayDecimals: 2, - stripTrailingZeroes: false - }) - } - } + readonly property var currencyStore: CurrenciesStore {} readonly property var groupedAccountsAssetsModel: GroupedAccountsAssetsModel {} readonly property var tokensBySymbolModel: TokensBySymbolModel {} - readonly property CommunitiesModel communityModel: CommunitiesModel{} + readonly property CommunitiesModel communityModel: CommunitiesModel {} // Added this here simply because the network and address filtering wont work in Storybook applied in AssetsView readonly property SubmodelProxyModel assetsWithFilteredBalances: SubmodelProxyModel { @@ -88,7 +71,7 @@ SplitView { d.networksChainsCurrentlySelected d.addressesSelected return d.networksChainsCurrentlySelected.split(":").includes(chainId+"") && - (!! d.addressesSelected ? d.addressesSelected.toUpperCase() === account.toUpperCase() : true) + (!! d.addressesSelected ? d.addressesSelected.toUpperCase() === account.toUpperCase() : true) } } } @@ -185,7 +168,7 @@ SplitView { CheckBox { id: loadingCheckbox - checked: true + checked: false text: "loading" } diff --git a/storybook/pages/CollectiblesViewPage.qml b/storybook/pages/CollectiblesViewPage.qml index 17b452e834..2a2ae4c7ad 100644 --- a/storybook/pages/CollectiblesViewPage.qml +++ b/storybook/pages/CollectiblesViewPage.qml @@ -23,10 +23,8 @@ SplitView { ManageCollectiblesModel { id: collectiblesModel - } - - ListModel { - id: emptyModel + includeRegularCollectibles: ctrlIncludeRegularCollectibles.checked + includeCommunityCollectibles: ctrlIncludeCommunityCollectibles.checked } Popups { @@ -39,7 +37,7 @@ SplitView { id: assetsView SplitView.preferredWidth: 600 SplitView.fillHeight: true - collectiblesModel: ctrlEmptyModel.checked ? emptyModel : collectiblesModel + collectiblesModel: collectiblesModel filterVisible: ctrlFilterVisible.checked onCollectibleClicked: logs.logEvent("onCollectibleClicked", ["chainId", "contractAddress", "tokenId", "uid"], arguments) onSendRequested: logs.logEvent("onSendRequested", ["symbol"], arguments) @@ -63,8 +61,14 @@ SplitView { checked: true } Switch { - id: ctrlEmptyModel - text: "Empty model" + id: ctrlIncludeRegularCollectibles + text: "Regular collectibles" + checked: true + } + Switch { + id: ctrlIncludeCommunityCollectibles + text: "Community collectibles" + checked: true } } } diff --git a/storybook/qmlTests/tests/tst_ManageCollectiblesPanel.qml b/storybook/qmlTests/tests/tst_ManageCollectiblesPanel.qml index 00b69a8617..a25c70bce4 100644 --- a/storybook/qmlTests/tests/tst_ManageCollectiblesPanel.qml +++ b/storybook/qmlTests/tests/tst_ManageCollectiblesPanel.qml @@ -77,7 +77,7 @@ Item { const lvRegular = findChild(controlUnderTest, "lvRegularTokens") verify(!!lvRegular) const lvRegularCount = lvRegular.count - verify(lvRegularCount === 6) + verify(lvRegularCount === 7) const delegate0 = findChild(lvRegular, "manageTokensDelegate-0") verify(!!delegate0) @@ -124,14 +124,14 @@ Item { verify(!!lvCommunityTokenGroups) // verify we have 2 community collectible groups - tryCompare(lvCommunityTokenGroups, "count", 2) + tryCompare(lvCommunityTokenGroups, "count", 3) triggerDelegateMenuAction(lvCommunityTokenGroups, 0, "miHideTokenGroup", true) verify(controlUnderTest.dirty) // verify we have one less group waitForItemPolished(lvCommunityTokenGroups) - tryCompare(lvCommunityTokenGroups, "count", 1) + tryCompare(lvCommunityTokenGroups, "count", 2) const lvHidden = findChild(controlUnderTest, "lvHiddenTokens") verify(!!lvHidden) tryCompare(lvHidden, "count", 4) // we've just hidden 4 collectibles coming from this group @@ -154,8 +154,8 @@ Item { verify(controlUnderTest.dirty) - // verify we again have 2 community groups, and one less hidden token - tryCompare(lvCommunityTokenGroups, "count", 2) + // verify we again have 3 community groups, and one less hidden token + tryCompare(lvCommunityTokenGroups, "count", 3) tryCompare(lvHidden, "count", 3) verify(controlUnderTest.dirty) @@ -164,7 +164,7 @@ Item { triggerDelegateMenuAction(lvHidden, 0, "miShowTokenGroup") waitForItemPolished(lvHidden) tryCompare(lvHidden, "count", 0) - tryCompare(lvCommunityTokenGroups, "count", 2) + tryCompare(lvCommunityTokenGroups, "count", 3) verify(controlUnderTest.dirty) } @@ -209,7 +209,7 @@ Item { const lvCommunityTokenGroups = findChild(loaderCommunityTokens, "lvCommunityTokenGroups") verify(!!lvCommunityTokenGroups) waitForItemPolished(lvCommunityTokenGroups) - tryCompare(lvCommunityTokenGroups, "count", 2) + tryCompare(lvCommunityTokenGroups, "count", 3) const group0 = findChild(lvCommunityTokenGroups, "manageTokensGroupDelegate-0") const title0 = group0.title @@ -242,7 +242,7 @@ Item { const lvCommunityTokenGroups = findChild(loaderCommunityTokens, "lvCommunityTokenGroups") verify(!!lvCommunityTokenGroups) waitForItemPolished(lvCommunityTokenGroups) - tryCompare(lvCommunityTokenGroups, "count", 2) + tryCompare(lvCommunityTokenGroups, "count", 3) // get the "Bearz" group at index 1 var bearzGroupTokenDelegate = findChild(lvCommunityTokenGroups, "manageTokensGroupDelegate-1") diff --git a/storybook/src/Models/ManageCollectiblesModel.qml b/storybook/src/Models/ManageCollectiblesModel.qml index 23a41c35d1..1030392f57 100644 --- a/storybook/src/Models/ManageCollectiblesModel.qml +++ b/storybook/src/Models/ManageCollectiblesModel.qml @@ -4,23 +4,20 @@ import QtQml.Models 2.15 import Models 1.0 ListModel { - function randomizeData() { - // TODO + property bool includeRegularCollectibles: true + onIncludeRegularCollectiblesChanged: fillData() + property bool includeCommunityCollectibles: true + onIncludeCommunityCollectiblesChanged: fillData() + + function fillData() { + clear() + if (includeRegularCollectibles) + append(data) + if (includeCommunityCollectibles) + append(communityData) } readonly property var data: [ - { - uid: "fp#9140", - name: "Frenly Panda #9140", - collectionUid: "", - collectionName: "", - communityId: "fpan", - communityName: "Frenly Pandas", - communityImage: "https://pbs.twimg.com/profile_images/1599347398769143808/C6qG3RQv_400x400.jpg", - imageUrl: "https://i.seadn.io/gae/qPfQjj4P1w0xVQXAmQJLmQ4ZtLFAJU6oiH69Lsny82LFbipLAgXhHKrcLBx2U09SmRnzeHY0ygz-3NIb-JegE_hWrZquFeL-qUPXPdw", - isLoading: false, - backgroundColor: "pink" - }, { uid: "123", name: "Punx not dead!", @@ -28,6 +25,7 @@ ListModel { collectionName: "", communityId: "", communityName: "", + communityImage: "", imageUrl: ModelsData.collectibles.cryptoPunks, isLoading: false, backgroundColor: "" @@ -39,6 +37,7 @@ ListModel { collectionName: "Pepepunks", communityId: "", communityName: "", + communityImage: "", imageUrl: "https://i.seadn.io/s/raw/files/ba2811bb5cd0bed67529d69fa92ef5aa.jpg?auto=format&dpr=1&w=1000", isLoading: false, backgroundColor: "" @@ -50,6 +49,7 @@ ListModel { collectionName: "Kitties", communityId: "", communityName: "", + communityImage: "", imageUrl: ModelsData.collectibles.kitty1Big, isLoading: true, backgroundColor: "" @@ -61,6 +61,7 @@ ListModel { collectionName: "Kitties", communityId: "", communityName: "", + communityImage: "", imageUrl: ModelsData.collectibles.kitty2Big, isLoading: false, backgroundColor: "" @@ -72,10 +73,50 @@ ListModel { collectionName: "Kitties", communityId: "", communityName: "", + communityImage: "", imageUrl: ModelsData.collectibles.kitty3Big, isLoading: false, backgroundColor: "" }, + { + uid: "pp21", + name: "pepepunk#21", + collectionUid: "pepepunks", + collectionName: "Pepepunks", + communityId: "", + communityName: "", + communityImage: "", + imageUrl: "https://i.seadn.io/s/raw/files/cfa559bb63e4378f17649c1e3b8f18fe.jpg?auto=format&dpr=1&w=1000", + isLoading: false, + backgroundColor: "" + }, + { + uid: "lp#666a", + name: "Lonely Panda #666", + collectionUid: "lpan_collection", + collectionName: "Lonely Panda Collection", + communityId: "", + communityName: "", + communityImage: "", + imageUrl: "", + isLoading: false, + backgroundColor: "pink" + }, + ] + + readonly property var communityData: [ + { + uid: "fp#9140", + name: "Frenly Panda #9140", + collectionUid: "", + collectionName: "", + communityId: "fpan", + communityName: "Frenly Pandas", + communityImage: "https://pbs.twimg.com/profile_images/1599347398769143808/C6qG3RQv_400x400.jpg", + imageUrl: "https://i.seadn.io/gae/qPfQjj4P1w0xVQXAmQJLmQ4ZtLFAJU6oiH69Lsny82LFbipLAgXhHKrcLBx2U09SmRnzeHY0ygz-3NIb-JegE_hWrZquFeL-qUPXPdw", + isLoading: false, + backgroundColor: "pink" + }, { uid: "691", name: "KILLABEAR #691", @@ -137,17 +178,20 @@ ListModel { backgroundColor: "" }, { - uid: "pp21", - name: "pepepunk#21", - collectionUid: "pepepunks", - collectionName: "Pepepunks", - communityId: "", - communityName: "", - imageUrl: "https://i.seadn.io/s/raw/files/cfa559bb63e4378f17649c1e3b8f18fe.jpg?auto=format&dpr=1&w=1000", + uid: "lb#666", + name: "Lonely Bear #666", + collectionUid: "", + collectionName: "", + communityId: "lbear", + communityName: "Lonely Bearz Community", + communityImage: "", + imageUrl: "", isLoading: false, - backgroundColor: "" + backgroundColor: "pink" }, ] - Component.onCompleted: append(data) + Component.onCompleted: { + fillData() + } } diff --git a/storybook/stubs/shared/stores/CurrenciesStore.qml b/storybook/stubs/shared/stores/CurrenciesStore.qml index de594bff3c..7012fb8033 100644 --- a/storybook/stubs/shared/stores/CurrenciesStore.qml +++ b/storybook/stubs/shared/stores/CurrenciesStore.qml @@ -1,6 +1,8 @@ import QtQuick 2.15 QtObject { + id: root + readonly property string currentCurrency: "USD" property string currentCurrencySymbol: "$" @@ -11,4 +13,22 @@ QtObject { function getFiatValue(balance, cryptoSymbol, fiatSymbol) { return balance } + + function getCurrencyAmount(amount, symbol) { + return ({ + amount: amount, + symbol: symbol ? symbol.toUpperCase() : root.currentCurrency, + displayDecimals: 2, + stripTrailingZeroes: false + }) + } + + function getCurrentCurrencyAmount(amount) { + return ({ + amount: amount, + symbol: root.currentCurrency, + displayDecimals: 2, + stripTrailingZeroes: false + }) + } } diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusBaseInput.qml b/ui/StatusQ/src/StatusQ/Controls/StatusBaseInput.qml index 0a65071f0a..456bbc7d95 100644 --- a/ui/StatusQ/src/StatusQ/Controls/StatusBaseInput.qml +++ b/ui/StatusQ/src/StatusQ/Controls/StatusBaseInput.qml @@ -278,7 +278,7 @@ Item { Rectangle { id: background anchors.fill: parent - color: root.showBackground ? Theme.palette.baseColor2 : "transparent" + color: root.showBackground ? Theme.palette.statusAppNavBar.backgroundColor : "transparent" radius: 8 clip: true diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusComboBox.qml b/ui/StatusQ/src/StatusQ/Controls/StatusComboBox.qml index ab4005cd30..3c4a0004fe 100644 --- a/ui/StatusQ/src/StatusQ/Controls/StatusComboBox.qml +++ b/ui/StatusQ/src/StatusQ/Controls/StatusComboBox.qml @@ -43,6 +43,8 @@ Item { implicitWidth: layout.implicitWidth implicitHeight: layout.implicitHeight + opacity: enabled ? 1 : 0.3 + LayoutMirroring.childrenInherit: true ColumnLayout { @@ -55,7 +57,6 @@ Item { id: labelItem Layout.fillWidth: true visible: !!text - font.pixelSize: 15 color: root.enabled ? Theme.palette.directColor1 : Theme.palette.baseColor1 } @@ -69,7 +70,7 @@ Item { enabled: root.enabled font.family: Theme.palette.baseFont.name - font.pixelSize: 14 + font.pixelSize: root.size === StatusComboBox.Size.Large ? Theme.secondaryTextFontSize : 13 padding: 16 spacing: 16 @@ -112,7 +113,7 @@ Item { verticalAlignment: Text.AlignVCenter elide: Text.ElideRight text: comboBox.displayText - color: root.enabled ? Theme.palette.directColor1 : Theme.palette.baseColor1 + color: root.type === StatusComboBox.Type.Secondary ? Theme.palette.baseColor1 : Theme.palette.directColor1 } indicator: StatusIcon { @@ -186,7 +187,7 @@ Item { Layout.fillWidth: true Layout.topMargin: 11 visible: !!text - font.pixelSize: 12 + font.pixelSize: Theme.tertiaryTextFontSize color: Theme.palette.dangerColor1 horizontalAlignment: TextEdit.AlignRight wrapMode: Text.WordWrap diff --git a/ui/StatusQ/src/StatusQ/Core/StatusAssetSettings.qml b/ui/StatusQ/src/StatusQ/Core/StatusAssetSettings.qml index 813d88a4af..b00dd55ce7 100644 --- a/ui/StatusQ/src/StatusQ/Core/StatusAssetSettings.qml +++ b/ui/StatusQ/src/StatusQ/Core/StatusAssetSettings.qml @@ -27,7 +27,7 @@ QtObject { //icon bg property real bgWidth property real bgHeight - property int bgRadius + property real bgRadius property color bgColor: "transparent" property color bgBorderColor: "transparent" property int bgBorderWidth: 0 diff --git a/ui/StatusQ/src/concatmodel.cpp b/ui/StatusQ/src/concatmodel.cpp index 4505af34a7..3ce5f20955 100644 --- a/ui/StatusQ/src/concatmodel.cpp +++ b/ui/StatusQ/src/concatmodel.cpp @@ -434,7 +434,7 @@ void ConcatModel::initRoles() m_nameRoles.reserve(m_expectedRoles.size() + 1); - for (auto& expectedRoleName : qAsConst(m_expectedRoles)) + for (const auto& expectedRoleName : qAsConst(m_expectedRoles)) m_nameRoles.try_emplace(expectedRoleName.toUtf8(), m_nameRoles.size()); for (auto sourceModel : qAsConst(m_sources)) { @@ -455,7 +455,7 @@ void ConcatModel::initRoles() m_roleNames.reserve(m_nameRoles.size()); - for (auto& [name, role] : m_nameRoles) + for (const auto& [name, role] : m_nameRoles) m_roleNames.insert(role, name); } @@ -580,7 +580,7 @@ void ConcatModel::connectModelSlots(int index, QAbstractItemModel *model) emit this->layoutAboutToBeChanged(); }); - connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, [this] + connect(model, &QAbstractItemModel::layoutChanged, this, [this] { emit this->layoutChanged(); }); diff --git a/ui/StatusQ/src/wallet/managetokenscontroller.cpp b/ui/StatusQ/src/wallet/managetokenscontroller.cpp index d69a6a8b57..52befe7096 100644 --- a/ui/StatusQ/src/wallet/managetokenscontroller.cpp +++ b/ui/StatusQ/src/wallet/managetokenscontroller.cpp @@ -6,6 +6,7 @@ ManageTokensController::ManageTokensController(QObject* parent) : QObject(parent) , m_regularTokensModel(new ManageTokensModel(this)) + , m_regularTokenGroupsModel(new ManageTokensModel(this)) , m_communityTokensModel(new ManageTokensModel(this)) , m_communityTokenGroupsModel(new ManageTokensModel(this)) , m_hiddenTokensModel(new ManageTokensModel(this)) @@ -33,6 +34,7 @@ ManageTokensController::ManageTokensController(QObject* parent) m_communityTokensModel->setCommunityIds(m_communityIds); m_communityTokensModel->saveCustomSortOrder(); rebuildCommunityTokenGroupsModel(); + rebuildRegularTokenGroupsModel(); #ifdef QT_DEBUG qCInfo(manageTokens) << "!!! ADDING NEW SOURCE DATA TOOK" << t.nsecsElapsed()/1'000'000.f << "ms"; #endif @@ -270,6 +272,8 @@ bool ManageTokensController::lessThan(const QString& lhsSymbol, const QString& r bool ManageTokensController::filterAcceptsSymbol(const QString& symbol) const { + if (symbol.isEmpty()) return true; + const auto& [pos, visible, groupId] = m_settingsData.value(symbol, {INT_MAX, true, QString()}); return visible; } @@ -345,6 +349,9 @@ void ManageTokensController::parseSourceModel() reloadCommunityIds(); m_communityTokensModel->setCommunityIds(m_communityIds); + // build collections + rebuildRegularTokenGroupsModel(); + // (pre)sort for (auto model: m_allModels) { model->applySort(); @@ -478,6 +485,46 @@ void ManageTokensController::rebuildCommunityTokenGroupsModel() qCDebug(manageTokens) << "!!! GROUPS MODEL REBUILT WITH GROUPS:" << communityIds; } +void ManageTokensController::rebuildRegularTokenGroupsModel() +{ + QStringList collectionIds; + QList result; + + const auto count = m_regularTokensModel->count(); + for (auto i = 0; i < count; i++) { + const auto& collectionToken = m_regularTokensModel->itemAt(i); + const auto collectionId = collectionToken.collectionUid; + if (collectionId.isEmpty()) + continue; + if (!collectionIds.contains(collectionId)) { // insert into groups + collectionIds.append(collectionId); + + TokenData tokenGroup; + tokenGroup.collectionUid = collectionId; + tokenGroup.collectionName = collectionToken.collectionName; + tokenGroup.image = collectionToken.image; + tokenGroup.balance = 1; + result.append(tokenGroup); + } else { // update group's childCount + const auto tokenGroup = std::find_if(result.cbegin(), result.cend(), [collectionId](const auto& item) { + return collectionId == item.collectionUid; + }); + if (tokenGroup != result.cend()) { + const auto row = std::distance(result.cbegin(), tokenGroup); + TokenData updTokenGroup = result.takeAt(row); + updTokenGroup.balance = updTokenGroup.balance.toInt() + 1; + result.insert(row, updTokenGroup); + } + } + } + + m_regularTokenGroupsModel->clear(); + for (const auto& group: result) + m_regularTokenGroupsModel->addItem(group); + + qCDebug(manageTokens) << "!!! COLLECTION MODEL REBUILT WITH GROUPS:" << collectionIds; +} + QString ManageTokensController::settingsKey() const { return m_settingsKey; diff --git a/ui/StatusQ/src/wallet/managetokenscontroller.h b/ui/StatusQ/src/wallet/managetokenscontroller.h index 68b5b54b4e..e5ace7d7ce 100644 --- a/ui/StatusQ/src/wallet/managetokenscontroller.h +++ b/ui/StatusQ/src/wallet/managetokenscontroller.h @@ -17,10 +17,11 @@ class ManageTokensController : public QObject, public QQmlParserStatus Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged FINAL) Q_PROPERTY(QString settingsKey READ settingsKey WRITE setSettingsKey NOTIFY settingsKeyChanged FINAL REQUIRED) Q_PROPERTY(bool arrangeByCommunity READ arrangeByCommunity WRITE setArrangeByCommunity NOTIFY arrangeByCommunityChanged FINAL) // TODO persist in settings + // TODO arrangeByCollection for collectibles // output properties Q_PROPERTY(QAbstractItemModel* regularTokensModel READ regularTokensModel CONSTANT FINAL) - // TODO regularTokenGroupsModel for grouped (collections of) collectibles? + Q_PROPERTY(QAbstractItemModel* regularTokenGroupsModel READ regularTokenGroupsModel CONSTANT FINAL) Q_PROPERTY(QAbstractItemModel* communityTokensModel READ communityTokensModel CONSTANT FINAL) Q_PROPERTY(QAbstractItemModel* communityTokenGroupsModel READ communityTokenGroupsModel CONSTANT FINAL) Q_PROPERTY(QAbstractItemModel* hiddenTokensModel READ hiddenTokensModel CONSTANT FINAL) @@ -68,6 +69,9 @@ private: ManageTokensModel* m_regularTokensModel{nullptr}; QAbstractItemModel* regularTokensModel() const { return m_regularTokensModel; } + ManageTokensModel* m_regularTokenGroupsModel{nullptr}; + QAbstractItemModel* regularTokenGroupsModel() const { return m_regularTokenGroupsModel; } + ManageTokensModel* m_communityTokensModel{nullptr}; QAbstractItemModel* communityTokensModel() const { return m_communityTokensModel; } @@ -86,8 +90,9 @@ private: QStringList m_communityIds; void reloadCommunityIds(); void rebuildCommunityTokenGroupsModel(); + void rebuildRegularTokenGroupsModel(); - const std::array m_allModels {m_regularTokensModel, m_communityTokensModel, m_communityTokenGroupsModel, m_hiddenTokensModel}; + const std::array m_allModels {m_regularTokensModel, m_regularTokenGroupsModel, m_communityTokensModel, m_communityTokenGroupsModel, m_hiddenTokensModel}; QString m_settingsKey; QString settingsKey() const; diff --git a/ui/StatusQ/src/wallet/managetokensmodel.cpp b/ui/StatusQ/src/wallet/managetokensmodel.cpp index c51a5c46a5..45b8922eb3 100644 --- a/ui/StatusQ/src/wallet/managetokensmodel.cpp +++ b/ui/StatusQ/src/wallet/managetokensmodel.cpp @@ -174,7 +174,8 @@ void ManageTokensModel::saveCustomSortOrder() } m_data[i] = newToken; } - emit dataChanged(index(0, 0), index(count - 1, 0), {TokenDataRoles::CustomSortOrderNoRole}); + if (count > 0) + emit dataChanged(index(0, 0), index(count - 1, 0), {TokenDataRoles::CustomSortOrderNoRole}); } void ManageTokensModel::applySort() diff --git a/ui/StatusQ/tests/tst_ConcatModel.cpp b/ui/StatusQ/tests/tst_ConcatModel.cpp index 73144b5825..d21680cbca 100644 --- a/ui/StatusQ/tests/tst_ConcatModel.cpp +++ b/ui/StatusQ/tests/tst_ConcatModel.cpp @@ -1598,8 +1598,10 @@ private slots: QSignalSpy layoutChangedSpy(&model, &ConcatModel::layoutChanged); emit sourceModel1.model()->layoutAboutToBeChanged(); - emit sourceModel1.model()->layoutChanged(); + QCOMPARE(layoutAboutToBeChangedSpy.count(), 1); + QCOMPARE(layoutChangedSpy.count(), 0); + emit sourceModel1.model()->layoutChanged(); QCOMPARE(layoutAboutToBeChangedSpy.count(), 1); QCOMPARE(layoutChangedSpy.count(), 1); } diff --git a/ui/app/AppLayouts/Wallet/controls/FilterComboBox.qml b/ui/app/AppLayouts/Wallet/controls/FilterComboBox.qml new file mode 100644 index 0000000000..75c4a3a2ec --- /dev/null +++ b/ui/app/AppLayouts/Wallet/controls/FilterComboBox.qml @@ -0,0 +1,369 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtGraphicalEffects 1.15 + +import StatusQ 0.1 +import StatusQ.Core 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Components 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Popups 0.1 + +import utils 1.0 +import shared.controls 1.0 + +import SortFilterProxyModel 0.2 + +ComboBox { + id: root + + required property var regularTokensModel // "uncategorized" collectibles (not grouped) + required property var regularTokenGroupsModel // collection groups + required property var communityTokenGroupsModel // community groups + + property bool hasCommunityGroups + + property alias selectedFilterGroupIds: d.selectedFilterGroupIds + readonly property bool hasEnabledFilters: d.selectedFilterGroupIds.length + + function clearFilter() { + d.selectedFilterGroupIds = [] + } + + enabled: d.searchTextLowerCase || d.combinedProxyModel.count || d.uncategorizedModel.count + opacity: enabled ? 1 : 0.3 + + displayText: qsTr("Collection") + + horizontalPadding: 12 + verticalPadding: Style.current.halfPadding + spacing: Style.current.halfPadding + + font.family: Theme.palette.baseFont.name + font.pixelSize: Style.current.additionalTextSize + + QtObject { + id: d + + readonly property int defaultDelegateHeight: 34 + + readonly property string searchTextLowerCase: searchBox.input.text.toLowerCase() + + readonly property var combinedModel: ConcatModel { + sources: [ + SourceModel { + model: root.communityTokenGroupsModel + markerRoleValue: "community" + }, + SourceModel { + model: root.regularTokenGroupsModel + markerRoleValue: "collection" + } + ] + + markerRoleName: "sourceGroup" + onRowsRemoved: root.clearFilter() // different underlying model -> uncheck all groups + } + + readonly property var combinedProxyModel: SortFilterProxyModel { + sourceModel: d.combinedModel + proxyRoles: [ + JoinRole { + name: "groupName" + roleNames: ["collectionName", "communityName"] + separator: "" + }, + JoinRole { + name: "groupId" + roleNames: ["collectionUid", "communityId"] + separator: "" + } + ] + filters: [ + ExpressionFilter { + enabled: d.searchTextLowerCase !== "" + expression: { + d.searchTextLowerCase // ensure expression is reevaluated when searchString changes + return model.groupName.toLowerCase().includes(d.searchTextLowerCase) || model.groupId.toLowerCase().includes(d.searchTextLowerCase) + } + } + ] + } + + readonly property var uncategorizedModel: SortFilterProxyModel { // regular collectibles with no collection + sourceModel: root.regularTokensModel + filters: ValueFilter { + roleName: "collectionUid" + value: "" + } + onCountChanged: if (!count) d.removeFilter("") // different underlying model -> uncheck + } + + property var selectedFilterGroupIds: [] + readonly property bool allVisuallyChecked: selectedFilterGroupIds.length === 0 + + function addFilter(groupId) { + if (d.selectedFilterGroupIds.includes(groupId)) + return + const newFilters = d.selectedFilterGroupIds.concat(groupId) + d.selectedFilterGroupIds = newFilters + } + function removeFilter(groupId) { + const newFilters = d.selectedFilterGroupIds.filter((filter) => filter !== groupId) + d.selectedFilterGroupIds = newFilters + } + function removeGroupFilters() { + const newFilters = d.selectedFilterGroupIds.filter((filter) => filter === "") + d.selectedFilterGroupIds = newFilters + } + } + + background: Rectangle { + border.width: 1 + border.color: Theme.palette.directColor7 + radius: Style.current.radius + color: root.down ? Theme.palette.baseColor2 : "transparent" + HoverHandler { + cursorShape: root.enabled ? Qt.PointingHandCursor : undefined + } + } + + contentItem: RowLayout { + spacing: -6 // badge has an implicit border :/ + StatusBaseText { + leftPadding: root.horizontalPadding + rightPadding: root.horizontalPadding + font.pixelSize: root.font.pixelSize + font.weight: Font.Medium + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + text: root.displayText + color: Theme.palette.baseColor1 + } + StatusBadge { + Layout.preferredHeight: 16 + Layout.preferredWidth: 16 + Layout.rightMargin: Style.current.halfPadding + value: d.selectedFilterGroupIds.length + visible: root.hasEnabledFilters + } + } + + indicator: StatusIcon { + x: root.mirrored ? root.horizontalPadding : root.width - width - root.horizontalPadding + y: root.topPadding + (root.availableHeight - height) / 2 + width: 16 + height: width + icon: "chevron-down" + color: Theme.palette.baseColor1 + } + + popup: Popup { + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + y: root.height + 4 + + implicitWidth: 290 + implicitHeight: Math.min(contentHeight+margins, 380) + margins: Style.current.halfPadding + + padding: 0 + bottomPadding: Style.current.halfPadding + + background: Rectangle { + color: Theme.palette.statusSelect.menuItemBackgroundColor + radius: Style.current.radius + layer.enabled: true + layer.effect: DropShadow { + horizontalOffset: 0 + verticalOffset: 4 + radius: 12 + samples: 25 + spread: 0.2 + color: Theme.palette.dropShadow + } + } + + contentItem: ColumnLayout { + spacing: 0 + SearchBox { + id: searchBox + Layout.fillWidth: true + Layout.topMargin: 12 + Layout.leftMargin: Style.current.halfPadding + Layout.rightMargin: Style.current.halfPadding + Layout.bottomMargin: 12 + minimumHeight: d.defaultDelegateHeight + maximumHeight: d.defaultDelegateHeight + input.edit.font.pixelSize: root.font.pixelSize + input.placeholder.font.pixelSize: root.font.pixelSize + input.asset.width: 16 + input.asset.height: 16 + topPadding: 0 + bottomPadding: 0 + placeholderText: qsTr("Collection, community, name or #") + } + StatusListView { + Layout.fillWidth: true + Layout.fillHeight: true + implicitWidth: contentWidth + implicitHeight: contentHeight + + model: d.combinedProxyModel + delegate: Item { // NB anything but AbstractButton to prevent auto-closing of the popup + width: ListView.view.width + implicitHeight: customMenuDelegate.implicitHeight + + CustomItemDelegate { + id: customMenuDelegate + width: parent.width + } + } + section.property: "sourceGroup" + section.delegate: ColumnLayout { + id: sectionDelegate + width: ListView.view.width + height: d.defaultDelegateHeight + spacing: 0 + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: Theme.palette.statusListItem.backgroundColor + + StatusBaseText { + anchors.fill: parent + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding + verticalAlignment: Text.AlignVCenter + text: section === "community" ? qsTr("Community minted") : root.hasCommunityGroups ? qsTr("Other") + : qsTr("Collections") + color: Theme.palette.baseColor1 + font.pixelSize: Style.current.tertiaryTextFontSize + elide: Text.ElideRight + } + } + + // floating divider + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 4 + color: sectionDelegate.y <= sectionDelegate.ListView.view.contentY && sectionDelegate.y !== 0 ? Theme.palette.directColor8 + : Theme.palette.statusListItem.backgroundColor + } + } + section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart + } + StatusMenuSeparator { + Layout.fillWidth: true + visible: d.uncategorizedModel.count + } + CustomItemDelegate { + Layout.fillWidth: true + icon.name: "image" + text: qsTr("No collection") + count: d.uncategorizedModel.count + groupId: "" + visible: count + } + } + } + + component CustomItemDelegate: CheckDelegate { + id: menuDelegate + + property int count: model.enabledNetworkBalance + readonly property bool isCommunityGroup: !!model && !!model.communityId + property string groupId: model.groupId + readonly property string groupImage: !!model ? model.communityImage || model.imageUrl : "" + + highlighted: hovered + leftPadding: Style.current.padding + rightPadding: 44 + verticalPadding: 4 + spacing: root.spacing + font: root.font + text: model.groupName + icon.source: groupImage + icon.name: isCommunityGroup ? "group" : "gallery" + checked: d.selectedFilterGroupIds.includes(menuDelegate.groupId) + onToggled: checked ? d.addFilter(menuDelegate.groupId) : d.removeFilter(menuDelegate.groupId) + background: Rectangle { + color: menuDelegate.highlighted ? Theme.palette.statusMenu.hoverBackgroundColor : "transparent" + HoverHandler { + cursorShape: menuDelegate.enabled ? Qt.PointingHandCursor : undefined + } + } + indicator: Rectangle { + anchors.right: parent.right + anchors.rightMargin: Style.current.padding + anchors.verticalCenter: parent.verticalCenter + implicitWidth: 18 + implicitHeight: implicitWidth + radius: 2 + color: menuDelegate.down || menuDelegate.checkState !== Qt.Checked + ? Theme.palette.directColor8 + : Theme.palette.primaryColor1 + + StatusIcon { + icon: "checkbox" + width: 11 + height: 8 + anchors.centerIn: parent + anchors.horizontalCenterOffset: 1 + color: d.allVisuallyChecked ? Theme.palette.baseColor1 : Theme.palette.white + visible: menuDelegate.down || menuDelegate.checkState !== Qt.Unchecked || d.allVisuallyChecked + } + } + contentItem: RowLayout { + spacing: root.spacing + + Loader { + Layout.preferredWidth: 32 + Layout.preferredHeight: 32 + + sourceComponent: !!menuDelegate.groupImage ? roundImage : roundIcon + + Component { + id: roundImage + StatusRoundedImage { + image.source: menuDelegate.icon.source + radius: menuDelegate.isCommunityGroup ? width/2 : 6 + showLoadingIndicator: true + image.fillMode: Image.PreserveAspectCrop + } + } + + Component { + id: roundIcon + StatusRoundIcon { + asset.bgRadius: menuDelegate.isCommunityGroup ? width/2 : 6 + asset.bgWidth: 16 + asset.bgHeight: 16 + asset.bgColor: Theme.palette.primaryColor3 + asset.width: 16 + asset.height: 16 + asset.name: menuDelegate.icon.name + asset.color: Theme.palette.primaryColor1 + } + } + } + + StatusBaseText { + Layout.fillWidth: true + text: menuDelegate.text + elide: Text.ElideRight + font.pixelSize: menuDelegate.font.pixelSize + font.weight: menuDelegate.checked ? Font.Medium : Font.Normal + } + + Item { Layout.fillWidth: true } + + StatusBaseText { + font.pixelSize: Style.current.tertiaryTextFontSize + color: Theme.palette.baseColor1 + text: menuDelegate.count + } + } + } +} diff --git a/ui/app/AppLayouts/Wallet/controls/qmldir b/ui/app/AppLayouts/Wallet/controls/qmldir index 4d1562f1cc..61e60f4e29 100644 --- a/ui/app/AppLayouts/Wallet/controls/qmldir +++ b/ui/app/AppLayouts/Wallet/controls/qmldir @@ -5,6 +5,7 @@ StatusTxProgressBar 1.0 StatusTxProgressBar.qml StatusDateRangePicker 1.0 StatusDateRangePicker.qml ActivityFilterTagItem 1.0 ActivityFilterTagItem.qml SortOrderComboBox 1.0 SortOrderComboBox.qml +FilterComboBox 1.0 FilterComboBox.qml ManageTokenMenuButton 1.0 ManageTokenMenuButton.qml ManageTokensCommunityTag 1.0 ManageTokensCommunityTag.qml ManageTokensDelegate 1.0 ManageTokensDelegate.qml diff --git a/ui/app/AppLayouts/Wallet/panels/ActivityFilterPanel.qml b/ui/app/AppLayouts/Wallet/panels/ActivityFilterPanel.qml index 7d67468fe9..53b09ec82b 100644 --- a/ui/app/AppLayouts/Wallet/panels/ActivityFilterPanel.qml +++ b/ui/app/AppLayouts/Wallet/panels/ActivityFilterPanel.qml @@ -31,16 +31,12 @@ Column { spacing: 8 - StatusRoundButton { - id: filterButton - width: 32 - height: 32 - icon.name: "filter" - border.width: 1 - border.color: Theme.palette.directColor8 - type: StatusRoundButton.Type.Tertiary - icon.color: Theme.palette.primaryColor1 - onClicked: { + StatusComboBox { + height: 34 + size: StatusComboBox.Size.Small + type: StatusComboBox.Type.Secondary + control.displayText: qsTr("Filter") + control.popup.onOpened: { activityFilterStore.updateStartTimestamp() activityFilterMenu.popup(x, y + height + 4) } diff --git a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml index 202f22e1f6..d774567967 100644 --- a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml +++ b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml @@ -64,6 +64,7 @@ ColumnLayout { readonly property var controller: ManageTokensController { settingsKey: "WalletCollectibles" + sourceModel: d.renamedModel } function hideAllCommunityTokens(communityId) { @@ -87,6 +88,16 @@ ColumnLayout { d.controller.settingsDirty return d.controller.filterAcceptsSymbol(model.symbol) && (customFilter.isCommunity ? !!model.communityId : !model.communityId) } + }, + ExpressionFilter { + enabled: customFilter.isCommunity && cmbFilter.hasEnabledFilters + expression: cmbFilter.selectedFilterGroupIds.includes(model.communityId) || + (!model.communityId && cmbFilter.selectedFilterGroupIds.includes("")) + }, + ExpressionFilter { + enabled: !customFilter.isCommunity && cmbFilter.hasEnabledFilters + expression: cmbFilter.selectedFilterGroupIds.includes(model.collectionUid) || + (!model.collectionUid && cmbFilter.selectedFilterGroupIds.includes("")) } ] sorters: [ @@ -109,13 +120,14 @@ ColumnLayout { category: "CollectiblesViewSortSettings" property alias currentSortField: cmbTokenOrder.currentIndex property alias currentSortOrder: cmbTokenOrder.currentSortOrder + property alias selectedFilterGroupIds: cmbFilter.selectedFilterGroupIds } ColumnLayout { Layout.fillWidth: true - Layout.preferredHeight: root.filterVisible && (d.hasCollectibles || d.hasCommunityCollectibles) ? implicitHeight : 0 + Layout.preferredHeight: root.filterVisible ? implicitHeight : 0 spacing: 20 - opacity: Layout.preferredHeight < implicitHeight ? 0 : 1 + opacity: root.filterVisible ? 1 : 0 Behavior on Layout.preferredHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } @@ -128,6 +140,22 @@ ColumnLayout { Layout.fillWidth: true spacing: Style.current.halfPadding + FilterComboBox { + id: cmbFilter + regularTokensModel: d.controller.regularTokensModel + regularTokenGroupsModel: d.controller.regularTokenGroupsModel + communityTokenGroupsModel: d.controller.communityTokenGroupsModel + hasCommunityGroups: d.hasCommunityCollectibles + } + + Rectangle { + Layout.preferredHeight: 34 + Layout.preferredWidth: 1 + Layout.leftMargin: 12 + Layout.rightMargin: 12 + color: Theme.palette.baseColor2 + } + StatusBaseText { color: Theme.palette.baseColor1 font.pixelSize: Style.current.additionalTextSize @@ -150,6 +178,15 @@ ColumnLayout { root.manageTokensRequested() } } + + Item { Layout.fillWidth: true } + + StatusLinkText { + visible: cmbFilter.hasEnabledFilters + normalColor: Theme.palette.primaryColor1 + text: qsTr("Clear filter") + onClicked: cmbFilter.clearFilter() + } } StatusDialogDivider { @@ -159,6 +196,7 @@ ColumnLayout { ShapeRectangle { Layout.fillWidth: true + Layout.topMargin: Style.current.padding visible: !d.hasCollectibles && !d.hasCommunityCollectibles text: qsTr("Collectibles will appear here") } @@ -185,7 +223,7 @@ ColumnLayout { Layout.fillWidth: true Layout.topMargin: Style.current.padding Layout.bottomMargin: Style.current.halfPadding - visible: d.hasCommunityCollectibles + visible: d.hasCollectibles && d.hasCommunityCollectibles } RowLayout { diff --git a/ui/app/AppLayouts/Wallet/views/RightTabView.qml b/ui/app/AppLayouts/Wallet/views/RightTabView.qml index bc66fc146d..c6906b4eb2 100644 --- a/ui/app/AppLayouts/Wallet/views/RightTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/RightTabView.qml @@ -189,6 +189,7 @@ Item { overview: RootStore.overview showAllAccounts: root.showAllAccounts sendModal: root.sendModal + filterVisible: filterButton.checked onLaunchTransactionDetail: function (entry, entryIndex) { transactionDetailView.transactionIndex = entryIndex transactionDetailView.transaction = entry diff --git a/ui/imports/shared/controls/TokenDelegate.qml b/ui/imports/shared/controls/TokenDelegate.qml index a570539ca5..d971e07741 100644 --- a/ui/imports/shared/controls/TokenDelegate.qml +++ b/ui/imports/shared/controls/TokenDelegate.qml @@ -110,7 +110,7 @@ StatusListItem { font.pixelSize: 13 loading: modelData && modelData.marketDetailsLoading text: modelData && modelData.marketDetails && modelData.marketDetails.changePct24hour !== undefined ? "%1 %2%".arg(root.upDownTriangle).arg(LocaleUtils.numberToLocaleString(modelData.marketDetails.changePct24hour, 2)) - : "---" + : "---" } Rectangle { anchors.verticalCenter: parent.verticalCenter @@ -124,7 +124,7 @@ StatusListItem { customColor: root.textColor font.pixelSize: 13 loading: modelData && modelData.marketDetailsLoading - text: modelData && modelData.marketDetails ? LocaleUtils.currencyAmountToLocaleString(modelData.marketDetails.currencyPrice) : "" + text: modelData && modelData.marketDetails ? LocaleUtils.currencyAmountToLocaleString(modelData.marketDetails.currencyPrice) : "" } } ManageTokensCommunityTag { diff --git a/ui/imports/shared/views/AssetsView.qml b/ui/imports/shared/views/AssetsView.qml index dc8d2b3185..6bd915eee0 100644 --- a/ui/imports/shared/views/AssetsView.qml +++ b/ui/imports/shared/views/AssetsView.qml @@ -126,6 +126,11 @@ ColumnLayout { expression: model.marketDetails.currencyPrice.amount expectedRoles: ["marketDetails"] }, + FastExpressionRole { + name: "changePct24hour" + expression: model.marketDetails.changePct24hour + expectedRoles: ["marketDetails"] + }, FastExpressionRole { name: "isCommunityAsset" expression: !!model.communityId @@ -170,7 +175,7 @@ ColumnLayout { Layout.fillWidth: true Layout.preferredHeight: root.filterVisible ? implicitHeight : 0 spacing: 20 - opacity: Layout.preferredHeight < implicitHeight ? 0 : 1 + opacity: root.filterVisible ? 1 : 0 Behavior on Layout.preferredHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } @@ -214,38 +219,29 @@ ColumnLayout { } } - StatusScrollView { + StatusListView { + id: assetsListView Layout.fillWidth: true - Layout.fillHeight: true Layout.topMargin: Style.current.padding - leftPadding: 0 - verticalPadding: 0 - contentWidth: availableWidth - - StatusListView { - id: assetsListView - Layout.fillWidth: true - Layout.preferredHeight: contentHeight - interactive: false - objectName: "assetViewStatusListView" - model: root.areAssetsLoading ? d.loadingItemsCount : d.customSFPM - delegate: delegateLoader - section { - property: "isCommunityAsset" - delegate: Loader { - width: ListView.view.width - required property string section - sourceComponent: section === "true" ? sectionDelegate: null - } + Layout.preferredHeight: contentHeight + Layout.fillHeight: true + objectName: "assetViewStatusListView" + model: root.areAssetsLoading ? d.loadingItemsCount : d.customSFPM + delegate: delegateLoader + section { + property: "isCommunityAsset" + delegate: Loader { + width: ListView.view.width + required property string section + sourceComponent: section === "true" ? sectionDelegate : null } - } } Component { id: sectionDelegate ColumnLayout { - width: ListView.view.width + width: parent.width spacing: 0 StatusDialogDivider { @@ -282,7 +278,7 @@ ColumnLayout { property var modelData: model property int delegateIndex: index width: ListView.view.width - sourceComponent: root.areAssetsLoading ? loadingTokenDelegate: tokenDelegate + sourceComponent: root.areAssetsLoading ? loadingTokenDelegate : tokenDelegate } } @@ -297,7 +293,7 @@ ColumnLayout { id: tokenDelegate TokenDelegate { objectName: "AssetView_TokenListItem_" + (!!modelData ? modelData.symbol : "") - readonly property string balance: !!modelData && !!modelData.currentBalance? "%1".arg(modelData.currentBalance) : "" // Needed for the tests + readonly property string balance: !!modelData && !!modelData.currentBalance ? "%1".arg(modelData.currentBalance) : "" // Needed for the tests errorTooltipText_1: !!modelData && !!networkConnectionStore ? networkConnectionStore.getBlockchainNetworkDownTextForToken(modelData.balances) : "" errorTooltipText_2: !!networkConnectionStore ? networkConnectionStore.getMarketNetworkDownText() : "" subTitle: { diff --git a/ui/imports/shared/views/HistoryView.qml b/ui/imports/shared/views/HistoryView.qml index b9d236b75c..e81587c2ec 100644 --- a/ui/imports/shared/views/HistoryView.qml +++ b/ui/imports/shared/views/HistoryView.qml @@ -30,6 +30,7 @@ ColumnLayout { property var overview property bool showAllAccounts: false property var sendModal + property bool filterVisible signal launchTransactionDetail(var transaction, int entryIndex) @@ -43,7 +44,6 @@ ColumnLayout { if (!visible) return - filterPanelLoader.active = true if (RootStore.transactionActivityStatus.isFilterDirty) { WalletStores.RootStore.currentActivityFiltersStore.applyAllFilters() } @@ -101,11 +101,11 @@ ColumnLayout { Loader { id: filterPanelLoader - active: false + active: root.filterVisible && (d.isInitialLoading || transactionListRoot.count > 0 || WalletStores.RootStore.currentActivityFiltersStore.filtersSet) + visible: active asynchronous: true Layout.fillWidth: true sourceComponent: ActivityFilterPanel { - visible: d.isInitialLoading || transactionListRoot.count > 0 || WalletStores.RootStore.currentActivityFiltersStore.filtersSet activityFilterStore: WalletStores.RootStore.currentActivityFiltersStore store: WalletStores.RootStore hideNoResults: newTransactions.visible diff --git a/ui/imports/shared/views/chat/ProfileContextMenu.qml b/ui/imports/shared/views/chat/ProfileContextMenu.qml index 189340df3f..1e88b71901 100644 --- a/ui/imports/shared/views/chat/ProfileContextMenu.qml +++ b/ui/imports/shared/views/chat/ProfileContextMenu.qml @@ -239,7 +239,7 @@ StatusMenu { StatusAction { id: blockMenuItem - objectName: blockUser_StatusItem + objectName: "blockUser_StatusItem" text: qsTr("Block User") icon.name: "cancel" type: StatusAction.Type.Danger