Skip to content

Commit

Permalink
Merge #862(nvrwhere): Join rule event
Browse files Browse the repository at this point in the history
  • Loading branch information
KitsuneRal authored Feb 10, 2025
2 parents 348b2c7 + 9d82662 commit fe1f64d
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ target_sources(${QUOTIENT_LIB_NAME} PUBLIC FILE_SET HEADERS BASE_DIRS .
Quotient/events/eventcontent.h
Quotient/events/eventrelation.h
Quotient/events/roomcreateevent.h
Quotient/events/roomjoinrulesevent.h
Quotient/events/roomtombstoneevent.h
Quotient/events/roommessageevent.h
Quotient/events/roommemberevent.h
Expand Down
95 changes: 95 additions & 0 deletions Quotient/events/roomjoinrulesevent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: 2025 James Graham <[email protected]>
// SPDX-License-Identifier: LGPL-2.1-or-later

#pragma once

#include "../quotient_common.h"
#include "stateevent.h"

namespace Quotient
{
namespace EventContent {

//! \brief Definition of an allow AllowCondition
//!
//! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_ruless
struct AllowCondition {
QString roomId;
QString type;
};

[[maybe_unused]] constexpr std::array JoinRuleStrings {
"public"_L1,
"knock"_L1,
"invite"_L1,
"private"_L1,
"restricted"_L1,
"knock_restricted"_L1,
};

//! \brief The content of a join rule event
//!
//! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
struct JoinRuleContent {
JoinRule joinRule;
QList<AllowCondition> allow;
};
} // namespace EventContent

template<>
inline EventContent::AllowCondition fromJson(const QJsonObject& jo)
{
return EventContent::AllowCondition {
fromJson<QString>(jo["room_id"_L1]),
fromJson<QString>(jo["type"_L1])
};
}

template<>
inline auto toJson(const EventContent::AllowCondition& c)
{
QJsonObject jo;
addParam<IfNotEmpty>(jo, "room_id"_L1, c.roomId);
addParam<IfNotEmpty>(jo, "type"_L1, c.type);
return jo;
}

template<>
inline EventContent::JoinRuleContent fromJson(const QJsonObject& jo)
{
return EventContent::JoinRuleContent {
enumFromJsonString<JoinRule>(jo["join_rule"_L1].toString(), EventContent::JoinRuleStrings).value_or(Public),
fromJson<QList<EventContent::AllowCondition>>(jo["allow"_L1])
};
}

template<>
inline auto toJson(const EventContent::JoinRuleContent& c)
{
QJsonObject jo;
addParam<IfNotEmpty>(jo, "join_rule"_L1, enumToJsonString<JoinRule>(c.joinRule, EventContent::JoinRuleStrings));
addParam<IfNotEmpty>(jo, "allow"_L1, c.allow);
return jo;
}

//! \brief Class to define a join rule state event.
//!
//! \sa Quotient::StateEvent, https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
class JoinRulesEvent : public KeylessStateEventBase<JoinRulesEvent,
EventContent::JoinRuleContent>
{
public:
QUO_EVENT(JoinRulesEvent, "m.room.join_rules")
using KeylessStateEventBase::KeylessStateEventBase;

//! \brief The join rule for the room.
//!
//! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
JoinRule joinRule() const { return content().joinRule; }

//! \brief The allow rules for restricted rooms.
//!
//! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
QList<EventContent::AllowCondition> allow() const { return content().allow; }
};
} // namespace Quotient
11 changes: 11 additions & 0 deletions Quotient/quotient_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@ enum class EncryptionType : uint8_t {
};
Q_ENUM_NS(EncryptionType)

//! Enum representing the available room join rules
enum JoinRule : uint16_t {
Public,
Knock,
Invite,
Private,
Restricted,
KnockRestricted,
};
Q_ENUM_NS(JoinRule)

} // namespace Quotient
Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::MembershipMask)
Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::JoinStates)
39 changes: 39 additions & 0 deletions Quotient/room.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include "events/roomavatarevent.h"
#include "events/roomcanonicalaliasevent.h"
#include "events/roomcreateevent.h"
#include "events/roomjoinrulesevent.h"
#include "events/roommemberevent.h"
#include "events/roompowerlevelsevent.h"
#include "events/roomtombstoneevent.h"
Expand Down Expand Up @@ -1534,6 +1535,36 @@ RoomStateView Room::currentState() const
return d->currentState;
}

JoinRule Room::joinRule() const
{
return currentState().queryOr(&JoinRulesEvent::joinRule, Public);
}

QList<QString> Room::allowIds() const
{
QList<QString> allowIds;
for (const auto& allowCondition : currentState().queryOr(&JoinRulesEvent::allow, QList<EventContent::AllowCondition>())) {
allowIds.append(allowCondition.roomId);
}
return allowIds;
}

void Room::setJoinRule(JoinRule newRule, const QList<QString>& allowedRooms)
{
if (memberEffectivePowerLevel() < powerLevelFor<JoinRulesEvent>()) {
return;
}

JoinRule actualRule = (newRule == Restricted || newRule == KnockRestricted) && allowedRooms.isEmpty() ? Invite : newRule;
QList<EventContent::AllowCondition> newAllow;
for (const auto& room :allowedRooms) {
newAllow.append({room, "m.room_membership"_L1});
}
setState<JoinRulesEvent>(actualRule, newAllow);
// Not emitting joinRuleChanged() here, since that would override the change
// in the UI with the *current* value, which is not the *new* value.
}

int Room::memberEffectivePowerLevel(const UserId& memberId) const
{
return currentState().get<RoomPowerLevelsEvent>()->powerLevelForUser(
Expand Down Expand Up @@ -3211,6 +3242,14 @@ Room::Change Room::Private::processStateEvent(const RoomEvent& curEvent,

return Change::Other;
},
[this, oldEvent](const JoinRulesEvent& evt) {
if (const auto* oldJRE = static_cast<const JoinRulesEvent*>(oldEvent);
oldJRE && oldJRE->content().joinRule != evt.content().joinRule
) {
emit q->joinRuleChanged();
}
return Change::Other;
},
Change::Other);
}

Expand Down
35 changes: 35 additions & 0 deletions Quotient/room.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "events/roommessageevent.h"
#include "events/roompowerlevelsevent.h"
#include "events/roomtombstoneevent.h"
#include "events/roomjoinrulesevent.h"

#include <QtCore/QJsonObject>
#include <QtGui/QImage>
Expand Down Expand Up @@ -165,6 +166,8 @@ class QUOTIENT_API Room : public QObject {
Q_PROPERTY(QStringList tagNames READ tagNames NOTIFY tagsChanged)
Q_PROPERTY(bool isFavourite READ isFavourite NOTIFY tagsChanged STORED false)
Q_PROPERTY(bool isLowPriority READ isLowPriority NOTIFY tagsChanged STORED false)
Q_PROPERTY(JoinRule joinRule READ joinRule WRITE setJoinRule NOTIFY joinRuleChanged)
Q_PROPERTY(QList<QString> allowIds READ allowIds NOTIFY joinRuleChanged)

Q_PROPERTY(GetRoomEventsJob* eventsHistoryJob READ eventsHistoryJob NOTIFY eventsHistoryJobChanged)
Q_PROPERTY(int requestedHistorySize READ requestedHistorySize NOTIFY eventsHistoryJobChanged)
Expand Down Expand Up @@ -672,6 +675,35 @@ class QUOTIENT_API Room : public QObject {
/// \brief Get the current room state
RoomStateView currentState() const;

//! \brief The current Join Rule for the room
//!
//! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
JoinRule joinRule() const;

//! \brief Set the Join Rule for the room
//!
//! If the local user does not have a high enough power level the request is rejected.
//!
//! \param newRule the new JoinRule to apply to the room
//! \param allowedRooms only required when the join rule is restricted. This is a
//! list of room IDs that members of can join without an invite.
//! If the rule is restricted and this list is empty it is treated as a join
//! rule of invite instead.
//!
//! \note While any room ID is permitted it is designed to be only spaces that are
//! input. I.e. only memebers of space `x` can join this room.
//!
//! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
Q_INVOKABLE void setJoinRule(JoinRule newRule, const QList<QString>& allowedRooms = {});

//! \brief The list of Room IDs for when the join rule is Restricted
//!
//! This value will be empty when the Join Rule is not Restricted or
//! Knock-Restricted.
//!
//! \sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
QList<QString> allowIds() const;

//! \brief The effective power level of the given member in the room
//!
//! This is normally the same as calling `RoomPowerLevelEvent::powerLevelForUser(userId)` but
Expand Down Expand Up @@ -899,6 +931,9 @@ public Q_SLOTS:
void topicChanged();
void avatarChanged();

//! \brief The join rule for the room has changed
void joinRuleChanged();

//! \brief A new member has joined the room
//!
//! This can be from any previous state or a member previously unknown to
Expand Down

0 comments on commit fe1f64d

Please sign in to comment.