chore(SharedAddressesPermissionsPanel): optimize the speed

- don't use the expensive `ExpressionFoo` in SFPM, just use
`AnyOf/AllOf` combinations where possible
- in HoldingsSelectionModel, don't call the `getTokenByKey` twice to
construct the `text`, once is enough
- lastly, rewrite the JS helper `PermissionsHelpers.getTokenByKey` to
C++; this method gets called recursively way too often from many places

In the longterm, we should provide a specific C++ transformation model
for SharedAddressesPermissionsPanel to follow the UI requirements more
closely; that way we'd be able to fix the issues here for good

Fixes #14276
This commit is contained in:
Lukáš Tinkl 2024-04-04 13:06:44 +02:00 committed by Lukáš Tinkl
parent 795718b39f
commit 375de59cfa
5 changed files with 147 additions and 79 deletions

View File

@ -16,6 +16,8 @@ class PermissionUtilsInternal : public QObject
public:
explicit PermissionUtilsInternal(QObject* parent = nullptr);
Q_INVOKABLE QVariantMap getTokenByKey(QAbstractItemModel *model, const QVariant& keyValue) const;
//!< traverse the permissions @p model, and look for unique token keys recursively under holdingsListModel->key
Q_INVOKABLE QStringList getUniquePermissionTokenKeys(QAbstractItemModel *model, int type) const;

View File

@ -194,3 +194,36 @@ int /*PermissionTypes::Type*/ PermissionUtilsInternal::isEligibleToJoinAs(QAbstr
return PermissionTypes::Type::None;
}
QVariantMap PermissionUtilsInternal::getTokenByKey(QAbstractItemModel *model,
const QVariant &keyValue) const
{
if (!model)
return {};
const auto roles = model->roleNames();
const auto keyRole = roles.key(QByteArrayLiteral("key"), -1);
const auto subItemsRole = roles.key(QByteArrayLiteral("subItems"), -1);
const auto count = model->rowCount();
for (int i = 0; i < count; i++) {
const auto modelIndex = model->index(i, 0);
if (keyRole != -1 && modelIndex.data(keyRole) == keyValue) {
QVariantMap result;
for (auto it = roles.cbegin(); it != roles.cend(); ++it)
result.insert(it.value(), modelIndex.data(it.key()));
return result;
}
if (subItemsRole != -1) {
const auto subItemModel = qvariant_cast<QAbstractItemModel *>(modelIndex.data(subItemsRole));
if (subItemModel) {
const auto subItem = getTokenByKey(subItemModel, keyValue);
if (!subItem.isEmpty())
return subItem;
}
}
}
return {};
}

View File

