Rework how access rules for rooms are modified completely

This commit is contained in:
Nicolas Werner 2022-09-19 21:39:37 +02:00
parent 603b90a6f5
commit fe403ddc70
No known key found for this signature in database
GPG Key ID: C8D75E610773F2D9
7 changed files with 562 additions and 79 deletions

View File

@ -94,6 +94,22 @@ Pane {
}
Component {
id: allowedRoomSettingsComponent
AllowedRoomsSettingsDialog {
}
}
function showAllowedRoomsEditor(settings) {
var dialog = allowedRoomSettingsComponent.createObject(timelineRoot, {
"roomSettings": settings
});
dialog.show();
destroyOnClose(dialog);
}
Component {
id: roomMembersComponent

View File

@ -0,0 +1,178 @@
// 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: allowedDialog
property var roomSettings
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("Allowed rooms settings")
Shortcut {
sequence: StandardKey.Cancel
onActivated: roomSettingsDialog.close()
}
ColumnLayout {
anchors.margins: Nheko.paddingMedium
anchors.fill: parent
spacing: 0
MatrixText {
text: qsTr("List of rooms that allow access to this room. Anyone who is in any of those rooms can join this room.")
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
Layout.fillWidth: true
Layout.fillHeight: false
color: Nheko.colors.text
Layout.bottomMargin: Nheko.paddingMedium
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
id: view
clip: true
ScrollHelper {
flickable: parent
anchors.fill: parent
}
model: roomSettings.allowedRoomsModel
spacing: 4
cacheBuffer: 50
delegate: RowLayout {
anchors.left: parent.left
anchors.right: parent.right
ColumnLayout {
Layout.fillWidth: true
Text {
Layout.fillWidth: true
text: model.name
color: Nheko.colors.text
textFormat: Text.PlainText
}
Text {
Layout.fillWidth: true
text: model.isParent ? qsTr("Parent community") : qsTr("Other room")
color: Nheko.colors.buttonText
textFormat: Text.PlainText
}
}
ToggleButton {
checked: model.allowed
Layout.alignment: Qt.AlignRight
onCheckedChanged: model.allowed = checked
}
}
}
Column{
id: roomEntryCompleter
Layout.fillWidth: true
spacing: 1
z: 5
Completer {
id: roomCompleter
visible: roomEntry.text.length > 0
width: parent.width
roomId: allowedDialog.roomSettings.roomId
completerName: "room"
bottomToTop: true
fullWidth: true
avatarHeight: Nheko.avatarSize / 2
avatarWidth: Nheko.avatarSize / 2
centerRowContent: false
rowMargin: 2
rowSpacing: 2
}
MatrixTextField {
id: roomEntry
width: parent.width
placeholderText: qsTr("Enter additional rooms not in the list yet...")
//font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
color: Nheko.colors.text
onTextEdited: {
roomCompleter.completer.searchString = text;
}
Keys.onPressed: {
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
event.accepted = true;
roomCompleter.up();
} else if (event.key == Qt.Key_Down || event.key == Qt.Key_Tab) {
event.accepted = true;
if (event.key == Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))
roomCompleter.up();
else
roomCompleter.down();
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
roomCompleter.finishCompletion();
event.accepted = true;
}
}
}
}
Connections {
function onCompletionSelected(id) {
console.log("selected: " + id);
roomSettings.allowedRoomsModel.addRoom(id);
roomEntry.clear();
}
function onCountChanged() {
if (roomCompleter.count > 0 && (roomCompleter.currentIndex < 0 || roomCompleter.currentIndex >= roomCompleter.count))
roomCompleter.currentIndex = 0;
}
target: roomCompleter
}
}
footer: DialogButtonBox {
id: dbb
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
onAccepted: {
roomSettings.applyAllowedFromModel();
allowedDialog.close();
}
onRejected: allowedDialog.close()
}
}

View File

