aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--lib/connection.cpp4
-rw-r--r--lib/connection.h1
-rw-r--r--lib/events/roommemberevent.cpp65
-rw-r--r--lib/events/roommemberevent.h26
-rw-r--r--lib/joinstate.h32
-rw-r--r--lib/quotient_common.cpp45
-rw-r--r--lib/quotient_common.h67
-rw-r--r--lib/room.cpp49
-rw-r--r--lib/room.h9
-rw-r--r--lib/syncdata.h2
-rw-r--r--lib/user.cpp2
12 files changed, 190 insertions, 118 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index deb50aea..49105389 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -122,11 +122,7 @@ endif ()
# Set up source files
list(APPEND lib_SRCS
- # This .h is special in that it declares a Q_NAMESPACE but has no .cpp
- # where staticMetaObject for that namespace would be defined; passing it
- # to add_library (see below) puts it on the automoc radar, producing
- # a compilation unit with the needed definition.
- lib/quotient_common.h
+ lib/quotient_common.cpp
lib/networkaccessmanager.cpp
lib/connectiondata.cpp
lib/connection.cpp
diff --git a/lib/connection.cpp b/lib/connection.cpp
index e076957a..7dd04aaa 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -640,7 +640,7 @@ void Connection::Private::consumeRoomData(SyncDataList&& roomDataList,
}
qWarning(MAIN) << "Room" << roomData.roomId
<< "has just been forgotten but /sync returned it in"
- << toCString(roomData.joinState)
+ << roomData.joinState
<< "state - suspiciously fast turnaround";
}
if (auto* r = q->provideRoom(roomData.roomId, roomData.joinState)) {
@@ -1356,7 +1356,7 @@ void Connection::Private::removeRoom(const QString& roomId)
for (auto f : { false, true })
if (auto r = roomMap.take({ roomId, f })) {
qCDebug(MAIN) << "Room" << r->objectName() << "in state"
- << toCString(r->joinState()) << "will be deleted";
+ << r->joinState() << "will be deleted";
emit r->beforeDestruction(r);
r->deleteLater();
}
diff --git a/lib/connection.h b/lib/connection.h
index 0d22d01f..a7a071f3 100644
--- a/lib/connection.h
+++ b/lib/connection.h
@@ -6,7 +6,6 @@
#pragma once
#include "ssosession.h"
-#include "joinstate.h"
#include "qt_connection_util.h"
#include "quotient_common.h"
diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp
index 9634ca3a..8a6bddd8 100644
--- a/lib/events/roommemberevent.cpp
+++ b/lib/events/roommemberevent.cpp
@@ -7,27 +7,26 @@
#include "converters.h"
#include "logging.h"
-#include <array>
-
-static const std::array<QString, 5> membershipStrings = {
- { QStringLiteral("invite"), QStringLiteral("join"), QStringLiteral("knock"),
- QStringLiteral("leave"), QStringLiteral("ban") }
-};
+#include <QtCore/QtAlgorithms>
namespace Quotient {
template <>
-struct JsonConverter<MembershipType> {
- static MembershipType load(const QJsonValue& jv)
+struct JsonConverter<Membership> {
+ static Membership load(const QJsonValue& jv)
{
- const auto& membershipString = jv.toString();
- for (auto it = membershipStrings.begin(); it != membershipStrings.end();
- ++it)
- if (membershipString == *it)
- return MembershipType(it - membershipStrings.begin());
-
- if (!membershipString.isEmpty())
- qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString;
- return MembershipType::Undefined;
+ const auto& ms = jv.toString();
+ if (ms.isEmpty())
+ {
+ qCWarning(EVENTS) << "Empty member state:" << ms;
+ return Membership::Invalid;
+ }
+ const auto it =
+ std::find(MembershipStrings.begin(), MembershipStrings.end(), ms);
+ if (it != MembershipStrings.end())
+ return Membership(1U << (it - MembershipStrings.begin()));
+
+ qCWarning(EVENTS) << "Unknown Membership value: " << ms;
+ return Membership::Invalid;
}
};
} // namespace Quotient
@@ -35,7 +34,7 @@ struct JsonConverter<MembershipType> {
using namespace Quotient;
MemberEventContent::MemberEventContent(const QJsonObject& json)
- : membership(fromJson<MembershipType>(json["membership"_ls]))
+ : membership(fromJson<Membership>(json["membership"_ls]))
, isDirect(json["is_direct"_ls].toBool())
, displayName(fromJson<Omittable<QString>>(json["displayname"_ls]))
, avatarUrl(fromJson<Omittable<QString>>(json["avatar_url"_ls]))
@@ -48,10 +47,12 @@ MemberEventContent::MemberEventContent(const QJsonObject& json)
void MemberEventContent::fillJson(QJsonObject* o) const
{
Q_ASSERT(o);
- Q_ASSERT_X(membership != MembershipType::Undefined, __FUNCTION__,
- "The key 'membership' must be explicit in MemberEventContent");
- if (membership != MembershipType::Undefined)
- o->insert(QStringLiteral("membership"), membershipStrings[membership]);
+ if (membership != Membership::Invalid)
+ o->insert(
+ QStringLiteral("membership"),
+ MembershipStrings[qCountTrailingZeroBits(
+ std::underlying_type_t<Membership>(membership))
+ + 1]);
if (displayName)
o->insert(QStringLiteral("displayname"), *displayName);
if (avatarUrl && avatarUrl->isValid())
@@ -67,37 +68,37 @@ bool RoomMemberEvent::changesMembership() const
bool RoomMemberEvent::isInvite() const
{
- return membership() == MembershipType::Invite && changesMembership();
+ return membership() == Membership::Invite && changesMembership();
}
bool RoomMemberEvent::isRejectedInvite() const
{
- return membership() == MembershipType::Leave && prevContent()
- && prevContent()->membership == MembershipType::Invite;
+ return membership() == Membership::Leave && prevContent()
+ && prevContent()->membership == Membership::Invite;
}
bool RoomMemberEvent::isJoin() const
{
- return membership() == MembershipType::Join && changesMembership();
+ return membership() == Membership::Join && changesMembership();
}
bool RoomMemberEvent::isLeave() const
{
- return membership() == MembershipType::Leave && prevContent()
+ return membership() == Membership::Leave && prevContent()
&& prevContent()->membership != membership()
- && prevContent()->membership != MembershipType::Ban
- && prevContent()->membership != MembershipType::Invite;
+ && prevContent()->membership != Membership::Ban
+ && prevContent()->membership != Membership::Invite;
}
bool RoomMemberEvent::isBan() const
{
- return membership() == MembershipType::Ban && changesMembership();
+ return membership() == Membership::Ban && changesMembership();
}
bool RoomMemberEvent::isUnban() const
{
- return membership() == MembershipType::Leave && prevContent()
- && prevContent()->membership == MembershipType::Ban;
+ return membership() == Membership::Leave && prevContent()
+ && prevContent()->membership == Membership::Ban;
}
bool RoomMemberEvent::isRename() const
diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h
index f2fbe689..f3047159 100644
--- a/lib/events/roommemberevent.h
+++ b/lib/events/roommemberevent.h
@@ -7,23 +7,21 @@
#include "eventcontent.h"
#include "stateevent.h"
+#include "quotient_common.h"
namespace Quotient {
class MemberEventContent : public EventContent::Base {
public:
- enum MembershipType : unsigned char {
- Invite = 0,
- Join,
- Knock,
- Leave,
- Ban,
- Undefined
- };
+ using MembershipType
+ [[deprecated("Use Quotient::Membership instead")]] = Membership;
- explicit MemberEventContent(MembershipType mt = Join) : membership(mt) {}
+ explicit MemberEventContent(Membership ms = Membership::Join)
+ : membership(ms)
+ {}
explicit MemberEventContent(const QJsonObject& json);
- MembershipType membership;
+ Membership membership;
+ /// (Only for invites) Whether the invite is to a direct chat
bool isDirect = false;
Omittable<QString> displayName;
Omittable<QUrl> avatarUrl;
@@ -33,15 +31,15 @@ protected:
void fillJson(QJsonObject* o) const override;
};
-using MembershipType = MemberEventContent::MembershipType;
+using MembershipType [[deprecated("Use Membership instead")]] = Membership;
class RoomMemberEvent : public StateEvent<MemberEventContent> {
Q_GADGET
public:
DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent)
- using MembershipType = MemberEventContent::MembershipType;
- Q_ENUM(MembershipType)
+ using MembershipType
+ [[deprecated("Use Quotient::Membership instead")]] = Membership;
explicit RoomMemberEvent(const QJsonObject& obj) : StateEvent(typeId(), obj)
{}
@@ -65,7 +63,7 @@ public:
: StateEvent(type, fullJson)
{}
- MembershipType membership() const { return content().membership; }
+ Membership membership() const { return content().membership; }
QString userId() const { return stateKey(); }
bool isDirect() const { return content().isDirect; }
Omittable<QString> newDisplayName() const { return content().displayName; }
diff --git a/lib/joinstate.h b/lib/joinstate.h
deleted file mode 100644
index 805ce73a..00000000
--- a/lib/joinstate.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#pragma once
-
-#include <QtCore/QFlags>
-
-#include <array>
-
-namespace Quotient {
-enum class JoinState : unsigned int {
- Join = 0x1,
- Invite = 0x2,
- Leave = 0x4,
-};
-
-Q_DECLARE_FLAGS(JoinStates, JoinState)
-
-// We cannot use Q_ENUM outside of a Q_OBJECT and besides, we want
-// to use strings that match respective JSON keys.
-static const std::array<const char*, 3> JoinStateStrings { { "join", "invite",
- "leave" } };
-
-inline const char* toCString(JoinState js)
-{
- size_t state = size_t(js), index = 0;
- while (state >>= 1u)
- ++index;
- return JoinStateStrings[index];
-}
-} // namespace Quotient
-Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::JoinStates)
diff --git a/lib/quotient_common.cpp b/lib/quotient_common.cpp
new file mode 100644
index 00000000..805070d1
--- /dev/null
+++ b/lib/quotient_common.cpp
@@ -0,0 +1,45 @@
+#include "quotient_common.h"
+
+#include <QtCore/QDebug>
+
+using namespace Quotient;
+
+template <typename Enum>
+inline QDebug suppressScopeAndDump(QDebug dbg, Enum e)
+{
+ // Suppress "Quotient::" prefix
+ QDebugStateSaver _dss(dbg);
+ dbg.setVerbosity(QDebug::MinimumVerbosity);
+ return qt_QMetaEnum_debugOperator(dbg, std::underlying_type_t<Enum>(e),
+ qt_getEnumMetaObject(e),
+ qt_getEnumName(e));
+}
+
+template <typename Enum>
+inline QDebug suppressScopeAndDump(QDebug dbg, const QFlags<Enum>& f)
+{
+ // Suppress "Quotient::" prefix
+ QDebugStateSaver _dss(dbg);
+ dbg.setVerbosity(QDebug::MinimumVerbosity);
+ return qt_QMetaEnum_flagDebugOperator_helper(dbg, f);
+}
+
+QDebug operator<<(QDebug dbg, Membership m)
+{
+ return suppressScopeAndDump(dbg, m);
+}
+
+QDebug operator<<(QDebug dbg, MembershipMask mm)
+{
+ return suppressScopeAndDump(dbg, mm) << ")";
+}
+
+QDebug operator<<(QDebug dbg, JoinState js)
+{
+ return suppressScopeAndDump(dbg, js);
+}
+
+QDebug operator<<(QDebug dbg, JoinStates jss)
+{
+ return suppressScopeAndDump(dbg, jss) << ")";
+}
diff --git a/lib/quotient_common.h b/lib/quotient_common.h
index 22fdbe94..789128cf 100644
--- a/lib/quotient_common.h
+++ b/lib/quotient_common.h
@@ -8,15 +8,62 @@
namespace Quotient {
Q_NAMESPACE
-/** Enumeration with flags defining the network job running policy
- * So far only background/foreground flags are available.
- *
- * \sa Connection::callApi, Connection::run
- */
+// TODO: code like this should be generated from the CS API definition
+
+//! \brief Membership states
+//!
+//! These are used for member events. The names here are case-insensitively
+//! equal to state names used on the wire.
+//! \sa MemberEventContent, RoomMemberEvent
+enum class Membership : unsigned int {
+ // Specific power-of-2 values (1,2,4,...) are important here as syncdata.cpp
+ // depends on that, as well as Join being the first in line
+ Invalid = 0x0,
+ Join = 0x1,
+ Leave = 0x2,
+ Invite = 0x4,
+ Knock = 0x8,
+ Ban = 0x10,
+ Undefined = Invalid
+};
+Q_DECLARE_FLAGS(MembershipMask, Membership)
+Q_FLAG_NS(MembershipMask)
+
+constexpr inline std::array MembershipStrings = {
+ // The order MUST be the same as the order in the original enum
+ "join", "leave", "invite", "knock", "ban"
+};
+
+//! \brief Local user join-state names
+//!
+//! This represents a subset of Membership values that may arrive as the local
+//! user's state grouping for the sync response.
+//! \sa SyncData
+enum class JoinState : std::underlying_type_t<Membership> {
+ Invalid = std::underlying_type_t<Membership>(Membership::Invalid),
+ Join = std::underlying_type_t<Membership>(Membership::Join),
+ Leave = std::underlying_type_t<Membership>(Membership::Leave),
+ Invite = std::underlying_type_t<Membership>(Membership::Invite),
+ Knock = std::underlying_type_t<Membership>(Membership::Knock),
+};
+Q_DECLARE_FLAGS(JoinStates, JoinState)
+Q_FLAG_NS(JoinStates)
+
+constexpr inline std::array JoinStateStrings {
+ MembershipStrings[0], MembershipStrings[1], MembershipStrings[2],
+ MembershipStrings[3] /* same as MembershipStrings, sans "ban" */
+};
+
+//! \brief Network job running policy flags
+//!
+//! So far only background/foreground flags are available.
+//! \sa Connection::callApi, Connection::run
enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 };
Q_ENUM_NS(RunningPolicy)
+//! \brief The result of URI resolution using UriResolver
+//! \sa UriResolver
enum UriResolveResult : short {
StillResolving = -1,
UriResolved = 0,
@@ -28,5 +75,11 @@ enum UriResolveResult : short {
Q_ENUM_NS(UriResolveResult)
} // namespace Quotient
-/// \deprecated Use namespace Quotient instead
-namespace QMatrixClient = Quotient;
+Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::MembershipMask)
+Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::JoinStates)
+
+class QDebug;
+QDebug operator<<(QDebug dbg, Quotient::Membership m);
+QDebug operator<<(QDebug dbg, Quotient::MembershipMask m);
+QDebug operator<<(QDebug dbg, Quotient::JoinState js);
+QDebug operator<<(QDebug dbg, Quotient::JoinStates js);
diff --git a/lib/room.cpp b/lib/room.cpp
index 6b729b8f..54c67c2f 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -460,7 +460,7 @@ Room::Room(Connection* connection, QString id, JoinState initialJoinState)
emit baseStateLoaded();
return this == r; // loadedRoomState fires only once per room
});
- qCDebug(STATE) << "New" << toCString(initialJoinState) << "Room:" << id;
+ qCDebug(STATE) << "New" << initialJoinState << "Room:" << id;
}
Room::~Room() { delete d; }
@@ -603,6 +603,11 @@ JoinState Room::memberJoinState(User* user) const
: JoinState::Leave;
}
+Membership Room::memberState(User* user) const
+{
+ return d->getCurrentState<RoomMemberEvent>(user->id())->membership();
+}
+
JoinState Room::joinState() const { return d->joinState; }
void Room::setJoinState(JoinState state)
@@ -611,8 +616,8 @@ void Room::setJoinState(JoinState state)
if (state == oldState)
return;
d->joinState = state;
- qCDebug(STATE) << "Room" << id() << "changed state: " << int(oldState)
- << "->" << int(state);
+ qCDebug(STATE) << "Room" << id() << "changed state: " << oldState
+ << "->" << state;
emit changed(Change::JoinStateChange);
emit joinStateChanged(oldState, state);
}
@@ -2513,16 +2518,16 @@ Room::Changes Room::processStateEvent(const RoomEvent& e)
return false; // Stay low and hope for the best...
}
const auto prevMembership = oldRme ? oldRme->membership()
- : MembershipType::Leave;
+ : Membership::Leave;
switch (prevMembership) {
- case MembershipType::Invite:
+ case Membership::Invite:
if (rme.membership() != prevMembership) {
d->usersInvited.removeOne(u);
Q_ASSERT(!d->usersInvited.contains(u));
}
break;
- case MembershipType::Join:
- if (rme.membership() == MembershipType::Join) {
+ case Membership::Join:
+ if (rme.membership() == Membership::Join) {
// rename/avatar change or no-op
if (rme.newDisplayName()) {
emit memberAboutToRename(u, *rme.newDisplayName());
@@ -2536,7 +2541,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e)
return false;
}
} else {
- if (rme.membership() == MembershipType::Invite)
+ if (rme.membership() == Membership::Invite)
qCWarning(MAIN)
<< "Membership change from Join to Invite:" << rme;
// whatever the new membership, it's no more Join
@@ -2544,16 +2549,16 @@ Room::Changes Room::processStateEvent(const RoomEvent& e)
emit userRemoved(u);
}
break;
- case MembershipType::Ban:
- case MembershipType::Knock:
- case MembershipType::Leave:
- if (rme.membership() == MembershipType::Invite
- || rme.membership() == MembershipType::Join) {
+ case Membership::Ban:
+ case Membership::Knock:
+ case Membership::Leave:
+ if (rme.membership() == Membership::Invite
+ || rme.membership() == Membership::Join) {
d->membersLeft.removeOne(u);
Q_ASSERT(!d->membersLeft.contains(u));
}
break;
- case MembershipType::Undefined:
+ case Membership::Undefined:
; // A warning will be dropped in the post-processing block below
}
return true;
@@ -2636,10 +2641,10 @@ Room::Changes Room::processStateEvent(const RoomEvent& e)
static_cast<const RoomMemberEvent*>(oldStateEvent);
const auto prevMembership = oldMemberEvent
? oldMemberEvent->membership()
- : MembershipType::Leave;
+ : Membership::Leave;
switch (evt.membership()) {
- case MembershipType::Join:
- if (prevMembership != MembershipType::Join) {
+ case Membership::Join:
+ if (prevMembership != Membership::Join) {
d->insertMemberIntoMap(u);
emit userAdded(u);
} else {
@@ -2651,19 +2656,19 @@ Room::Changes Room::processStateEvent(const RoomEvent& e)
emit memberAvatarChanged(u);
}
break;
- case MembershipType::Invite:
+ case Membership::Invite:
if (!d->usersInvited.contains(u))
d->usersInvited.push_back(u);
if (u == localUser() && evt.isDirect())
connection()->addToDirectChats(this, user(evt.senderId()));
break;
- case MembershipType::Knock:
- case MembershipType::Ban:
- case MembershipType::Leave:
+ case Membership::Knock:
+ case Membership::Ban:
+ case Membership::Leave:
if (!d->membersLeft.contains(u))
d->membersLeft.append(u);
break;
- case MembershipType::Undefined:
+ case Membership::Undefined:
qCWarning(MEMBERS) << "Ignored undefined membership type";
}
return MembersChange;
diff --git a/lib/room.h b/lib/room.h
index d71bff9c..cdbfe58f 100644
--- a/lib/room.h
+++ b/lib/room.h
@@ -11,7 +11,7 @@
#include "connection.h"
#include "eventitem.h"
-#include "joinstate.h"
+#include "quotient_common.h"
#include "csapi/message_pagination.h"
@@ -245,6 +245,8 @@ public:
/**
* \brief Check the join state of a given user in this room
*
+ * \deprecated Use memberState and check against a mask
+ *
* \note Banned and invited users are not tracked separately for now (Leave
* will be returned for them).
*
@@ -252,6 +254,11 @@ public:
*/
Q_INVOKABLE Quotient::JoinState memberJoinState(Quotient::User* user) const;
+ //! \brief Check the join state of a given user in this room
+ //!
+ //! \return the given user's state with respect to the room
+ Q_INVOKABLE Quotient::Membership memberState(User* user) const;
+
//! \brief Get a display name (without disambiguation) for the given member
//!
//! \sa safeMemberName, htmlSafeMemberName
diff --git a/lib/syncdata.h b/lib/syncdata.h
index e69bac17..0153bfd6 100644
--- a/lib/syncdata.h
+++ b/lib/syncdata.h
@@ -3,7 +3,7 @@
#pragma once
-#include "joinstate.h"
+#include "quotient_common.h"
#include "events/stateevent.h"
diff --git a/lib/user.cpp b/lib/user.cpp
index 6cc9e161..c97e33a4 100644
--- a/lib/user.cpp
+++ b/lib/user.cpp
@@ -123,7 +123,7 @@ void User::rename(const QString& newName, const Room* r)
}
// #481: take the current state and update it with the new name
auto evtC = r->getCurrentState<RoomMemberEvent>(id())->content();
- Q_ASSERT_X(evtC.membership == MembershipType::Join, __FUNCTION__,
+ Q_ASSERT_X(evtC.membership == Membership::Join, __FUNCTION__,
"Attempt to rename a user that's not a room member");
evtC.displayName = sanitized(newName);
r->setState<RoomMemberEvent>(id(), move(evtC));