@ -12,26 +12,7 @@ import utils 1.0
QtObject {
function getTokenByKey(model, key) {
if (!model)
return null
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
return Internal.PermissionUtils.getTokenByKey(model, key)
}
function getTokenNameByKey(model, key) {

View File

@ -53,7 +53,7 @@ Rectangle {
readonly property bool lostPermissionToJoin: root.isEditMode && joinPermissionsModel.count && !joinPermissionPanel.tokenCriteriaMet
readonly property var uniquePermissionChannels:
root.permissionsModel && root.permissionsModel.count ?
d.channelsPermissionsModel.count ?
PermissionsHelpers.getUniquePermissionChannels(root.permissionsModel, [PermissionTypes.Type.Read, PermissionTypes.Type.ViewAndPost])
: []
@ -74,51 +74,102 @@ Rectangle {
readonly property var tokenMasterPermissionsModel: SortFilterProxyModel {
id: tokenMasterPermissionsModel
sourceModel: root.permissionsModel
function filterPredicate(permissionType, tokenCriteriaMet) {
return (permissionType === Constants.permissionType.becomeTokenMaster) && tokenCriteriaMet
}
filters: FastExpressionFilter {
expression: tokenMasterPermissionsModel.filterPredicate(model.permissionType, model.tokenCriteriaMet)
expectedRoles: ["permissionType", "tokenCriteriaMet"]
}
filters: [
ValueFilter {
roleName: "permissionType"
value: Constants.permissionType.becomeTokenMaster
},
ValueFilter {
roleName: "tokenCriteriaMet"
value: true
}
]
}
readonly property var adminPermissionsModel: SortFilterProxyModel {
id: adminPermissionsModel
sourceModel: root.permissionsModel
function filterPredicate(permissionType, tokenCriteriaMet, isPrivate) {
return (permissionType === Constants.permissionType.admin) &&
(!isPrivate || (tokenCriteriaMet && isPrivate)) // visible or (hidden & met)
}
filters: FastExpressionFilter {
expression: adminPermissionsModel.filterPredicate(model.permissionType, model.tokenCriteriaMet, model.isPrivate)
expectedRoles: ["permissionType", "tokenCriteriaMet", "isPrivate"]
}
filters: [
ValueFilter {
roleName: "permissionType"
value: Constants.permissionType.admin
},
AnyOf {
ValueFilter {
roleName: "isPrivate"
value: false
}
AllOf {
ValueFilter {
roleName: "tokenCriteriaMet"
value: true
}
ValueFilter {
roleName: "isPrivate"
value: true
}
}
}
]
}
readonly property var joinPermissionsModel: SortFilterProxyModel {
id: joinPermissionsModel
sourceModel: root.permissionsModel
function filterPredicate(permissionType, tokenCriteriaMet, isPrivate) {
return (permissionType === Constants.permissionType.member) &&
(!isPrivate || (tokenCriteriaMet && isPrivate)) // visible or (hidden & met)
}
filters: FastExpressionFilter {
expression: joinPermissionsModel.filterPredicate(model.permissionType, model.tokenCriteriaMet, model.isPrivate)
expectedRoles: ["permissionType", "tokenCriteriaMet", "isPrivate"]
}
filters: [
ValueFilter {
roleName: "permissionType"
value: Constants.permissionType.member
},
AnyOf {
ValueFilter {
roleName: "isPrivate"
value: false
}
AllOf {
ValueFilter {
roleName: "tokenCriteriaMet"
value: true
}
ValueFilter {
roleName: "isPrivate"
value: true
}
}
}
]
}
// used to check if there are any visible channel permissions
readonly property var channelsPermissionsModel: SortFilterProxyModel {
id: channelsPermissionsModel
sourceModel: root.permissionsModel
function filterPredicate(permissionType, tokenCriteriaMet, isPrivate) {
return (permissionType === Constants.permissionType.read || permissionType === Constants.permissionType.viewAndPost) &&
(!isPrivate || (tokenCriteriaMet && isPrivate)) // visible or (hidden & met)
}
filters: FastExpressionFilter {
expression: channelsPermissionsModel.filterPredicate(model.permissionType, model.tokenCriteriaMet, model.isPrivate)
expectedRoles: ["permissionType", "tokenCriteriaMet", "isPrivate"]
}
filters: [
AnyOf {
ValueFilter {
roleName: "permissionType"
value: Constants.permissionType.read
}
ValueFilter {
roleName: "permissionType"
value: Constants.permissionType.viewAndPost
}
},
AnyOf {
ValueFilter {
roleName: "isPrivate"
value: false
}
AllOf {
ValueFilter {
roleName: "tokenCriteriaMet"
value: true
}
ValueFilter {
roleName: "isPrivate"
value: true
}
}
}
]
}
}
@ -277,7 +328,7 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: Theme.tertiaryTextFontSize
text: model.text
color: model.available ? Theme.palette.successColor1 : Theme.palette.directColor1
color: model.available ? Theme.palette.successColor1 : Theme.palette.baseColor1
}
}
}
@ -488,17 +539,22 @@ Rectangle {
model: SortFilterProxyModel {
id: channelPermissionsModel
sourceModel: root.permissionsModel
function filterPredicate(permissionType, tokenCriteriaMet, isPrivate, channelsListModel) {
return permissionType === channelPermsSubPanel.permissionType &&
(!isPrivate || (tokenCriteriaMet && isPrivate)) &&
ModelUtils.contains(channelsListModel, "key", channelPermsPanel.channelKey) // filter and group by channel "key"
}
filters: FastExpressionFilter {
expression: channelPermissionsModel.filterPredicate(model.permissionType, model.tokenCriteriaMet,
model.isPrivate, model.channelsListModel)
expectedRoles: ["permissionType", "tokenCriteriaMet", "isPrivate", "channelsListModel"]
sourceModel: d.channelsPermissionsModel
function filterPredicate(channelsListModel) {
return ModelUtils.contains(channelsListModel, "key", channelPermsPanel.channelKey)
}
filters: [
ValueFilter {
roleName: "permissionType"
value: channelPermsSubPanel.permissionType
},
FastExpressionFilter {
expression: channelPermissionsModel.filterPredicate(model.channelsListModel) // filter and group by channel "key"
expectedRoles: ["channelsListModel"]
}
]
}
delegate: ColumnLayout {
Layout.column: 0

View File

@ -25,29 +25,26 @@ SortFilterProxyModel {
FastExpressionRole {
name: "text"
function getName(type, key) {
function getName(type, item, key) {
if (type === Constants.TokenType.ENS)
return key
return item ? item.symbol || item.shortName || item.name : ""
}
function getDecimals(type, item) {
if (type !== Constants.TokenType.ERC20)
return 0
return item.decimals
}
function getText(type, key, amount) {
const model = type === Constants.TokenType.ERC20
? assetsModel
: collectiblesModel
const item = PermissionsHelpers.getTokenByKey(model, key)
return item ? item.symbol || item.shortName || item.name : ""
}
function getDecimals(type, key) {
if (type !== Constants.TokenType.ERC20) {
return 0
}
const item = PermissionsHelpers.getTokenByKey(assetsModel, key)
return item.decimals
}
function getText(type, key, amount) {
const name = getName(type, key)
const decimals = getDecimals(type, key)
const name = getName(type, item, key)
const decimals = getDecimals(type, item)
return PermissionsHelpers.setHoldingsTextFormat(
type, name, amount, decimals)
@ -89,7 +86,6 @@ SortFilterProxyModel {
readonly property int none: OperatorsUtils.Operators.None
expression: none
expectedRoles: []
}
]
}