@ -288,32 +288,98 @@ ApplicationWindow {
}
Label {
text: qsTr("Room access")
text: qsTr("Anyone can join")
Layout.fillWidth: true
color: Nheko.colors.text
}
ComboBox {
ToggleButton {
id: publicRoomButton
enabled: roomSettings.canChangeJoinRules
model: {
let opts = [qsTr("Anyone and guests"), qsTr("Anyone"), qsTr("Invited users")];
if (roomSettings.supportsKnocking)
opts.push(qsTr("By knocking"));
checked: !roomSettings.privateAccess
Layout.alignment: Qt.AlignRight
}
if (roomSettings.supportsRestricted)
opts.push(qsTr("Restricted by membership in other rooms"));
if (roomSettings.supportsKnockRestricted)
opts.push(qsTr("Restricted by membership in other rooms or by knocking"));
return opts;
}
currentIndex: roomSettings.accessJoinRules
onActivated: {
roomSettings.changeAccessRules(index);
}
Label {
text: qsTr("Allow knocking")
Layout.fillWidth: true
color: Nheko.colors.text
visible: knockingButton.visible
}
ToggleButton {
id: knockingButton
visible: !publicRoomButton.checked
enabled: roomSettings.canChangeJoinRules && roomSettings.supportsKnocking
checked: roomSettings.knockingEnabled
onCheckedChanged: {
if (checked && !roomSettings.supportsKnockRestricted) restrictedButton.checked = false;
}
Layout.alignment: Qt.AlignRight
}
Label {
text: qsTr("Allow joining via other rooms")
Layout.fillWidth: true
color: Nheko.colors.text
visible: restrictedButton.visible
}
ToggleButton {
id: restrictedButton
visible: !publicRoomButton.checked
enabled: roomSettings.canChangeJoinRules && roomSettings.supportsRestricted
checked: roomSettings.restrictedEnabled
onCheckedChanged: {
if (checked && !roomSettings.supportsKnockRestricted) knockingButton.checked = false;
}
Layout.alignment: Qt.AlignRight
}
Label {
text: qsTr("Rooms to join via")
Layout.fillWidth: true
color: Nheko.colors.text
visible: allowedRoomsButton.visible
}
Button {
id: allowedRoomsButton
visible: restrictedButton.checked && restrictedButton.visible
enabled: roomSettings.canChangeJoinRules && roomSettings.supportsRestricted
text: qsTr("Change")
ToolTip.text: qsTr("Change the list of rooms users can join this room via. Usually this is the official community of this room.")
onClicked: timelineRoot.showAllowedRoomsEditor(roomSettings)
Layout.alignment: Qt.AlignRight
}
Label {
text: qsTr("Allow guests to join")
Layout.fillWidth: true
color: Nheko.colors.text
}
ToggleButton {
id: guestAccessButton
enabled: roomSettings.canChangeJoinRules
checked: roomSettings.guestAccess
Layout.alignment: Qt.AlignRight
}
Button {
visible: publicRoomButton.checked == roomSettings.privateAccess || knockingButton.checked != roomSettings.knockingEnabled || restrictedButton.checked != roomSettings.restrictedEnabled || guestAccessButton.checked != roomSettings.guestAccess || roomSettings.allowedRoomsModified
enabled: roomSettings.canChangeJoinRules
text: qsTr("Apply access rules")
onClicked: roomSettings.changeAccessRules(!publicRoomButton.checked, guestAccessButton.checked, knockingButton.checked, restrictedButton.checked)
Layout.columnSpan: 2
Layout.fillWidth: true
WheelHandler{} // suppress scrolling changing values
}
Label {

View File

@ -166,6 +166,7 @@
<file>qml/dialogs/ReadReceipts.qml</file>
<file>qml/dialogs/RoomDirectory.qml</file>
<file>qml/dialogs/RoomMembers.qml</file>
<file>qml/dialogs/AllowedRoomsSettingsDialog.qml</file>
<file>qml/dialogs/RoomSettings.qml</file>
<file>qml/dialogs/UserProfile.qml</file>
<file>qml/emoji/EmojiPicker.qml</file>

View File

@ -148,6 +148,7 @@ MainWindow::registerQmlTypes()
qRegisterMetaType<mtx::responses::PublicRoom>();
qRegisterMetaType<mtx::responses::Profile>();
qRegisterMetaType<CombinedImagePackModel *>();
qRegisterMetaType<RoomSettingsAllowedRoomsModel *>();
qRegisterMetaType<mtx::events::collections::TimelineEvents>();
qRegisterMetaType<std::vector<DeviceInfo>>();

View File

@ -27,6 +27,7 @@ RoomSettings::RoomSettings(QString roomid, QObject *parent)
: QObject(parent)
, roomid_{std::move(roomid)}
{
connect(this, &RoomSettings::accessJoinRulesChanged, &RoomSettings::allowedRoomsChanged);
retrieveRoomInfo();
// get room setting notifications
@ -66,22 +67,15 @@ RoomSettings::RoomSettings(QString roomid, QObject *parent)
});
// access rules
if (info_.join_rule == state::JoinRule::Public) {
if (info_.guest_access) {
accessRules_ = 0;
} else {
accessRules_ = 1;
}
} else if (info_.join_rule == state::JoinRule::Invite) {
accessRules_ = 2;
} else if (info_.join_rule == state::JoinRule::Knock) {
accessRules_ = 3;
} else if (info_.join_rule == state::JoinRule::Restricted) {
accessRules_ = 4;
} else if (info_.join_rule == state::JoinRule::KnockRestricted) {
accessRules_ = 5;
}
this->accessRules_ = cache::client()
->getStateEvent<mtx::events::state::JoinRules>(roomid_.toStdString())
.value_or(mtx::events::StateEvent<mtx::events::state::JoinRules>{})
.content;
using mtx::events::state::AccessState;
guestRules_ = info_.guest_access ? AccessState::CanJoin : AccessState::Forbidden;
emit accessJoinRulesChanged();
this->allowedRoomsModel = new RoomSettingsAllowedRoomsModel(this);
}
QString
@ -158,10 +152,49 @@ RoomSettings::notifications()
return notifications_;
}
int
RoomSettings::accessJoinRules()
bool
RoomSettings::privateAccess() const
{
return accessRules_;
return accessRules_.join_rule != mtx::events::state::JoinRule::Public;
}
bool
RoomSettings::guestAccess() const
{
return guestRules_ == mtx::events::state::AccessState::CanJoin;
}
bool
RoomSettings::knockingEnabled() const
{
return accessRules_.join_rule == mtx::events::state::JoinRule::Knock ||
accessRules_.join_rule == mtx::events::state::JoinRule::KnockRestricted;
}
bool
RoomSettings::restrictedEnabled() const
{
return accessRules_.join_rule == mtx::events::state::JoinRule::Restricted ||
accessRules_.join_rule == mtx::events::state::JoinRule::KnockRestricted;
}
QStringList
RoomSettings::allowedRooms() const
{
QStringList rooms;
rooms.reserve(accessRules_.allow.size());
for (const auto &e : accessRules_.allow) {
if (e.type == mtx::events::state::JoinAllowanceType::RoomMembership)
rooms.push_back(QString::fromStdString(e.room_id));
}
return rooms;
}
void
RoomSettings::setAllowedRooms(QStringList rooms)
{
accessRules_.allow.clear();
for (const auto &e : rooms) {
accessRules_.allow.push_back(
{mtx::events::state::JoinAllowanceType::RoomMembership, e.toStdString()});
}
}
void
@ -254,24 +287,48 @@ RoomSettings::isEncryptionEnabled() const
bool
RoomSettings::supportsKnocking() const
{
return info_.version != "" && info_.version != "1" && info_.version != "2" &&
info_.version != "3" && info_.version != "4" && info_.version != "5" &&
info_.version != "6";
const static std::set<std::string_view> unsupported{
"",
"1",
"2",
"3",
"4",
"5",
"6",
};
return !unsupported.count(info_.version);
}
bool
RoomSettings::supportsRestricted() const
{
return info_.version != "" && info_.version != "1" && info_.version != "2" &&
info_.version != "3" && info_.version != "4" && info_.version != "5" &&
info_.version != "6" && info_.version != "7";
const static std::set<std::string_view> unsupported{
"",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
};
return !unsupported.count(info_.version);
}
bool
RoomSettings::supportsKnockRestricted() const
{
return info_.version != "" && info_.version != "1" && info_.version != "2" &&
info_.version != "3" && info_.version != "4" && info_.version != "5" &&
info_.version != "6" && info_.version != "7" && info_.version != "8" &&
info_.version != "9";
const static std::set<std::string_view> unsupported{
"",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
};
return !unsupported.count(info_.version);
}
void
@ -327,47 +384,41 @@ RoomSettings::changeNotifications(int currentIndex)
}
void
RoomSettings::changeAccessRules(int index)
RoomSettings::changeAccessRules(bool private_,
bool guestsAllowed,
bool knockingAllowed,
bool restrictedAllowed)
{
using namespace mtx::events::state;
auto guest_access = [](int index) -> state::GuestAccess {
auto guest_access = [guestsAllowed]() -> state::GuestAccess {
state::GuestAccess event;
if (index == 0)
if (guestsAllowed)
event.guest_access = state::AccessState::CanJoin;
else
event.guest_access = state::AccessState::Forbidden;
return event;
}(index);
}();
auto join_rule = [](int index) -> state::JoinRules {
state::JoinRules event;
auto join_rule = [this, private_, knockingAllowed, restrictedAllowed]() -> state::JoinRules {
state::JoinRules event = this->accessRules_;
switch (index) {
case 0:
case 1:
if (!private_) {
event.join_rule = state::JoinRule::Public;
break;
case 2:
event.join_rule = state::JoinRule::Invite;
break;
case 3:
event.join_rule = state::JoinRule::Knock;
break;
case 4:
event.join_rule = state::JoinRule::Restricted;
break;
case 5:
} else if (knockingAllowed && restrictedAllowed && supportsKnockRestricted()) {
event.join_rule = state::JoinRule::KnockRestricted;
break;
default:
} else if (knockingAllowed && supportsKnocking()) {
event.join_rule = state::JoinRule::Knock;
} else if (restrictedAllowed && supportsRestricted()) {
event.join_rule = state::JoinRule::Restricted;
} else {
event.join_rule = state::JoinRule::Invite;
}
return event;
}(index);
}();
updateAccessRules(roomid_.toStdString(), join_rule, guest_access);
}
@ -445,13 +496,16 @@ RoomSettings::updateAccessRules(const std::string &room_id,
const mtx::events::state::JoinRules &join_rule,
const mtx::events::state::GuestAccess &guest_access)
{
isLoading_ = true;
isLoading_ = true;
allowedRoomsModified_ = false;
emit loadingChanged();
emit allowedRoomsModifiedChanged();
http::client()->send_state_event(
room_id,
join_rule,
[this, room_id, guest_access](const mtx::responses::EventId &, mtx::http::RequestErr err) {
[this, room_id, guest_access, join_rule](const mtx::responses::EventId &,
mtx::http::RequestErr err) {
if (err) {
nhlog::net()->warn("failed to send m.room.join_rule: {} {}",
static_cast<int>(err->status_code),
@ -465,7 +519,7 @@ RoomSettings::updateAccessRules(const std::string &room_id,
http::client()->send_state_event(
room_id,
guest_access,
[this](const mtx::responses::EventId &, mtx::http::RequestErr err) {
[this, join_rule](const mtx::responses::EventId &, mtx::http::RequestErr err) {
if (err) {
nhlog::net()->warn("failed to send m.room.guest_access: {} {}",
static_cast<int>(err->status_code),
@ -475,6 +529,9 @@ RoomSettings::updateAccessRules(const std::string &room_id,
isLoading_ = false;
emit loadingChanged();
this->accessRules_ = join_rule;
emit accessJoinRulesChanged();
});
});
}
@ -576,3 +633,101 @@ RoomSettings::updateAvatar()
});
});
}
RoomSettingsAllowedRoomsModel::RoomSettingsAllowedRoomsModel(RoomSettings *parent)
: QAbstractListModel(parent)
, settings(parent)
{
this->allowedRoomIds = settings->allowedRooms();
auto prIds = cache::client()->getParentRoomIds(settings->roomId().toStdString());
for (const auto &prId : prIds) {
this->parentSpaces.insert(QString::fromStdString(prId));
}
this->listedRoomIds = QStringList(parentSpaces.begin(), parentSpaces.end());
for (const auto &e : this->allowedRoomIds) {
if (!this->parentSpaces.count(e))
this->listedRoomIds.push_back(e);
}
}
QHash<int, QByteArray>
RoomSettingsAllowedRoomsModel::roleNames() const
{
return {
{Roles::Name, "name"},
{Roles::IsAllowed, "allowed"},
{Roles::IsSpaceParent, "isParent"},
};
}
int
RoomSettingsAllowedRoomsModel::rowCount(const QModelIndex &) const
{
return listedRoomIds.size();
}
QVariant
RoomSettingsAllowedRoomsModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() > listedRoomIds.size())
return {};
if (role == Roles::IsAllowed) {
return allowedRoomIds.contains(listedRoomIds.at(index.row()));
} else if (role == Roles::IsSpaceParent) {
return parentSpaces.find(listedRoomIds.at(index.row())) != parentSpaces.cend();
} else if (role == Roles::Name) {
auto id = listedRoomIds.at(index.row());
auto info = cache::client()->getRoomInfo({
id.toStdString(),
});
if (!info.empty())
return QString::fromStdString(info[id].name);
else
return "";
} else {
return {};
}
}
bool
RoomSettingsAllowedRoomsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.row() < 0 || index.row() > listedRoomIds.size())
return false;
if (role != Roles::IsAllowed)
return false;
if (value.toBool()) {
if (!allowedRoomIds.contains(listedRoomIds.at(index.row())))
allowedRoomIds.push_back(listedRoomIds.at(index.row()));
} else {
allowedRoomIds.removeAll(listedRoomIds.at(index.row()));
}
return true;
}
void
RoomSettingsAllowedRoomsModel::addRoom(QString room)
{
if (listedRoomIds.contains(room) || !room.startsWith('!'))
return;
beginInsertRows(QModelIndex(), listedRoomIds.size(), listedRoomIds.size());
listedRoomIds.push_back(room);
allowedRoomIds.push_back(room);
endInsertRows();
}
void
RoomSettings::applyAllowedFromModel()
{
this->setAllowedRooms(this->allowedRoomsModel->allowedRoomIds);
this->allowedRoomsModified_ = true;
emit allowedRoomsModifiedChanged();
}

View File

@ -5,10 +5,13 @@
#pragma once
#include <QAbstractListModel>
#include <QObject>
#include <QSet>
#include <QString>
#include <unordered_set>
#include <mtx/events/event_type.hpp>
#include <mtx/events/guest_access.hpp>
@ -27,6 +30,43 @@ signals:
void stopLoading();
};
class RoomSettings;
class RoomSettingsAllowedRoomsModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles
{
Name,
IsAllowed,
IsSpaceParent,
};
explicit RoomSettingsAllowedRoomsModel(RoomSettings *parent);
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool
setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override;
Q_INVOKABLE void addRoom(QString room);
Qt::ItemFlags flags(const QModelIndex &) const override
{
return Qt::ItemIsEditable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled |
Qt::ItemNeverHasChildren;
}
QStringList allowedRoomIds;
private:
QStringList listedRoomIds;
std::unordered_set<QString> parentSpaces;
RoomSettings *settings;
};
class RoomSettings : public QObject
{
Q_OBJECT
@ -39,7 +79,10 @@ class RoomSettings : public QObject
Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY avatarUrlChanged)
Q_PROPERTY(int memberCount READ memberCount CONSTANT)
Q_PROPERTY(int notifications READ notifications NOTIFY notificationsChanged)
Q_PROPERTY(int accessJoinRules READ accessJoinRules NOTIFY accessJoinRulesChanged)
Q_PROPERTY(bool privateAccess READ privateAccess NOTIFY accessJoinRulesChanged)
Q_PROPERTY(bool guestAccess READ guestAccess NOTIFY accessJoinRulesChanged)
Q_PROPERTY(bool knockingEnabled READ knockingEnabled NOTIFY accessJoinRulesChanged)
Q_PROPERTY(bool restrictedEnabled READ restrictedEnabled NOTIFY accessJoinRulesChanged)
Q_PROPERTY(bool isLoading READ isLoading NOTIFY loadingChanged)
Q_PROPERTY(bool canChangeAvatar READ canChangeAvatar CONSTANT)
Q_PROPERTY(bool canChangeJoinRules READ canChangeJoinRules CONSTANT)
@ -49,6 +92,11 @@ class RoomSettings : public QObject
Q_PROPERTY(bool supportsKnocking READ supportsKnocking CONSTANT)
Q_PROPERTY(bool supportsRestricted READ supportsRestricted CONSTANT)
Q_PROPERTY(bool supportsKnockRestricted READ supportsKnockRestricted CONSTANT)
Q_PROPERTY(
QStringList allowedRooms READ allowedRooms WRITE setAllowedRooms NOTIFY allowedRoomsChanged)
Q_PROPERTY(RoomSettingsAllowedRoomsModel *allowedRoomsModel MEMBER allowedRoomsModel CONSTANT)
Q_PROPERTY(
bool allowedRoomsModified READ allowedRoomsModified NOTIFY allowedRoomsModifiedChanged)
public:
RoomSettings(QString roomid, QObject *parent = nullptr);
@ -62,7 +110,10 @@ public:
QString roomAvatarUrl();
int memberCount() const;
int notifications();
int accessJoinRules();
bool privateAccess() const;
bool guestAccess() const;
bool knockingEnabled() const;
bool restrictedEnabled() const;
bool isLoading() const;
//! Whether the user has enough power level to send m.room.join_rules events.
bool canChangeJoinRules() const;
@ -76,14 +127,22 @@ public:
bool supportsKnocking() const;
bool supportsRestricted() const;
bool supportsKnockRestricted() const;
QStringList allowedRooms() const;
void setAllowedRooms(QStringList rooms);
bool allowedRoomsModified() const { return allowedRoomsModified_; }
Q_INVOKABLE void enableEncryption();
Q_INVOKABLE void updateAvatar();
Q_INVOKABLE void changeAccessRules(int index);
Q_INVOKABLE void changeAccessRules(bool private_,
bool guestsAllowed,
bool knockingAllowed,
bool restrictedAllowed);
Q_INVOKABLE void changeNotifications(int currentIndex);
Q_INVOKABLE void changeTopic(QString topic);
Q_INVOKABLE void changeName(QString name);
Q_INVOKABLE void applyAllowedFromModel();
signals:
void loadingChanged();
void roomNameChanged();
@ -92,7 +151,9 @@ signals:
void encryptionChanged();
void notificationsChanged();
void accessJoinRulesChanged();
void allowedRoomsChanged();
void displayError(const QString &errorMessage);
void allowedRoomsModifiedChanged();
public slots:
void stopLoading();
@ -106,9 +167,14 @@ private:
private:
QString roomid_;
bool usesEncryption_ = false;
bool isLoading_ = false;
bool usesEncryption_ = false;
bool isLoading_ = false;
bool allowedRoomsModified_ = false;
RoomInfo info_;
int notifications_ = 0;
int accessRules_ = 0;
mtx::events::state::JoinRules accessRules_;
mtx::events::state::AccessState guestRules_ = mtx::events::state::AccessState::Forbidden;
RoomSettingsAllowedRoomsModel *allowedRoomsModel;
};