Skip to content
Snippets Groups Projects
Verified Commit 051c25d5 authored by Nicolas Werner's avatar Nicolas Werner
Browse files

Allow editing permissions in spaces recursively

parent 0752f947
No related branches found
No related tags found
No related merge requests found
Pipeline #3701 failed
...@@ -78,6 +78,22 @@ Pane { ...@@ -78,6 +78,22 @@ Pane {
destroyOnClose(dialog); destroyOnClose(dialog);
} }
Component {
id: plApplyPrompt
PowerLevelSpacesApplyDialog {
}
}
function showSpacePLApplyPrompt(settings, editingModel) {
var dialog = plApplyPrompt.createObject(timelineRoot, {
"roomSettings": settings,
"editingModel": editingModel
});
dialog.show();
destroyOnClose(dialog);
}
Component { Component {
id: plEditor id: plEditor
......
...@@ -397,8 +397,15 @@ ApplicationWindow { ...@@ -397,8 +397,15 @@ ApplicationWindow {
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
onAccepted: { onAccepted: {
editingModel.commit(); if (editingModel.isSpace) {
plEditorW.close(); // TODO(Nico): Replace with showing a list of spaces to apply to
editingModel.updateSpacesModel();
plEditorW.close();
timelineRoot.showSpacePLApplyPrompt(roomSettings, editingModel)
} else {
editingModel.commit();
plEditorW.close();
}
} }
onRejected: plEditorW.close(); onRejected: plEditorW.close();
} }
......
// SPDX-FileCopyrightText: 2022 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import ".."
import "../ui"
import Qt.labs.platform 1.1 as Platform
import QtQuick 2.15
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.13
import im.nheko 1.0
ApplicationWindow {
id: applyDialog
property RoomSettings roomSettings
property PowerlevelEditingModels editingModel
minimumWidth: 340
minimumHeight: 450
width: 450
height: 680
palette: Nheko.colors
color: Nheko.colors.window
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
title: qsTr("Apply permission changes")
Shortcut {
sequence: StandardKey.Cancel
onActivated: roomSettingsDialog.close()
}
ColumnLayout {
anchors.margins: Nheko.paddingMedium
anchors.fill: parent
spacing: Nheko.paddingLarge
MatrixText {
text: qsTr("Which of the subcommunities and rooms should these permissions be applied to?")
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
Layout.fillWidth: true
Layout.fillHeight: false
color: Nheko.colors.text
Layout.bottomMargin: Nheko.paddingMedium
}
GridLayout {
Layout.fillWidth: true
Layout.fillHeight: false
columns: 2
Label {
text: qsTr("Apply permissions recursively")
Layout.fillWidth: true
color: Nheko.colors.text
}
ToggleButton {
checked: editingModel.spaces.applyToChildren
Layout.alignment: Qt.AlignRight
onCheckedChanged: editingModel.spaces.applyToChildren = checked
}
Label {
text: qsTr("Overwrite exisiting modifications in rooms")
Layout.fillWidth: true
color: Nheko.colors.text
}
ToggleButton {
checked: editingModel.spaces.overwriteDiverged
Layout.alignment: Qt.AlignRight
onCheckedChanged: editingModel.spaces.overwriteDiverged = checked
}
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
id: view
clip: true
ScrollHelper {
flickable: parent
anchors.fill: parent
}
model: editingModel.spaces
spacing: 4
cacheBuffer: 50
delegate: RowLayout {
anchors.left: parent.left
anchors.right: parent.right
ColumnLayout {
Layout.fillWidth: true
Text {
Layout.fillWidth: true
text: model.displayName
color: Nheko.colors.text
textFormat: Text.PlainText
elide: Text.ElideRight
}
Text {
Layout.fillWidth: true
text: {
if (!model.isEditable) return qsTr("No permissions to apply the new permissions here");
if (model.isAlreadyUpToDate) return qsTr("No changes needed");
if (model.isDifferentFromBase) return qsTr("Existing modifications to the permissions in this room will be overwritten");
return qsTr("Permissions synchronized with community")
}
elide: Text.ElideRight
color: Nheko.colors.buttonText
textFormat: Text.PlainText
}
}
ToggleButton {
checked: model.applyPermissions
Layout.alignment: Qt.AlignRight
onCheckedChanged: model.applyPermissions = checked
enabled: model.isEditable
}
}
}
}
footer: DialogButtonBox {
id: dbb
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
onAccepted: {
editingModel.spaces.commit();
applyDialog.close();
}
onRejected: applyDialog.close()
}
}
...@@ -162,6 +162,7 @@ ...@@ -162,6 +162,7 @@
<file>qml/dialogs/LogoutDialog.qml</file> <file>qml/dialogs/LogoutDialog.qml</file>
<file>qml/dialogs/PhoneNumberInputDialog.qml</file> <file>qml/dialogs/PhoneNumberInputDialog.qml</file>
<file>qml/dialogs/PowerLevelEditor.qml</file> <file>qml/dialogs/PowerLevelEditor.qml</file>
<file>qml/dialogs/PowerLevelSpacesApplyDialog.qml</file>
<file>qml/dialogs/RawMessageDialog.qml</file> <file>qml/dialogs/RawMessageDialog.qml</file>
<file>qml/dialogs/ReadReceipts.qml</file> <file>qml/dialogs/ReadReceipts.qml</file>
<file>qml/dialogs/RoomDirectory.qml</file> <file>qml/dialogs/RoomDirectory.qml</file>
......
...@@ -4,8 +4,12 @@ ...@@ -4,8 +4,12 @@
#include "PowerlevelsEditModels.h" #include "PowerlevelsEditModels.h"
#include <QCoreApplication>
#include <QTimer>
#include <algorithm> #include <algorithm>
#include <set> #include <set>
#include <unordered_set>
#include "Cache.h" #include "Cache.h"
#include "Cache_p.h" #include "Cache_p.h"
...@@ -76,7 +80,7 @@ PowerlevelsTypeListModel::PowerlevelsTypeListModel(const std::string &rid, ...@@ -76,7 +80,7 @@ PowerlevelsTypeListModel::PowerlevelsTypeListModel(const std::string &rid,
} }
std::map<std::string, mtx::events::state::power_level_t, std::less<>> std::map<std::string, mtx::events::state::power_level_t, std::less<>>
PowerlevelsTypeListModel::toEvents() PowerlevelsTypeListModel::toEvents() const
{ {
std::map<std::string, mtx::events::state::power_level_t, std::less<>> m; std::map<std::string, mtx::events::state::power_level_t, std::less<>> m;
for (const auto &[key, pl] : qAsConst(types)) for (const auto &[key, pl] : qAsConst(types))
...@@ -85,7 +89,7 @@ PowerlevelsTypeListModel::toEvents() ...@@ -85,7 +89,7 @@ PowerlevelsTypeListModel::toEvents()
return m; return m;
} }
mtx::events::state::power_level_t mtx::events::state::power_level_t
PowerlevelsTypeListModel::kick() PowerlevelsTypeListModel::kick() const
{ {
for (const auto &[key, pl] : qAsConst(types)) for (const auto &[key, pl] : qAsConst(types))
if (key == "kick") if (key == "kick")
...@@ -93,7 +97,7 @@ PowerlevelsTypeListModel::kick() ...@@ -93,7 +97,7 @@ PowerlevelsTypeListModel::kick()
return powerLevels_.users_default; return powerLevels_.users_default;
} }
mtx::events::state::power_level_t mtx::events::state::power_level_t
PowerlevelsTypeListModel::invite() PowerlevelsTypeListModel::invite() const
{ {
for (const auto &[key, pl] : qAsConst(types)) for (const auto &[key, pl] : qAsConst(types))
if (key == "invite") if (key == "invite")
...@@ -101,7 +105,7 @@ PowerlevelsTypeListModel::invite() ...@@ -101,7 +105,7 @@ PowerlevelsTypeListModel::invite()
return powerLevels_.users_default; return powerLevels_.users_default;
} }
mtx::events::state::power_level_t mtx::events::state::power_level_t
PowerlevelsTypeListModel::ban() PowerlevelsTypeListModel::ban() const
{ {
for (const auto &[key, pl] : qAsConst(types)) for (const auto &[key, pl] : qAsConst(types))
if (key == "ban") if (key == "ban")
...@@ -109,7 +113,7 @@ PowerlevelsTypeListModel::ban() ...@@ -109,7 +113,7 @@ PowerlevelsTypeListModel::ban()
return powerLevels_.users_default; return powerLevels_.users_default;
} }
mtx::events::state::power_level_t mtx::events::state::power_level_t
PowerlevelsTypeListModel::eventsDefault() PowerlevelsTypeListModel::eventsDefault() const
{ {
for (const auto &[key, pl] : qAsConst(types)) for (const auto &[key, pl] : qAsConst(types))
if (key == "zdefault_events") if (key == "zdefault_events")
...@@ -117,7 +121,7 @@ PowerlevelsTypeListModel::eventsDefault() ...@@ -117,7 +121,7 @@ PowerlevelsTypeListModel::eventsDefault()
return powerLevels_.users_default; return powerLevels_.users_default;
} }
mtx::events::state::power_level_t mtx::events::state::power_level_t
PowerlevelsTypeListModel::stateDefault() PowerlevelsTypeListModel::stateDefault() const
{ {
for (const auto &[key, pl] : qAsConst(types)) for (const auto &[key, pl] : qAsConst(types))
if (key == "zdefault_states") if (key == "zdefault_states")
...@@ -390,7 +394,7 @@ PowerlevelsUserListModel::PowerlevelsUserListModel(const std::string &rid, ...@@ -390,7 +394,7 @@ PowerlevelsUserListModel::PowerlevelsUserListModel(const std::string &rid,
} }
std::map<std::string, mtx::events::state::power_level_t, std::less<>> std::map<std::string, mtx::events::state::power_level_t, std::less<>>
PowerlevelsUserListModel::toUsers() PowerlevelsUserListModel::toUsers() const
{ {
std::map<std::string, mtx::events::state::power_level_t, std::less<>> m; std::map<std::string, mtx::events::state::power_level_t, std::less<>> m;
for (const auto &[key, pl] : qAsConst(users)) for (const auto &[key, pl] : qAsConst(users))
...@@ -399,7 +403,7 @@ PowerlevelsUserListModel::toUsers() ...@@ -399,7 +403,7 @@ PowerlevelsUserListModel::toUsers()
return m; return m;
} }
mtx::events::state::power_level_t mtx::events::state::power_level_t
PowerlevelsUserListModel::usersDefault() PowerlevelsUserListModel::usersDefault() const
{ {
for (const auto &[key, pl] : qAsConst(users)) for (const auto &[key, pl] : qAsConst(users))
if (key == "default") if (key == "default")
...@@ -565,6 +569,7 @@ PowerlevelEditingModels::PowerlevelEditingModels(QString room_id, QObject *paren ...@@ -565,6 +569,7 @@ PowerlevelEditingModels::PowerlevelEditingModels(QString room_id, QObject *paren
.content) .content)
, types_(room_id.toStdString(), powerLevels_, this) , types_(room_id.toStdString(), powerLevels_, this)
, users_(room_id.toStdString(), powerLevels_, this) , users_(room_id.toStdString(), powerLevels_, this)
, spaces_(room_id.toStdString(), powerLevels_, this)
, room_id_(room_id.toStdString()) , room_id_(room_id.toStdString())
{ {
connect(&types_, connect(&types_,
...@@ -581,17 +586,31 @@ PowerlevelEditingModels::PowerlevelEditingModels(QString room_id, QObject *paren ...@@ -581,17 +586,31 @@ PowerlevelEditingModels::PowerlevelEditingModels(QString room_id, QObject *paren
&PowerlevelEditingModels::defaultUserLevelChanged); &PowerlevelEditingModels::defaultUserLevelChanged);
} }
bool
PowerlevelEditingModels::isSpace() const
{
return cache::singleRoomInfo(room_id_).is_space;
}
mtx::events::state::PowerLevels
PowerlevelEditingModels::calculateNewPowerlevel() const
{
auto newPl = powerLevels_;
newPl.events = types_.toEvents();
newPl.kick = types_.kick();
newPl.invite = types_.invite();
newPl.ban = types_.ban();
newPl.events_default = types_.eventsDefault();
newPl.state_default = types_.stateDefault();
newPl.users = users_.toUsers();
newPl.users_default = users_.usersDefault();
return newPl;
}
void void
PowerlevelEditingModels::commit() PowerlevelEditingModels::commit()
{ {
powerLevels_.events = types_.toEvents(); powerLevels_ = calculateNewPowerlevel();
powerLevels_.kick = types_.kick();
powerLevels_.invite = types_.invite();
powerLevels_.ban = types_.ban();
powerLevels_.events_default = types_.eventsDefault();
powerLevels_.state_default = types_.stateDefault();
powerLevels_.users = users_.toUsers();
powerLevels_.users_default = users_.usersDefault();
http::client()->send_state_event( http::client()->send_state_event(
room_id_, powerLevels_, [](const mtx::responses::EventId &, mtx::http::RequestErr e) { room_id_, powerLevels_, [](const mtx::responses::EventId &, mtx::http::RequestErr e) {
...@@ -604,6 +623,13 @@ PowerlevelEditingModels::commit() ...@@ -604,6 +623,13 @@ PowerlevelEditingModels::commit()
}); });
} }
void
PowerlevelEditingModels::updateSpacesModel()
{
powerLevels_ = calculateNewPowerlevel();
spaces_.newPowerlevels_ = powerLevels_;
}
void void
PowerlevelEditingModels::addRole(int pl) PowerlevelEditingModels::addRole(int pl)
{ {
...@@ -614,3 +640,184 @@ PowerlevelEditingModels::addRole(int pl) ...@@ -614,3 +640,184 @@ PowerlevelEditingModels::addRole(int pl)
types_.addRole(pl); types_.addRole(pl);
users_.addRole(pl); users_.addRole(pl);
} }
static bool
samePl(const mtx::events::state::PowerLevels &a, const mtx::events::state::PowerLevels &b)
{
return std::tie(a.events,
a.users_default,
a.users,
a.state_default,
a.users_default,
a.events_default,
a.ban,
a.kick,
a.invite,
a.notifications,
a.redact) == std::tie(b.events,
b.users_default,
b.users,
b.state_default,
b.users_default,
b.events_default,
b.ban,
b.kick,
b.invite,
b.notifications,
b.redact);
}
PowerlevelsSpacesListModel::PowerlevelsSpacesListModel(const std::string &room_id_,
const mtx::events::state::PowerLevels &pl,
QObject *parent)
: QAbstractListModel(parent)
, room_id(std::move(room_id_))
, oldPowerLevels_(std::move(pl))
{
beginResetModel();
spaces.push_back(Entry{room_id, oldPowerLevels_, true});
std::unordered_set<std::string> visited;
std::function<void(const std::string &)> addChildren;
addChildren = [this, &addChildren, &visited](const std::string &space) {
if (visited.count(space))
return;
else
visited.insert(space);
for (const auto &s : cache::client()->getChildRoomIds(space)) {
auto parent =
cache::client()->getStateEvent<mtx::events::state::space::Parent>(s, space);
if (parent && parent->content.via && !parent->content.via->empty() &&
parent->content.canonical) {
auto parent = cache::client()->getStateEvent<mtx::events::state::PowerLevels>(s);
spaces.push_back(
Entry{s, parent ? parent->content : mtx::events::state::PowerLevels{}, false});
addChildren(s);
}
}
};
addChildren(room_id);
endResetModel();
updateToDefaults();
}
struct PowerLevelApplier
{
std::vector<std::string> spaces;
mtx::events::state::PowerLevels pl;
void next()
{
if (spaces.empty())
return;
auto room_id_ = spaces.back();
http::client()->send_state_event(
room_id_,
pl,
[self = *this](const mtx::responses::EventId &, mtx::http::RequestErr e) mutable {
if (e) {
if (e->status_code == 429 && e->matrix_error.retry_after.count() != 0) {
QTimer::singleShot(e->matrix_error.retry_after,
[self = std::move(self)]() mutable { self.next(); });
return;
}
nhlog::net()->error("Failed to send PL event: {}", *e);
ChatPage::instance()->showNotification(
QCoreApplication::translate("PowerLevels", "Failed to update powerlevel: %1")
.arg(QString::fromStdString(e->matrix_error.error)));
}
self.spaces.pop_back();
self.next();
});
}
};
void
PowerlevelsSpacesListModel::commit()
{
std::vector<std::string> spacesToApplyTo;
for (const auto &s : spaces)
if (s.apply)
spacesToApplyTo.push_back(s.roomid);
PowerLevelApplier context{std::move(spacesToApplyTo), newPowerlevels_};
context.next();
}
void
PowerlevelsSpacesListModel::updateToDefaults()
{
for (int i = 1; i < spaces.size(); i++) {
spaces[i].apply =
applyToChildren_ && data(index(i), Roles::IsEditable).toBool() &&
!data(index(i), Roles::IsAlreadyUpToDate).toBool() &&
(overwriteDiverged_ || !data(index(i), Roles::IsDifferentFromBase).toBool());
}
if (spaces.size() > 1)
emit dataChanged(index(1), index(spaces.size() - 1), {Roles::ApplyPermissions});
}
bool
PowerlevelsSpacesListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Roles::ApplyPermissions || index.row() < 0 || index.row() >= spaces.size())
return false;
spaces[index.row()].apply = value.toBool();
return true;
}
QVariant
PowerlevelsSpacesListModel::data(QModelIndex const &index, int role) const
{
auto row = index.row();
if (row >= spaces.size() && row < 0)
return {};
if (role == Roles::DisplayName || role == Roles::AvatarUrl || role == Roles::IsSpace) {
auto info = cache::singleRoomInfo(spaces.at(row).roomid);
if (role == Roles::DisplayName)
return QString::fromStdString(info.name);
else if (role == Roles::AvatarUrl)
return QString::fromStdString(info.avatar_url);
else
return info.is_space;
}
auto entry = spaces.at(row);
switch (role) {
case Roles::IsEditable:
return entry.pl.user_level(http::client()->user_id().to_string()) >=
entry.pl.state_level(to_string(mtx::events::EventType::RoomPowerLevels));
case Roles::IsDifferentFromBase:
return !samePl(entry.pl, oldPowerLevels_);
case Roles::IsAlreadyUpToDate:
return samePl(entry.pl, newPowerlevels_);
case Roles::ApplyPermissions:
return entry.apply;
}
return {};
}
QHash<int, QByteArray>
PowerlevelsSpacesListModel::roleNames() const
{
return {
{DisplayName, "displayName"},
{AvatarUrl, "avatarUrl"},
{IsEditable, "isEditable"},
{IsDifferentFromBase, "isDifferentFromBase"},
{IsAlreadyUpToDate, "isAlreadyUpToDate"},
{ApplyPermissions, "applyPermissions"},
};
}
...@@ -48,12 +48,12 @@ public: ...@@ -48,12 +48,12 @@ public:
const QModelIndex &destinationParent, const QModelIndex &destinationParent,
int destinationChild) override; int destinationChild) override;
std::map<std::string, mtx::events::state::power_level_t, std::less<>> toEvents(); std::map<std::string, mtx::events::state::power_level_t, std::less<>> toEvents() const;
mtx::events::state::power_level_t kick(); mtx::events::state::power_level_t kick() const;
mtx::events::state::power_level_t invite(); mtx::events::state::power_level_t invite() const;
mtx::events::state::power_level_t ban(); mtx::events::state::power_level_t ban() const;
mtx::events::state::power_level_t eventsDefault(); mtx::events::state::power_level_t eventsDefault() const;
mtx::events::state::power_level_t stateDefault(); mtx::events::state::power_level_t stateDefault() const;
struct Entry struct Entry
{ {
...@@ -106,8 +106,8 @@ public: ...@@ -106,8 +106,8 @@ public:
const QModelIndex &destinationParent, const QModelIndex &destinationParent,
int destinationChild) override; int destinationChild) override;
std::map<std::string, mtx::events::state::power_level_t, std::less<>> toUsers(); std::map<std::string, mtx::events::state::power_level_t, std::less<>> toUsers() const;
mtx::events::state::power_level_t usersDefault(); mtx::events::state::power_level_t usersDefault() const;
struct Entry struct Entry
{ {
...@@ -122,38 +122,117 @@ public: ...@@ -122,38 +122,117 @@ public:
mtx::events::state::PowerLevels powerLevels_; mtx::events::state::PowerLevels powerLevels_;
}; };
class PowerlevelsSpacesListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(bool applyToChildren READ applyToChildren WRITE setApplyToChildren NOTIFY
applyToChildrenChanged)
Q_PROPERTY(bool overwriteDiverged READ overwriteDiverged WRITE setOverwriteDiverged NOTIFY
overwriteDivergedChanged)
signals:
void applyToChildrenChanged();
void overwriteDivergedChanged();
public:
enum Roles
{
DisplayName,
AvatarUrl,
IsSpace,
IsEditable,
IsDifferentFromBase,
IsAlreadyUpToDate,
ApplyPermissions,
};
explicit PowerlevelsSpacesListModel(const std::string &room_id_,
const mtx::events::state::PowerLevels &pl,
QObject *parent = nullptr);
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &) const override { return static_cast<int>(spaces.size()); }
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool
setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override;
bool applyToChildren() const { return applyToChildren_; }
bool overwriteDiverged() const { return overwriteDiverged_; }
void setApplyToChildren(bool val)
{
applyToChildren_ = val;
emit applyToChildrenChanged();
updateToDefaults();
}
void setOverwriteDiverged(bool val)
{
overwriteDiverged_ = val;
emit overwriteDivergedChanged();
updateToDefaults();
}
void updateToDefaults();
Q_INVOKABLE void commit();
struct Entry
{
~Entry() = default;
std::string roomid;
mtx::events::state::PowerLevels pl;
bool apply = false;
};
std::string room_id;
QVector<Entry> spaces;
mtx::events::state::PowerLevels oldPowerLevels_, newPowerlevels_;
bool applyToChildren_ = true, overwriteDiverged_ = false;
};
class PowerlevelEditingModels : public QObject class PowerlevelEditingModels : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(PowerlevelsUserListModel *users READ users CONSTANT) Q_PROPERTY(PowerlevelsUserListModel *users READ users CONSTANT)
Q_PROPERTY(PowerlevelsTypeListModel *types READ types CONSTANT) Q_PROPERTY(PowerlevelsTypeListModel *types READ types CONSTANT)
Q_PROPERTY(PowerlevelsSpacesListModel *spaces READ spaces CONSTANT)
Q_PROPERTY(qlonglong adminLevel READ adminLevel NOTIFY adminLevelChanged) Q_PROPERTY(qlonglong adminLevel READ adminLevel NOTIFY adminLevelChanged)
Q_PROPERTY(qlonglong moderatorLevel READ moderatorLevel NOTIFY moderatorLevelChanged) Q_PROPERTY(qlonglong moderatorLevel READ moderatorLevel NOTIFY moderatorLevelChanged)
Q_PROPERTY(qlonglong defaultUserLevel READ defaultUserLevel NOTIFY defaultUserLevelChanged) Q_PROPERTY(qlonglong defaultUserLevel READ defaultUserLevel NOTIFY defaultUserLevelChanged)
Q_PROPERTY(bool isSpace READ isSpace CONSTANT)
signals: signals:
void adminLevelChanged(); void adminLevelChanged();
void moderatorLevelChanged(); void moderatorLevelChanged();
void defaultUserLevelChanged(); void defaultUserLevelChanged();
private:
mtx::events::state::PowerLevels calculateNewPowerlevel() const;
public: public:
explicit PowerlevelEditingModels(QString room_id, QObject *parent = nullptr); explicit PowerlevelEditingModels(QString room_id, QObject *parent = nullptr);
PowerlevelsUserListModel *users() { return &users_; } PowerlevelsUserListModel *users() { return &users_; }
PowerlevelsTypeListModel *types() { return &types_; } PowerlevelsTypeListModel *types() { return &types_; }
PowerlevelsSpacesListModel *spaces() { return &spaces_; }
qlonglong adminLevel() const qlonglong adminLevel() const
{ {
return powerLevels_.state_level(to_string(mtx::events::EventType::RoomPowerLevels)); return powerLevels_.state_level(to_string(mtx::events::EventType::RoomPowerLevels));
} }
qlonglong moderatorLevel() const { return powerLevels_.redact; } qlonglong moderatorLevel() const { return powerLevels_.redact; }
qlonglong defaultUserLevel() const { return powerLevels_.users_default; } qlonglong defaultUserLevel() const { return powerLevels_.users_default; }
bool isSpace() const;
Q_INVOKABLE void commit(); Q_INVOKABLE void commit();
Q_INVOKABLE void updateSpacesModel();
Q_INVOKABLE void addRole(int pl); Q_INVOKABLE void addRole(int pl);
mtx::events::state::PowerLevels powerLevels_; mtx::events::state::PowerLevels powerLevels_;
PowerlevelsTypeListModel types_; PowerlevelsTypeListModel types_;
PowerlevelsUserListModel users_; PowerlevelsUserListModel users_;
PowerlevelsSpacesListModel spaces_;
std::string room_id_; std::string room_id_;
}; };
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment