mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Allow editing permissions in spaces recursively
This commit is contained in:
parent
0752f9477e
commit
051c25d5b8
6 changed files with 484 additions and 26 deletions
|
@ -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,9 +397,16 @@ ApplicationWindow {
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
|
if (editingModel.isSpace) {
|
||||||
|
// TODO(Nico): Replace with showing a list of spaces to apply to
|
||||||
|
editingModel.updateSpacesModel();
|
||||||
|
plEditorW.close();
|
||||||
|
timelineRoot.showSpacePLApplyPrompt(roomSettings, editingModel)
|
||||||
|
} else {
|
||||||
editingModel.commit();
|
editingModel.commit();
|
||||||
plEditorW.close();
|
plEditorW.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
onRejected: plEditorW.close();
|
onRejected: plEditorW.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
148
resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml
Normal file
148
resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
// 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 @@
|
||||||
<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 @@
|
||||||
|
|
||||||
#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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
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()
|
||||||
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()
|
||||||
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()
|
||||||
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()
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
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
|
||||||
.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
|
||||||
&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()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
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_;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue