diff --git a/storybook/pages/CommunityPermissionsViewPage.qml b/storybook/pages/CommunityPermissionsViewPage.qml index 2903feb4e8..27d68bd65c 100644 --- a/storybook/pages/CommunityPermissionsViewPage.qml +++ b/storybook/pages/CommunityPermissionsViewPage.qml @@ -30,6 +30,7 @@ SplitView { store: CommunitiesStore { id: mockedCommunity + permissionsModel: PermissionsModel.permissionsModel readonly property var assetsModel: AssetsModel { @@ -83,10 +84,8 @@ SplitView { anchors.fill: parent model: mockedCommunity.permissionsModel - assetKeys: ModelUtils.modelToArray( - assetsModel, ["key"]).map(asset => asset.key) - collectibleKeys: ModelUtils.modelToArray( - collectiblesModel, ["key"]).map(collectible => collectible.key) + assetKeys: assetsModel.data.map(asset => asset.key) + collectibleKeys: collectiblesModel.data.map(collectible => collectible.key) } } } diff --git a/storybook/src/Models/AssetsModel.qml b/storybook/src/Models/AssetsModel.qml index 2f192f7a91..95442d432e 100644 --- a/storybook/src/Models/AssetsModel.qml +++ b/storybook/src/Models/AssetsModel.qml @@ -3,42 +3,43 @@ import QtQuick 2.15 import AppLayouts.Chat.controls.community 1.0 ListModel { - Component.onCompleted: - append([ - { - key: "socks", - iconSource: ModelsData.assets.socks, - name: "Unisocks", - shortName: "SOCKS", - category: TokenCategories.Category.Community - }, - { - key: "zrx", - iconSource: ModelsData.assets.zrx, - name: "Ox", - shortName: "ZRX", - category: TokenCategories.Category.Own - }, - { - key: "1inch", - iconSource: ModelsData.assets.inch, - name: "1inch", - shortName: "1INCH", - category: TokenCategories.Category.Own - }, - { - key: "Aave", - iconSource: ModelsData.assets.aave, - name: "Aave", - shortName: "AAVE", - category: TokenCategories.Category.Own - }, - { - key: "Amp", - iconSource: ModelsData.assets.amp, - name: "Amp", - shortName: "AMP", - category: TokenCategories.Category.Own - } - ]) + readonly property var data: [ + { + key: "socks", + iconSource: ModelsData.assets.socks, + name: "Unisocks", + shortName: "SOCKS", + category: TokenCategories.Category.Community + }, + { + key: "zrx", + iconSource: ModelsData.assets.zrx, + name: "Ox", + shortName: "ZRX", + category: TokenCategories.Category.Own + }, + { + key: "1inch", + iconSource: ModelsData.assets.inch, + name: "1inch", + shortName: "1INCH", + category: TokenCategories.Category.Own + }, + { + key: "Aave", + iconSource: ModelsData.assets.aave, + name: "Aave", + shortName: "AAVE", + category: TokenCategories.Category.Own + }, + { + key: "Amp", + iconSource: ModelsData.assets.amp, + name: "Amp", + shortName: "AMP", + category: TokenCategories.Category.Own + } + ] + + Component.onCompleted: append(data) } diff --git a/storybook/src/Models/CollectiblesModel.qml b/storybook/src/Models/CollectiblesModel.qml index 502f8b0dbf..5abaa1c021 100644 --- a/storybook/src/Models/CollectiblesModel.qml +++ b/storybook/src/Models/CollectiblesModel.qml @@ -3,75 +3,76 @@ import QtQuick 2.15 import AppLayouts.Chat.controls.community 1.0 ListModel { - Component.onCompleted: - append([ - { - key: "Anniversary", - iconSource: ModelsData.collectibles.anniversary, - name: "Anniversary", - category: TokenCategories.Category.Community - }, - { - key: "CryptoKitties", - iconSource: ModelsData.collectibles.cryptoKitties, - name: "CryptoKitties", - category: TokenCategories.Category.Own, - subItems: [ - { - key: "Kitty1", - iconSource: ModelsData.collectibles.kitty1, - imageSource: ModelsData.collectibles.kitty1Big, - name: "Furbeard" - }, - { - key: "Kitty2", - iconSource: ModelsData.collectibles.kitty2, - imageSource: ModelsData.collectibles.kitty2Big, - name: "Magicat" - }, - { - key: "Kitty3", - iconSource: ModelsData.collectibles.kitty3, - imageSource: ModelsData.collectibles.kitty3Big, - name: "Happy Meow" - }, - { - key: "Kitty4", - iconSource: ModelsData.collectibles.kitty4, - imageSource: ModelsData.collectibles.kitty4Big, - name: "Furbeard-2" - }, - { - key: "Kitty5", - iconSource: ModelsData.collectibles.kitty5, - imageSource: ModelsData.collectibles.kitty5Big, - name: "Magicat-3" - }, - { - key: "Kitty5", - iconSource: ModelsData.collectibles.kitty4, - imageSource: ModelsData.collectibles.kitty4Big, - name: "Furbeard-3" - }, - { - key: "Kitty6", - iconSource: ModelsData.collectibles.kitty5, - imageSource: ModelsData.collectibles.kitty5Big, - name: "Magicat-4" - } - ] - }, - { - key: "SuperRare", - iconSource: ModelsData.collectibles.superRare, - name: "SuperRare", - category: TokenCategories.Category.Own - }, - { - key: "Custom", - iconSource: ModelsData.collectibles.custom, - name: "Custom Collectible", - category: TokenCategories.Category.General - } - ]) + readonly property var data: [ + { + key: "Anniversary", + iconSource: ModelsData.collectibles.anniversary, + name: "Anniversary", + category: TokenCategories.Category.Community + }, + { + key: "CryptoKitties", + iconSource: ModelsData.collectibles.cryptoKitties, + name: "CryptoKitties", + category: TokenCategories.Category.Own, + subItems: [ + { + key: "Kitty1", + iconSource: ModelsData.collectibles.kitty1, + imageSource: ModelsData.collectibles.kitty1Big, + name: "Furbeard" + }, + { + key: "Kitty2", + iconSource: ModelsData.collectibles.kitty2, + imageSource: ModelsData.collectibles.kitty2Big, + name: "Magicat" + }, + { + key: "Kitty3", + iconSource: ModelsData.collectibles.kitty3, + imageSource: ModelsData.collectibles.kitty3Big, + name: "Happy Meow" + }, + { + key: "Kitty4", + iconSource: ModelsData.collectibles.kitty4, + imageSource: ModelsData.collectibles.kitty4Big, + name: "Furbeard-2" + }, + { + key: "Kitty5", + iconSource: ModelsData.collectibles.kitty5, + imageSource: ModelsData.collectibles.kitty5Big, + name: "Magicat-3" + }, + { + key: "Kitty5", + iconSource: ModelsData.collectibles.kitty4, + imageSource: ModelsData.collectibles.kitty4Big, + name: "Furbeard-3" + }, + { + key: "Kitty6", + iconSource: ModelsData.collectibles.kitty5, + imageSource: ModelsData.collectibles.kitty5Big, + name: "Magicat-4" + } + ] + }, + { + key: "SuperRare", + iconSource: ModelsData.collectibles.superRare, + name: "SuperRare", + category: TokenCategories.Category.Own + }, + { + key: "Custom", + iconSource: ModelsData.collectibles.custom, + name: "Custom Collectible", + category: TokenCategories.Category.General + } + ] + + Component.onCompleted: append(data) } diff --git a/ui/StatusQ/StatusQSources.cmake b/ui/StatusQ/StatusQSources.cmake index 774cf02315..c543c42a5e 100644 --- a/ui/StatusQ/StatusQSources.cmake +++ b/ui/StatusQ/StatusQSources.cmake @@ -1,19 +1,21 @@ set(STATUSQ_DIR ${CMAKE_CURRENT_LIST_DIR}) set(STATUSQ_HEADERS + ${STATUSQ_DIR}/include/StatusQ/QClipboardProxy.h + ${STATUSQ_DIR}/include/StatusQ/modelutilsinternal.h + ${STATUSQ_DIR}/include/StatusQ/rxvalidator.h + ${STATUSQ_DIR}/include/StatusQ/statussyntaxhighlighter.h ${STATUSQ_DIR}/include/StatusQ/statuswindow.h ${STATUSQ_DIR}/include/StatusQ/typesregistration.h - ${STATUSQ_DIR}/include/StatusQ/QClipboardProxy.h - ${STATUSQ_DIR}/include/StatusQ/statussyntaxhighlighter.h - ${STATUSQ_DIR}/include/StatusQ/rxvalidator.h ) set(STATUSQ_SOURCES + ${STATUSQ_DIR}/src/QClipboardProxy.cpp + ${STATUSQ_DIR}/src/modelutilsinternal.cpp + ${STATUSQ_DIR}/src/rxvalidator.cpp + ${STATUSQ_DIR}/src/statussyntaxhighlighter.cpp ${STATUSQ_DIR}/src/statuswindow.cpp ${STATUSQ_DIR}/src/typesregistration.cpp - ${STATUSQ_DIR}/src/QClipboardProxy.cpp - ${STATUSQ_DIR}/src/statussyntaxhighlighter.cpp - ${STATUSQ_DIR}/src/rxvalidator.cpp ) if(APPLE) diff --git a/ui/StatusQ/include/StatusQ/modelutilsinternal.h b/ui/StatusQ/include/StatusQ/modelutilsinternal.h new file mode 100644 index 0000000000..4eff079574 --- /dev/null +++ b/ui/StatusQ/include/StatusQ/modelutilsinternal.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +class QAbstractItemModel; +class QJSEngine; +class QQmlEngine; + +class ModelUtilsInternal : public QObject +{ + Q_OBJECT + +public: + explicit ModelUtilsInternal(QObject* parent = nullptr); + + Q_INVOKABLE int roleByName(QAbstractItemModel *model, + const QString &roleName) const; + + Q_INVOKABLE QStringList roleNames(QAbstractItemModel *model) const; + + Q_INVOKABLE QVariantMap get(QAbstractItemModel *model, int row) const; + Q_INVOKABLE QVariant get(QAbstractItemModel *model, int row, + const QString &roleName) const; + + static QObject* qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine) + { + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + + return new ModelUtilsInternal; + } +}; diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/ModelUtils.qml b/ui/StatusQ/src/StatusQ/Core/Utils/ModelUtils.qml index c593e006b4..647f4ace9d 100644 --- a/ui/StatusQ/src/StatusQ/Core/Utils/ModelUtils.qml +++ b/ui/StatusQ/src/StatusQ/Core/Utils/ModelUtils.qml @@ -2,16 +2,25 @@ pragma Singleton import QtQuick 2.14 +import StatusQ.Internal 0.1 as Internal + QtObject { + function get(model, index, role = "") { + if (role) + return Internal.ModelUtils.get(model, index, role) + else + return Internal.ModelUtils.get(model, index) + } + function modelToArray(model, roles) { if (!model) return [] - const count = model.count + const count = model.rowCount() const array = [] for (let i = 0; i < count; i++) { - const modelItem = model.get(i) + const modelItem = Internal.ModelUtils.get(model, i) const arrayItem = {} roles.forEach(role => { @@ -28,10 +37,10 @@ QtObject { } function indexOf(model, role, key) { - const count = model.count + const count = model.rowCount() for (let i = 0; i < count; i++) - if (model.get(i)[role] === key) + if (Internal.ModelUtils.get(model, i, role) === key) return i return -1 @@ -55,8 +64,8 @@ QtObject { return true for (let i = 0; i < countA; i++) { - const itemA = modelA.get(i) - const itemB = modelB.get(i) + const itemA = Internal.ModelUtils.get(modelA, i) + const itemB = Internal.ModelUtils.get(modelB, i) if (!checkItemsEquality(itemA, itemB, roles)) return false @@ -79,11 +88,11 @@ QtObject { return true for (let i = 0; i < countA; i++) { - const itemA = modelA.get(i) + const itemA = Internal.ModelUtils.get(modelA, i) let found = false for (let j = 0; j < countB; j++) { - const itemB = modelB.get(j) + const itemB = Internal.ModelUtils.get(modelB, j) if (checkItemsEquality(itemA, itemB, roles)) found = true diff --git a/ui/StatusQ/src/modelutilsinternal.cpp b/ui/StatusQ/src/modelutilsinternal.cpp new file mode 100644 index 0000000000..7aeac16f23 --- /dev/null +++ b/ui/StatusQ/src/modelutilsinternal.cpp @@ -0,0 +1,56 @@ +#include "StatusQ/modelutilsinternal.h" + +#include + +ModelUtilsInternal::ModelUtilsInternal(QObject* parent) + : QObject(parent) +{ +} + +QStringList ModelUtilsInternal::roleNames(QAbstractItemModel *model) const +{ + if (model == nullptr) + return {}; + + QHash roles = model->roleNames(); + + QStringList strings; + strings.reserve(roles.size()); + + for (auto it = roles.begin(); it != roles.end(); ++it) + strings << QString::fromUtf8(it.value()); + + return strings; +} + + +int ModelUtilsInternal::roleByName(QAbstractItemModel* model, + const QString &roleName) const +{ + if (model == nullptr) + return -1; + + return model->roleNames().key(roleName.toUtf8(), -1); +} + +QVariantMap ModelUtilsInternal::get(QAbstractItemModel *model, int row) const +{ + QVariantMap map; + + if (model == nullptr) + return map; + + QModelIndex modelIndex = model->index(row, 0); + QHash roles = model->roleNames(); + + for (auto it = roles.begin(); it != roles.end(); ++it) + map.insert(it.value(), model->data(modelIndex, it.key())); + + return map; +} + +QVariant ModelUtilsInternal::get(QAbstractItemModel *model, + int row, const QString &roleName) const +{ + return model->data(model->index(row, 0), roleByName(model, roleName)); +} diff --git a/ui/StatusQ/src/typesregistration.cpp b/ui/StatusQ/src/typesregistration.cpp index 6c9a2e8352..01d4499039 100644 --- a/ui/StatusQ/src/typesregistration.cpp +++ b/ui/StatusQ/src/typesregistration.cpp @@ -1,17 +1,21 @@ #include "StatusQ/typesregistration.h" #include "StatusQ/QClipboardProxy.h" +#include "StatusQ/modelutilsinternal.h" +#include "StatusQ/rxvalidator.h" #include "StatusQ/statussyntaxhighlighter.h" #include "StatusQ/statuswindow.h" -#include "StatusQ/rxvalidator.h" #include void registerStatusQTypes() { - qmlRegisterType("StatusQ", 0 , 1, "StatusWindow"); + qmlRegisterType("StatusQ", 0 , 1, "StatusWindow"); qmlRegisterSingletonType("StatusQ", 0 , 1, "QClipboardProxy", &QClipboardProxy::qmlInstance); qmlRegisterType("StatusQ", 0 , 1, "StatusSyntaxHighlighter"); qmlRegisterType("StatusQ", 0 , 1, "RXValidator"); + + qmlRegisterSingletonType("StatusQ.Internal", 0 , 1, "ModelUtils", + &ModelUtilsInternal::qmlInstance); } diff --git a/ui/app/AppLayouts/Chat/controls/community/ListDropdownContent.qml b/ui/app/AppLayouts/Chat/controls/community/ListDropdownContent.qml index 25e26d6798..e53571e374 100644 --- a/ui/app/AppLayouts/Chat/controls/community/ListDropdownContent.qml +++ b/ui/app/AppLayouts/Chat/controls/community/ListDropdownContent.qml @@ -125,7 +125,7 @@ StatusListView { Item { id: sectionDelegateRoot - property string section: root.model && root.model.count ? + property string section: root.count ? qsTr("Search result") : qsTr("No results") diff --git a/ui/app/AppLayouts/Chat/controls/community/ThumbnailsDropdownContent.qml b/ui/app/AppLayouts/Chat/controls/community/ThumbnailsDropdownContent.qml index 7674157aab..8e9d44d83f 100644 --- a/ui/app/AppLayouts/Chat/controls/community/ThumbnailsDropdownContent.qml +++ b/ui/app/AppLayouts/Chat/controls/community/ThumbnailsDropdownContent.qml @@ -37,7 +37,7 @@ StatusScrollView { Layout.leftMargin: 8 Layout.topMargin: 8 - visible: !!model ? model.count === 0 : false + visible: repeater.count === 0 Layout.fillWidth: true @@ -56,6 +56,8 @@ StatusScrollView { columns: d.columns Repeater { + id: repeater + model: root.model delegate: ColumnLayout { spacing: 4 diff --git a/ui/app/AppLayouts/Chat/helpers/CommunityPermissionsHelpers.qml b/ui/app/AppLayouts/Chat/helpers/CommunityPermissionsHelpers.qml index 8fb5cba5ed..955a046de4 100644 --- a/ui/app/AppLayouts/Chat/helpers/CommunityPermissionsHelpers.qml +++ b/ui/app/AppLayouts/Chat/helpers/CommunityPermissionsHelpers.qml @@ -3,28 +3,31 @@ pragma Singleton import QtQml 2.14 import StatusQ.Core 0.1 - -import utils 1.0 +import StatusQ.Core.Utils 0.1 import AppLayouts.Chat.controls.community 1.0 QtObject { - function getTokenByKey(model, key) { if (!model) return null - for (let i = 0; i < model.count; i++) { - const item = model.get(i) + const count = model.rowCount() + + for (let i = 0; i < count; i++) { + const item = ModelUtils.get(model, i) + if (item.key === key) return item if (item.subItems) { const subitem = getTokenByKey(item.subItems, key) + if (subitem !== null) return subitem } } + return null } diff --git a/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml b/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml index f48fc15d00..ad2cfc395d 100644 --- a/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml +++ b/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml @@ -170,13 +170,14 @@ SettingsPageLayout { communityNewPermissionView.dirtyValues.isPrivate const model = root.store.permissionsModel + const count = model.rowCount() - for (let i = 0; i < model.count; i++) { + for (let i = 0; i < count; i++) { if (root.state === d.editPermissionViewState && d.permissionIndexToEdit === i) continue - const item = model.get(i) + const item = ModelUtils.get(model, i) const holdings = item.holdingsListModel const channels = item.channelsListModel @@ -255,7 +256,7 @@ SettingsPageLayout { store: root.store function setInitialValuesFromIndex(index) { - const item = root.store.permissionsModel.get(index) + const item = ModelUtils.get(root.store.permissionsModel, index) d.holdingsToEditModel = item.holdingsListModel d.channelsToEditModel = item.channelsListModel diff --git a/ui/app/AppLayouts/Chat/panels/communities/HoldingsListPanel.qml b/ui/app/AppLayouts/Chat/panels/communities/HoldingsListPanel.qml index e08f269349..f8141bc4c8 100644 --- a/ui/app/AppLayouts/Chat/panels/communities/HoldingsListPanel.qml +++ b/ui/app/AppLayouts/Chat/panels/communities/HoldingsListPanel.qml @@ -49,6 +49,8 @@ Control { spacing: d.defaultHoldingsSpacing Repeater { + id: repeater + model: root.model ColumnLayout { @@ -88,7 +90,7 @@ Control { Layout.alignment: Qt.AlignHCenter text: qsTr("or") textFormat: Text.StyledText - visible: (index !== root.model.count - 1) + visible: (index !== repeater.count - 1) } } } diff --git a/ui/app/AppLayouts/Chat/views/communities/HoldingsSelectionModel.qml b/ui/app/AppLayouts/Chat/views/communities/HoldingsSelectionModel.qml index 66fb25074d..f4d7017054 100644 --- a/ui/app/AppLayouts/Chat/views/communities/HoldingsSelectionModel.qml +++ b/ui/app/AppLayouts/Chat/views/communities/HoldingsSelectionModel.qml @@ -12,6 +12,15 @@ SortFilterProxyModel { property var assetsModel property var collectiblesModel + + readonly property ModelChangeTracker _assetsChanges: ModelChangeTracker { + model: assetsModel + } + + readonly property ModelChangeTracker _collectiblesChanges: ModelChangeTracker { + model: collectiblesModel + } + proxyRoles: [ ExpressionRole { name: "text" @@ -23,10 +32,9 @@ SortFilterProxyModel { const model = type === HoldingTypes.Type.Asset ? assetsModel : collectiblesModel - const item = CommunityPermissionsHelpers.getTokenByKey(model, key) - return item ? item.name : "" + return item ? item.shortName || item.name : "" } function getText(type, key, amount) { @@ -38,7 +46,11 @@ SortFilterProxyModel { // Direct call for singleton function is not handled properly by // SortFilterProxyModel that's why helper function is used instead. - expression: getText(model.type, model.key, model.amount) + expression: { + _assetsChanges.revision + _collectiblesChanges.revision + getText(model.type, model.key, model.amount) + } }, ExpressionRole { name: "imageSource" @@ -53,7 +65,11 @@ SortFilterProxyModel { return CommunityPermissionsHelpers.getTokenIconByKey(model, key) } - expression: getIcon(model.type, model.key) + expression: { + _assetsChanges.revision + _collectiblesChanges.revision + getIcon(model.type, model.key) + } }, ExpressionRole { name: "operator" diff --git a/vendor/DOtherSide/lib/src/DOtherSide.cpp b/vendor/DOtherSide/lib/src/DOtherSide.cpp index d34fb1565c..89dd7f63e9 100644 --- a/vendor/DOtherSide/lib/src/DOtherSide.cpp +++ b/vendor/DOtherSide/lib/src/DOtherSide.cpp @@ -73,6 +73,7 @@ #include "DOtherSide/Status/SoundManager.h" #include "StatusQ/QClipboardProxy.h" +#include "StatusQ/modelutilsinternal.h" #include "StatusQ/rxvalidator.h" #include "StatusQ/statussyntaxhighlighter.h" #include "StatusQ/statuswindow.h" @@ -94,7 +95,8 @@ void register_meta_types() qmlRegisterType("StatusQ", 0 , 1, "StatusSyntaxHighlighter"); qmlRegisterType("StatusQ", 0, 1, "RXValidator"); qmlRegisterSingletonType("StatusQ", 0 , 1, "QClipboardProxy", &QClipboardProxy::qmlInstance); - + qmlRegisterSingletonType("StatusQ.Internal", 0 , 1, "ModelUtils", + &ModelUtilsInternal::qmlInstance); #ifdef MONITORING qmlRegisterSingletonType("Monitoring", 1 , 0, "Monitor", &Monitor::qmlInstance); #endif