aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-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/jobs/basejob.cpp13
-rw-r--r--lib/joinstate.h32
-rw-r--r--lib/quotient_common.cpp45
-rw-r--r--lib/quotient_common.h86
-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, 211 insertions, 123 deletions
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/jobs/basejob.cpp b/lib/jobs/basejob.cpp
index 9a7b9b5e..400a9243 100644
--- a/lib/jobs/basejob.cpp
+++ b/lib/jobs/basejob.cpp
@@ -5,6 +5,7 @@
#include "basejob.h"
#include "connectiondata.h"
+#include "quotient_common.h"
#include <QtCore/QRegularExpression>
#include <QtCore/QTimer>
@@ -15,8 +16,6 @@
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
-#include <array>
-
using namespace Quotient;
using std::chrono::seconds, std::chrono::milliseconds;
using namespace std::chrono_literals;
@@ -63,12 +62,6 @@ QDebug BaseJob::Status::dumpToLog(QDebug dbg) const
return dbg << ": " << message;
}
-template <typename... Ts>
-constexpr auto make_array(Ts&&... items)
-{
- return std::array<std::common_type_t<Ts...>, sizeof...(Ts)>({items...});
-}
-
class BaseJob::Private {
public:
struct JobTimeoutConfig {
@@ -163,8 +156,8 @@ public:
{
// FIXME: use std::array {} when Apple stdlib gets deduction guides for it
static const auto verbs =
- make_array(QStringLiteral("GET"), QStringLiteral("PUT"),
- QStringLiteral("POST"), QStringLiteral("DELETE"));
+ to_array({ QStringLiteral("GET"), QStringLiteral("PUT"),
+ QStringLiteral("POST"), QStringLiteral("DELETE") });
const auto verbWord = verbs.at(size_t(verb));
return verbWord % ' '
% (reply ? reply->url().toString(QUrl::RemoveQuery)
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..5d7a3027
--- /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(0 /* QDebug::MinimumVerbosity since Qt 5.13 */);
+ 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(0 /* QDebug::MinimumVerbosity since Qt 5.13 */);
+ 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..d225ad63 100644
--- a/lib/quotient_common.h
+++ b/lib/quotient_common.h
@@ -5,18 +5,84 @@
#include <qobjectdefs.h>
+#include <array>
+
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
- */
+namespace impl {
+ template <class T, std::size_t N, std::size_t... I>
+ constexpr std::array<std::remove_cv_t<T>, N>
+ to_array_impl(T (&&a)[N], std::index_sequence<I...>)
+ {
+ return { {std::move(a[I])...} };
+ }
+}
+// std::array {} needs explicit template parameters on macOS because
+// Apple stdlib doesn't have deduction guides for std::array; to alleviate that,
+// to_array() is borrowed from C++20 (thanks to cppreference for the possible
+// implementation: https://en.cppreference.com/w/cpp/container/array/to_array)
+template <typename T, size_t N>
+constexpr auto to_array(T (&& items)[N])
+{
+ return impl::to_array_impl(std::move(items), std::make_index_sequence<N>{});
+}
+
+// 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 auto MembershipStrings = to_array(
+ // 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 auto JoinStateStrings = to_array({
+ 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 +94,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));