aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-09-05 07:49:16 +0200
committerGitHub <noreply@github.com>2022-09-05 07:49:16 +0200
commit1e263a32fcbc44985e474a626393494a81f15e37 (patch)
tree8811e0a995dcd593cb9f233e02ece9402e76eb1b /lib
parent8cb629f406f5b8b1ff7ce787dd3967d5684e07c3 (diff)
parentbd2736bc9f8b6023ecbc21d0d831856703b853db (diff)
downloadlibquotient-1e263a32fcbc44985e474a626393494a81f15e37.tar.gz
libquotient-1e263a32fcbc44985e474a626393494a81f15e37.zip
Merge pull request #565 from quotient-im/kitsune/streamline-event-types-2
Streamline event types, part 2
Diffstat (limited to 'lib')
-rw-r--r--lib/connection.cpp15
-rw-r--r--lib/connection.h4
-rw-r--r--lib/csapi/event_context.h3
-rw-r--r--lib/csapi/message_pagination.h2
-rw-r--r--lib/csapi/notifications.h2
-rw-r--r--lib/csapi/peeking_events.h2
-rw-r--r--lib/csapi/relations.h2
-rw-r--r--lib/csapi/rooms.h3
-rw-r--r--lib/csapi/search.h3
-rw-r--r--lib/csapi/space_hierarchy.h2
-rw-r--r--lib/eventitem.h14
-rw-r--r--lib/events/accountdataevents.h18
-rw-r--r--lib/events/callanswerevent.cpp41
-rw-r--r--lib/events/callanswerevent.h24
-rw-r--r--lib/events/callcandidatesevent.h32
-rw-r--r--lib/events/callevents.cpp82
-rw-r--r--lib/events/callevents.h99
-rw-r--r--lib/events/callhangupevent.h23
-rw-r--r--lib/events/callinviteevent.cpp44
-rw-r--r--lib/events/callinviteevent.h27
-rw-r--r--lib/events/directchatevent.h5
-rw-r--r--lib/events/encryptedevent.cpp30
-rw-r--r--lib/events/encryptedevent.h11
-rw-r--r--lib/events/encryptionevent.cpp1
-rw-r--r--lib/events/encryptionevent.h13
-rw-r--r--lib/events/event.cpp49
-rw-r--r--lib/events/event.h538
-rw-r--r--lib/events/eventloader.h40
-rw-r--r--lib/events/keyverificationevent.h58
-rw-r--r--lib/events/reactionevent.h17
-rw-r--r--lib/events/receiptevent.cpp16
-rw-r--r--lib/events/receiptevent.h16
-rw-r--r--lib/events/redactionevent.h6
-rw-r--r--lib/events/roomavatarevent.h22
-rw-r--r--lib/events/roomcanonicalaliasevent.h23
-rw-r--r--lib/events/roomcreateevent.h9
-rw-r--r--lib/events/roomevent.cpp31
-rw-r--r--lib/events/roomevent.h39
-rw-r--r--lib/events/roomkeyevent.cpp22
-rw-r--r--lib/events/roomkeyevent.h14
-rw-r--r--lib/events/roommemberevent.cpp3
-rw-r--r--lib/events/roommemberevent.h34
-rw-r--r--lib/events/roommessageevent.cpp6
-rw-r--r--lib/events/roommessageevent.h4
-rw-r--r--lib/events/roompowerlevelsevent.h12
-rw-r--r--lib/events/roomtombstoneevent.h9
-rw-r--r--lib/events/simplestateevents.h45
-rw-r--r--lib/events/single_key_value.h15
-rw-r--r--lib/events/stateevent.cpp24
-rw-r--r--lib/events/stateevent.h168
-rw-r--r--lib/events/stickerevent.h3
-rw-r--r--lib/events/typingevent.cpp11
-rw-r--r--lib/events/typingevent.h10
-rw-r--r--lib/expected.h7
-rw-r--r--lib/room.cpp49
-rw-r--r--lib/room.h27
-rw-r--r--lib/roomstateview.cpp10
-rw-r--r--lib/roomstateview.h145
-rw-r--r--lib/syncdata.cpp2
59 files changed, 1045 insertions, 941 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index 79d7ae55..d9268028 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -28,7 +28,6 @@
#include "csapi/whoami.h"
#include "events/directchatevent.h"
-#include "events/eventloader.h"
#include "jobs/downloadfilejob.h"
#include "jobs/mediathumbnailjob.h"
#include "jobs/syncjob.h"
@@ -189,7 +188,7 @@ public:
emit q->accountDataChanged(eventType);
}
- template <typename EventT, typename ContentT>
+ template <EventClass EventT, typename ContentT>
void packAndSendAccountData(ContentT&& content)
{
packAndSendAccountData(
@@ -1701,7 +1700,7 @@ bool Connection::isIgnored(const User* user) const
IgnoredUsersList Connection::ignoredUsers() const
{
const auto* event = accountData<IgnoredUsersEvent>();
- return event ? event->ignored_users() : IgnoredUsersList();
+ return event ? event->ignoredUsers() : IgnoredUsersList();
}
void Connection::addToIgnoredUsers(const User* user)
@@ -2243,10 +2242,12 @@ void Connection::saveOlmAccount()
#ifdef Quotient_E2EE_ENABLED
QJsonObject Connection::decryptNotification(const QJsonObject &notification)
{
- auto r = room(notification["room_id"].toString());
- auto event = makeEvent<EncryptedEvent>(notification["event"].toObject());
- const auto decrypted = r->decryptMessage(*event);
- return decrypted ? decrypted->fullJson() : QJsonObject();
+ if (auto r = room(notification["room_id"].toString()))
+ if (auto event =
+ loadEvent<EncryptedEvent>(notification["event"].toObject()))
+ if (const auto decrypted = r->decryptMessage(*event))
+ return decrypted->fullJson();
+ return QJsonObject();
}
Database* Connection::database() const
diff --git a/lib/connection.h b/lib/connection.h
index 39921938..5afcfc2f 100644
--- a/lib/connection.h
+++ b/lib/connection.h
@@ -111,7 +111,7 @@ auto defaultUserFactory(Connection* c, const QString& id)
// are stored with no regard to their state.
using DirectChatsMap = QMultiHash<const User*, QString>;
using DirectChatUsersMap = QMultiHash<QString, User*>;
-using IgnoredUsersList = IgnoredUsersEvent::content_type;
+using IgnoredUsersList = IgnoredUsersEvent::value_type;
class QUOTIENT_API Connection : public QObject {
Q_OBJECT
@@ -189,7 +189,7 @@ public:
//! of that type.
//! \note Direct chats map cannot be retrieved using this method _yet_;
//! use directChats() instead.
- template <typename EventT>
+ template <EventClass EventT>
const EventT* accountData() const
{
return eventCast<EventT>(accountData(EventT::TypeId));
diff --git a/lib/csapi/event_context.h b/lib/csapi/event_context.h
index 662b976b..1614c7ed 100644
--- a/lib/csapi/event_context.h
+++ b/lib/csapi/event_context.h
@@ -4,7 +4,8 @@
#pragma once
-#include "events/eventloader.h"
+#include "events/roomevent.h"
+#include "events/stateevent.h"
#include "jobs/basejob.h"
namespace Quotient {
diff --git a/lib/csapi/message_pagination.h b/lib/csapi/message_pagination.h
index 9831ae2d..b4f3a38a 100644
--- a/lib/csapi/message_pagination.h
+++ b/lib/csapi/message_pagination.h
@@ -4,7 +4,7 @@
#pragma once
-#include "events/eventloader.h"
+#include "events/roomevent.h"
#include "jobs/basejob.h"
namespace Quotient {
diff --git a/lib/csapi/notifications.h b/lib/csapi/notifications.h
index 48167877..ff8aa47f 100644
--- a/lib/csapi/notifications.h
+++ b/lib/csapi/notifications.h
@@ -4,7 +4,7 @@
#pragma once
-#include "events/eventloader.h"
+#include "events/event.h"
#include "jobs/basejob.h"
namespace Quotient {
diff --git a/lib/csapi/peeking_events.h b/lib/csapi/peeking_events.h
index ff688c49..a67d2e4a 100644
--- a/lib/csapi/peeking_events.h
+++ b/lib/csapi/peeking_events.h
@@ -4,7 +4,7 @@
#pragma once
-#include "events/eventloader.h"
+#include "events/roomevent.h"
#include "jobs/basejob.h"
namespace Quotient {
diff --git a/lib/csapi/relations.h b/lib/csapi/relations.h
index 985a43b5..794ae445 100644
--- a/lib/csapi/relations.h
+++ b/lib/csapi/relations.h
@@ -4,7 +4,7 @@
#pragma once
-#include "events/eventloader.h"
+#include "events/roomevent.h"
#include "jobs/basejob.h"
namespace Quotient {
diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h
index 247fb13f..7823a1b0 100644
--- a/lib/csapi/rooms.h
+++ b/lib/csapi/rooms.h
@@ -4,7 +4,8 @@
#pragma once
-#include "events/eventloader.h"
+#include "events/roomevent.h"
+#include "events/stateevent.h"
#include "jobs/basejob.h"
namespace Quotient {
diff --git a/lib/csapi/search.h b/lib/csapi/search.h
index 8683413d..30095f32 100644
--- a/lib/csapi/search.h
+++ b/lib/csapi/search.h
@@ -6,7 +6,8 @@
#include "csapi/definitions/room_event_filter.h"
-#include "events/eventloader.h"
+#include "events/roomevent.h"
+#include "events/stateevent.h"
#include "jobs/basejob.h"
namespace Quotient {
diff --git a/lib/csapi/space_hierarchy.h b/lib/csapi/space_hierarchy.h
index 7a421be8..e5da6df2 100644
--- a/lib/csapi/space_hierarchy.h
+++ b/lib/csapi/space_hierarchy.h
@@ -4,7 +4,7 @@
#pragma once
-#include "events/eventloader.h"
+#include "events/stateevent.h"
#include "jobs/basejob.h"
namespace Quotient {
diff --git a/lib/eventitem.h b/lib/eventitem.h
index 90d9f458..96e45b38 100644
--- a/lib/eventitem.h
+++ b/lib/eventitem.h
@@ -5,6 +5,7 @@
#include "quotient_common.h"
+#include "events/callevents.h"
#include "events/filesourceinfo.h"
#include "events/stateevent.h"
@@ -45,7 +46,7 @@ public:
const RoomEvent* event() const { return rawPtr(evt); }
const RoomEvent* get() const { return event(); }
- template <typename EventT>
+ template <EventClass<RoomEvent> EventT>
const EventT* viewAs() const
{
return eventCast<const EventT>(evt);
@@ -66,7 +67,7 @@ public:
std::any& userData() { return data; }
protected:
- template <typename EventT>
+ template <EventClass<RoomEvent> EventT>
EventT* getAs()
{
return eventCast<EventT>(evt);
@@ -94,16 +95,15 @@ private:
};
template <>
-inline const StateEventBase* EventItemBase::viewAs<StateEventBase>() const
+inline const StateEvent* EventItemBase::viewAs<StateEvent>() const
{
- return evt->isStateEvent() ? weakPtrCast<const StateEventBase>(evt)
- : nullptr;
+ return evt->isStateEvent() ? weakPtrCast<const StateEvent>(evt) : nullptr;
}
template <>
-inline const CallEventBase* EventItemBase::viewAs<CallEventBase>() const
+inline const CallEvent* EventItemBase::viewAs<CallEvent>() const
{
- return evt->isCallEvent() ? weakPtrCast<const CallEventBase>(evt) : nullptr;
+ return evt->is<CallEvent>() ? weakPtrCast<const CallEvent>(evt) : nullptr;
}
class QUOTIENT_API PendingEventItem : public EventItemBase {
diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h
index 24c3353c..324ce449 100644
--- a/lib/events/accountdataevents.h
+++ b/lib/events/accountdataevents.h
@@ -4,7 +4,6 @@
#pragma once
#include "event.h"
-#include "util.h"
namespace Quotient {
constexpr auto FavouriteTag [[maybe_unused]] = "m.favourite"_ls;
@@ -46,14 +45,21 @@ struct JsonObjectConverter<TagRecord> {
using TagsMap = QHash<QString, TagRecord>;
-DEFINE_SIMPLE_EVENT(TagEvent, Event, "m.tag", TagsMap, tags)
-DEFINE_SIMPLE_EVENT(ReadMarkerEventImpl, Event, "m.fully_read", QString, eventId)
+DEFINE_SIMPLE_EVENT(TagEvent, Event, "m.tag", TagsMap, tags, "tags")
+DEFINE_SIMPLE_EVENT(ReadMarkerEventImpl, Event, "m.fully_read", QString,
+ eventId, "event_id")
class ReadMarkerEvent : public ReadMarkerEventImpl {
public:
using ReadMarkerEventImpl::ReadMarkerEventImpl;
[[deprecated("Use ReadMarkerEvent::eventId() instead")]]
- QString event_id() const { return eventId(); }
+ auto event_id() const { return eventId(); }
+};
+DEFINE_SIMPLE_EVENT(IgnoredUsersEventImpl, Event, "m.ignored_user_list",
+ QSet<QString>, ignoredUsers, "ignored_users")
+class IgnoredUsersEvent : public IgnoredUsersEventImpl {
+public:
+ using IgnoredUsersEventImpl::IgnoredUsersEventImpl;
+ [[deprecated("Use IgnoredUsersEvent::ignoredUsers() instead")]]
+ auto ignored_users() const { return ignoredUsers(); }
};
-DEFINE_SIMPLE_EVENT(IgnoredUsersEvent, Event, "m.ignored_user_list", QSet<QString>,
- ignored_users)
} // namespace Quotient
diff --git a/lib/events/callanswerevent.cpp b/lib/events/callanswerevent.cpp
deleted file mode 100644
index f75f8ad3..00000000
--- a/lib/events/callanswerevent.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Marius Gripsgard <marius@ubports.com>
-// SPDX-FileCopyrightText: 2018 Josip Delic <delijati@googlemail.com>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#include "callanswerevent.h"
-
-/*
-m.call.answer
-{
- "age": 242352,
- "content": {
- "answer": {
- "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
- "type": "answer"
- },
- "call_id": "12345",
- "version": 0
- },
- "event_id": "$WLGTSEFSEF:localhost",
- "origin_server_ts": 1431961217939,
- "room_id": "!Cuyf34gef24t:localhost",
- "sender": "@example:localhost",
- "type": "m.call.answer"
-}
-*/
-
-using namespace Quotient;
-
-CallAnswerEvent::CallAnswerEvent(const QJsonObject& obj)
- : CallEventBase(typeId(), obj)
-{
- qCDebug(EVENTS) << "Call Answer event";
-}
-
-CallAnswerEvent::CallAnswerEvent(const QString& callId, const QString& sdp)
- : CallEventBase(
- typeId(), matrixTypeId(), callId, 0,
- { { QStringLiteral("answer"),
- QJsonObject { { QStringLiteral("type"), QStringLiteral("answer") },
- { QStringLiteral("sdp"), sdp } } } })
-{}
diff --git a/lib/events/callanswerevent.h b/lib/events/callanswerevent.h
deleted file mode 100644
index 4d539b85..00000000
--- a/lib/events/callanswerevent.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Marius Gripsgard <marius@ubports.com>
-// SPDX-FileCopyrightText: 2018 Josip Delic <delijati@googlemail.com>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#pragma once
-
-#include "roomevent.h"
-
-namespace Quotient {
-class QUOTIENT_API CallAnswerEvent : public CallEventBase {
-public:
- DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent)
-
- explicit CallAnswerEvent(const QJsonObject& obj);
-
- explicit CallAnswerEvent(const QString& callId, const QString& sdp);
-
- QString sdp() const
- {
- return contentPart<QJsonObject>("answer"_ls).value("sdp"_ls).toString();
- }
-};
-REGISTER_EVENT_TYPE(CallAnswerEvent)
-} // namespace Quotient
diff --git a/lib/events/callcandidatesevent.h b/lib/events/callcandidatesevent.h
deleted file mode 100644
index e949f722..00000000
--- a/lib/events/callcandidatesevent.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Marius Gripsgard <marius@ubports.com>
-// SPDX-FileCopyrightText: 2018 Josip Delic <delijati@googlemail.com>
-// SPDX-FileCopyrightText: 2018 Kitsune Ral <Kitsune-Ral@users.sf.net>
-// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#pragma once
-
-#include "roomevent.h"
-
-namespace Quotient {
-class CallCandidatesEvent : public CallEventBase {
-public:
- DEFINE_EVENT_TYPEID("m.call.candidates", CallCandidatesEvent)
-
- explicit CallCandidatesEvent(const QJsonObject& obj)
- : CallEventBase(typeId(), obj)
- {}
-
- explicit CallCandidatesEvent(const QString& callId,
- const QJsonArray& candidates)
- : CallEventBase(typeId(), matrixTypeId(), callId, 0,
- { { QStringLiteral("candidates"), candidates } })
- {}
-
- QUO_CONTENT_GETTER(QJsonArray, candidates)
- QUO_CONTENT_GETTER(QString, callId)
- QUO_CONTENT_GETTER(int, version)
-};
-
-REGISTER_EVENT_TYPE(CallCandidatesEvent)
-} // namespace Quotient
diff --git a/lib/events/callevents.cpp b/lib/events/callevents.cpp
new file mode 100644
index 00000000..3873614d
--- /dev/null
+++ b/lib/events/callevents.cpp
@@ -0,0 +1,82 @@
+// SPDX-FileCopyrightText: 2022 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "callevents.h"
+
+#include "logging.h"
+
+using namespace Quotient;
+
+QJsonObject CallEvent::basicJson(const QString& matrixType,
+ const QString& callId, int version,
+ QJsonObject contentJson)
+{
+ contentJson.insert(QStringLiteral("call_id"), callId);
+ contentJson.insert(QStringLiteral("version"), version);
+ return RoomEvent::basicJson(matrixType, contentJson);
+}
+
+CallEvent::CallEvent(const QJsonObject& json)
+ : RoomEvent(json)
+{
+ if (callId().isEmpty())
+ qCWarning(EVENTS) << id() << "is a call event with an empty call id";
+}
+
+/*
+m.call.invite
+{
+ "age": 242352,
+ "content": {
+ "call_id": "12345",
+ "lifetime": 60000,
+ "offer": {
+ "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
+ "type": "offer"
+ },
+ "version": 0
+ },
+ "event_id": "$WLGTSEFSEF:localhost",
+ "origin_server_ts": 1431961217939,
+ "room_id": "!Cuyf34gef24t:localhost",
+ "sender": "@example:localhost",
+ "type": "m.call.invite"
+}
+*/
+
+CallInviteEvent::CallInviteEvent(const QString& callId, int lifetime,
+ const QString& sdp)
+ : EventTemplate(
+ callId,
+ { { QStringLiteral("lifetime"), lifetime },
+ { QStringLiteral("offer"),
+ QJsonObject{ { QStringLiteral("type"), QStringLiteral("offer") },
+ { QStringLiteral("sdp"), sdp } } } })
+{}
+
+/*
+m.call.answer
+{
+ "age": 242352,
+ "content": {
+ "answer": {
+ "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
+ "type": "answer"
+ },
+ "call_id": "12345",
+ "version": 0
+ },
+ "event_id": "$WLGTSEFSEF:localhost",
+ "origin_server_ts": 1431961217939,
+ "room_id": "!Cuyf34gef24t:localhost",
+ "sender": "@example:localhost",
+ "type": "m.call.answer"
+}
+*/
+
+CallAnswerEvent::CallAnswerEvent(const QString& callId, const QString& sdp)
+ : EventTemplate(callId, { { QStringLiteral("answer"),
+ QJsonObject { { QStringLiteral("type"),
+ QStringLiteral("answer") },
+ { QStringLiteral("sdp"), sdp } } } })
+{}
diff --git a/lib/events/callevents.h b/lib/events/callevents.h
new file mode 100644
index 00000000..752e331d
--- /dev/null
+++ b/lib/events/callevents.h
@@ -0,0 +1,99 @@
+// SPDX-FileCopyrightText: 2022 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#pragma once
+
+#include "roomevent.h"
+
+namespace Quotient {
+
+class QUOTIENT_API CallEvent : public RoomEvent {
+public:
+ QUO_BASE_EVENT(CallEvent, "m.call.*"_ls, RoomEvent::BaseMetaType)
+ static bool matches(const QJsonObject&, const QString& mType)
+ {
+ return mType.startsWith("m.call.");
+ }
+
+ QUO_CONTENT_GETTER(QString, callId)
+ QUO_CONTENT_GETTER(int, version)
+
+protected:
+ explicit CallEvent(const QJsonObject& json);
+
+ static QJsonObject basicJson(const QString& matrixType,
+ const QString& callId, int version,
+ QJsonObject contentJson = {});
+};
+using CallEventBase
+ [[deprecated("CallEventBase is CallEvent now")]] = CallEvent;
+
+template <typename EventT>
+class EventTemplate<EventT, CallEvent> : public CallEvent {
+public:
+ using CallEvent::CallEvent;
+ explicit EventTemplate(const QString& callId,
+ const QJsonObject& contentJson = {})
+ : EventTemplate(basicJson(EventT::TypeId, callId, 0, contentJson))
+ {}
+};
+
+template <typename EventT, typename ContentT>
+class EventTemplate<EventT, CallEvent, ContentT>
+ : public EventTemplate<EventT, CallEvent> {
+public:
+ using EventTemplate<EventT, CallEvent>::EventTemplate;
+ template <typename... ContentParamTs>
+ explicit EventTemplate(const QString& callId,
+ ContentParamTs&&... contentParams)
+ : EventTemplate<EventT, CallEvent>(
+ callId,
+ toJson(ContentT{ std::forward<ContentParamTs>(contentParams)... }))
+ {}
+};
+
+class QUOTIENT_API CallInviteEvent
+ : public EventTemplate<CallInviteEvent, CallEvent> {
+public:
+ QUO_EVENT(CallInviteEvent, "m.call.invite")
+
+ using EventTemplate::EventTemplate;
+
+ explicit CallInviteEvent(const QString& callId, int lifetime,
+ const QString& sdp);
+
+ QUO_CONTENT_GETTER(int, lifetime)
+ QString sdp() const
+ {
+ return contentPart<QJsonObject>("offer"_ls).value("sdp"_ls).toString();
+ }
+};
+
+DEFINE_SIMPLE_EVENT(CallCandidatesEvent, CallEvent, "m.call.candidates",
+ QJsonArray, candidates, "candidates")
+
+class QUOTIENT_API CallAnswerEvent
+ : public EventTemplate<CallAnswerEvent, CallEvent> {
+public:
+ QUO_EVENT(CallAnswerEvent, "m.call.answer")
+
+ using EventTemplate::EventTemplate;
+
+ explicit CallAnswerEvent(const QString& callId, const QString& sdp);
+
+ QString sdp() const
+ {
+ return contentPart<QJsonObject>("answer"_ls).value("sdp"_ls).toString();
+ }
+};
+
+class QUOTIENT_API CallHangupEvent
+ : public EventTemplate<CallHangupEvent, CallEvent> {
+public:
+ QUO_EVENT(CallHangupEvent, "m.call.hangup")
+ using EventTemplate::EventTemplate;
+};
+
+} // namespace Quotient
+Q_DECLARE_METATYPE(Quotient::CallEvent*)
+Q_DECLARE_METATYPE(const Quotient::CallEvent*)
diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h
deleted file mode 100644
index b0017c59..00000000
--- a/lib/events/callhangupevent.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Marius Gripsgard <marius@ubports.com>
-// SPDX-FileCopyrightText: 2018 Josip Delic <delijati@googlemail.com>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#pragma once
-
-#include "roomevent.h"
-
-namespace Quotient {
-class QUOTIENT_API CallHangupEvent : public CallEventBase {
-public:
- DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent)
-
- explicit CallHangupEvent(const QJsonObject& obj)
- : CallEventBase(typeId(), obj)
- {}
- explicit CallHangupEvent(const QString& callId)
- : CallEventBase(typeId(), matrixTypeId(), callId, 0)
- {}
-};
-
-REGISTER_EVENT_TYPE(CallHangupEvent)
-} // namespace Quotient
diff --git a/lib/events/callinviteevent.cpp b/lib/events/callinviteevent.cpp
deleted file mode 100644
index 2f26a1cb..00000000
--- a/lib/events/callinviteevent.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Marius Gripsgard <marius@ubports.com>
-// SPDX-FileCopyrightText: 2018 Josip Delic <delijati@googlemail.com>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#include "callinviteevent.h"
-
-/*
-m.call.invite
-{
- "age": 242352,
- "content": {
- "call_id": "12345",
- "lifetime": 60000,
- "offer": {
- "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
- "type": "offer"
- },
- "version": 0
- },
- "event_id": "$WLGTSEFSEF:localhost",
- "origin_server_ts": 1431961217939,
- "room_id": "!Cuyf34gef24t:localhost",
- "sender": "@example:localhost",
- "type": "m.call.invite"
-}
-*/
-
-using namespace Quotient;
-
-CallInviteEvent::CallInviteEvent(const QJsonObject& obj)
- : CallEventBase(typeId(), obj)
-{
- qCDebug(EVENTS) << "Call Invite event";
-}
-
-CallInviteEvent::CallInviteEvent(const QString& callId, int lifetime,
- const QString& sdp)
- : CallEventBase(
- typeId(), matrixTypeId(), callId, 0,
- { { QStringLiteral("lifetime"), lifetime },
- { QStringLiteral("offer"),
- QJsonObject { { QStringLiteral("type"), QStringLiteral("offer") },
- { QStringLiteral("sdp"), sdp } } } })
-{}
diff --git a/lib/events/callinviteevent.h b/lib/events/callinviteevent.h
deleted file mode 100644
index 5b4ca0df..00000000
--- a/lib/events/callinviteevent.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Marius Gripsgard <marius@ubports.com>
-// SPDX-FileCopyrightText: 2018 Josip Delic <delijati@googlemail.com>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#pragma once
-
-#include "roomevent.h"
-
-namespace Quotient {
-class QUOTIENT_API CallInviteEvent : public CallEventBase {
-public:
- DEFINE_EVENT_TYPEID("m.call.invite", CallInviteEvent)
-
- explicit CallInviteEvent(const QJsonObject& obj);
-
- explicit CallInviteEvent(const QString& callId, int lifetime,
- const QString& sdp);
-
- QUO_CONTENT_GETTER(int, lifetime)
- QString sdp() const
- {
- return contentPart<QJsonObject>("offer"_ls).value("sdp"_ls).toString();
- }
-};
-
-REGISTER_EVENT_TYPE(CallInviteEvent)
-} // namespace Quotient
diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h
index 2018d3d6..0756d816 100644
--- a/lib/events/directchatevent.h
+++ b/lib/events/directchatevent.h
@@ -8,11 +8,10 @@
namespace Quotient {
class QUOTIENT_API DirectChatEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent)
+ QUO_EVENT(DirectChatEvent, "m.direct")
- explicit DirectChatEvent(const QJsonObject& obj) : Event(typeId(), obj) {}
+ using Event::Event;
QMultiHash<QString, QString> usersToDirectChats() const;
};
-REGISTER_EVENT_TYPE(DirectChatEvent)
} // namespace Quotient
diff --git a/lib/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp
index ec00ad4c..94b44901 100644
--- a/lib/events/encryptedevent.cpp
+++ b/lib/events/encryptedevent.cpp
@@ -2,33 +2,31 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "encryptedevent.h"
-#include "roommessageevent.h"
-#include "events/eventloader.h"
+#include "logging.h"
using namespace Quotient;
-EncryptedEvent::EncryptedEvent(const QJsonObject& ciphertext,
+EncryptedEvent::EncryptedEvent(const QJsonObject& ciphertexts,
const QString& senderKey)
- : RoomEvent(typeId(), matrixTypeId(),
- { { AlgorithmKeyL, OlmV1Curve25519AesSha2AlgoKey },
- { CiphertextKeyL, ciphertext },
+ : RoomEvent({ { AlgorithmKeyL, OlmV1Curve25519AesSha2AlgoKey },
+ { CiphertextKeyL, ciphertexts },
{ SenderKeyKeyL, senderKey } })
{}
-EncryptedEvent::EncryptedEvent(QByteArray ciphertext, const QString& senderKey,
+EncryptedEvent::EncryptedEvent(const QByteArray& ciphertext,
+ const QString& senderKey,
const QString& deviceId, const QString& sessionId)
- : RoomEvent(typeId(), matrixTypeId(),
- {
- { AlgorithmKeyL, MegolmV1AesSha2AlgoKey },
- { CiphertextKeyL, QString(ciphertext) },
- { DeviceIdKeyL, deviceId },
- { SenderKeyKeyL, senderKey },
- { SessionIdKeyL, sessionId },
- })
+ : RoomEvent({
+ { AlgorithmKeyL, MegolmV1AesSha2AlgoKey },
+ { CiphertextKeyL, QString(ciphertext) },
+ { DeviceIdKeyL, deviceId },
+ { SenderKeyKeyL, senderKey },
+ { SessionIdKeyL, sessionId },
+ })
{}
EncryptedEvent::EncryptedEvent(const QJsonObject& obj)
- : RoomEvent(typeId(), obj)
+ : RoomEvent(obj)
{
qCDebug(E2EE) << "Encrypted event from" << senderId();
}
diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h
index ddd5e415..02d4c7aa 100644
--- a/lib/events/encryptedevent.h
+++ b/lib/events/encryptedevent.h
@@ -27,16 +27,17 @@ namespace Quotient {
*/
class QUOTIENT_API EncryptedEvent : public RoomEvent {
public:
- DEFINE_EVENT_TYPEID("m.room.encrypted", EncryptedEvent)
+ QUO_EVENT(EncryptedEvent, "m.room.encrypted")
/* In case with Olm, the encrypted content of the event is
* a map from the recipient Curve25519 identity key to ciphertext
* information */
- explicit EncryptedEvent(const QJsonObject& ciphertext,
+ explicit EncryptedEvent(const QJsonObject& ciphertexts,
const QString& senderKey);
/* In case with Megolm, device_id and session_id are required */
- explicit EncryptedEvent(QByteArray ciphertext, const QString& senderKey,
- const QString& deviceId, const QString& sessionId);
+ explicit EncryptedEvent(const QByteArray& ciphertext,
+ const QString& senderKey, const QString& deviceId,
+ const QString& sessionId);
explicit EncryptedEvent(const QJsonObject& obj);
QString algorithm() const;
@@ -59,6 +60,4 @@ public:
void setRelation(const QJsonObject& relation);
};
-REGISTER_EVENT_TYPE(EncryptedEvent)
-
} // namespace Quotient
diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp
index 8872447b..b1b04984 100644
--- a/lib/events/encryptionevent.cpp
+++ b/lib/events/encryptionevent.cpp
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "encryptionevent.h"
+#include "logging.h"
#include "e2ee/e2ee.h"
diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h
index 91452c3f..4bf7459c 100644
--- a/lib/events/encryptionevent.h
+++ b/lib/events/encryptionevent.h
@@ -26,20 +26,16 @@ public:
int rotationPeriodMsgs = 100;
};
-class QUOTIENT_API EncryptionEvent : public StateEvent<EncryptionEventContent> {
+class QUOTIENT_API EncryptionEvent
+ : public KeylessStateEventBase<EncryptionEvent, EncryptionEventContent> {
public:
- DEFINE_EVENT_TYPEID("m.room.encryption", EncryptionEvent)
+ QUO_EVENT(EncryptionEvent, "m.room.encryption")
using EncryptionType
[[deprecated("Use Quotient::EncryptionType instead")]] =
Quotient::EncryptionType;
- explicit EncryptionEvent(const QJsonObject& obj)
- : StateEvent(typeId(), obj)
- {}
- explicit EncryptionEvent(EncryptionEventContent&& content)
- : StateEvent(typeId(), matrixTypeId(), QString(), std::move(content))
- {}
+ using KeylessStateEventBase::KeylessStateEventBase;
Quotient::EncryptionType encryption() const { return content().encryption; }
QString algorithm() const { return content().algorithm; }
@@ -48,5 +44,4 @@ public:
bool useEncryption() const { return !algorithm().isEmpty(); }
};
-REGISTER_EVENT_TYPE(EncryptionEvent)
} // namespace Quotient
diff --git a/lib/events/event.cpp b/lib/events/event.cpp
index 1f1eebaa..da7de919 100644
--- a/lib/events/event.cpp
+++ b/lib/events/event.cpp
@@ -3,7 +3,9 @@
#include "event.h"
+#include "callevents.h"
#include "logging.h"
+#include "stateevent.h"
#include <QtCore/QJsonDocument>
@@ -11,15 +13,42 @@ using namespace Quotient;
QString EventTypeRegistry::getMatrixType(event_type_t typeId) { return typeId; }
-void _impl::EventFactoryBase::logAddingMethod(event_type_t TypeId,
- size_t newSize)
+void AbstractEventMetaType::addDerived(AbstractEventMetaType* newType)
{
- qDebug(EVENTS) << "Adding factory method for" << TypeId << "events;"
- << newSize << "methods will be in the" << name
- << "chain";
+ if (const auto existing =
+ std::find_if(derivedTypes.cbegin(), derivedTypes.cend(),
+ [&newType](const AbstractEventMetaType* t) {
+ return t->matrixId == newType->matrixId;
+ });
+ existing != derivedTypes.cend())
+ {
+ if (*existing == newType)
+ return;
+ // Two different metatype objects claim the same Matrix type id; this
+ // is not normal, so give as much information as possible to diagnose
+ if ((*existing)->className == newType->className) {
+ qCritical(EVENTS)
+ << newType->className << "claims" << newType->matrixId
+ << "repeatedly; check that it's exported across translation "
+ "units or shared objects";
+ Q_ASSERT(false); // That situation is plain wrong
+ return; // So maybe std::terminate() even?
+ }
+ qWarning(EVENTS).nospace()
+ << newType->matrixId << " is already mapped to "
+ << (*existing)->className << " before " << newType->className
+ << "; unless the two have different isValid() conditions, the "
+ "latter class will never be used";
+ }
+ derivedTypes.emplace_back(newType);
+ qDebug(EVENTS).nospace()
+ << newType->matrixId << " -> " << newType->className << "; "
+ << derivedTypes.size() << " derived type(s) registered for "
+ << className;
}
-Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json)
+Event::Event(const QJsonObject& json)
+ : _json(json)
{
if (!json.contains(ContentKeyL)
&& !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL)) {
@@ -28,10 +57,6 @@ Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json)
}
}
-Event::Event(Type type, event_mtype_t matrixType, const QJsonObject& contentJson)
- : Event(type, basicJson(matrixType, contentJson))
-{}
-
Event::~Event() = default;
QString Event::matrixType() const { return fullJson()[TypeKeyL].toString(); }
@@ -48,6 +73,10 @@ const QJsonObject Event::unsignedJson() const
return fullJson()[UnsignedKeyL].toObject();
}
+bool Event::isStateEvent() const { return is<StateEvent>(); }
+
+bool Event::isCallEvent() const { return is<CallEvent>(); }
+
void Event::dumpTo(QDebug dbg) const
{
dbg << QJsonDocument(contentJson()).toJson(QJsonDocument::Compact);
diff --git a/lib/events/event.h b/lib/events/event.h
index b7454337..0abef1f0 100644
--- a/lib/events/event.h
+++ b/lib/events/event.h
@@ -4,11 +4,11 @@
#pragma once
#include "converters.h"
-#include "logging.h"
#include "function_traits.h"
+#include "single_key_value.h"
namespace Quotient {
-// === event_ptr_tt<> and type casting facilities ===
+// === event_ptr_tt<> and basic type casting facilities ===
template <typename EventT>
using event_ptr_tt = std::unique_ptr<EventT>;
@@ -48,141 +48,233 @@ const QString RoomIdKey { RoomIdKeyL };
const QString UnsignedKey { UnsignedKeyL };
const QString StateKeyKey { StateKeyKeyL };
-// === Event types ===
-
using event_type_t = QLatin1String;
-using event_mtype_t = const char*;
-
-class QUOTIENT_API EventTypeRegistry {
-public:
- ~EventTypeRegistry() = default;
- [[deprecated("event_type_t is a string now, use it directly instead")]]
+// TODO: Remove in 0.8
+struct QUOTIENT_API EventTypeRegistry {
+ [[deprecated("event_type_t is a string since libQuotient 0.7, use it directly instead")]]
static QString getMatrixType(event_type_t typeId);
-private:
- EventTypeRegistry() = default;
+ EventTypeRegistry() = delete;
+ ~EventTypeRegistry() = default;
Q_DISABLE_COPY_MOVE(EventTypeRegistry)
};
-template <typename EventT>
-constexpr event_type_t typeId()
-{
- return std::decay_t<EventT>::TypeId;
-}
+// === EventMetaType ===
-constexpr event_type_t UnknownEventTypeId = "?"_ls;
-[[deprecated("Use UnknownEventTypeId")]]
-constexpr event_type_t unknownEventTypeId() { return UnknownEventTypeId; }
+class Event;
-// === Event creation facilities ===
+// TODO: move over to std::derived_from<Event> once it's available everywhere
+template <typename EventT, typename BaseEventT = Event>
+concept EventClass = std::is_base_of_v<BaseEventT, EventT>;
-//! Create an event of arbitrary type from its arguments
-template <typename EventT, typename... ArgTs>
-inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args)
-{
- return std::make_unique<EventT>(std::forward<ArgTs>(args)...);
-}
+template <EventClass EventT>
+bool is(const Event& e);
-namespace _impl {
- class QUOTIENT_API EventFactoryBase {
- public:
- EventFactoryBase(const EventFactoryBase&) = delete;
-
- protected: // This class is only to inherit from
- explicit EventFactoryBase(const char* name)
- : name(name)
- {}
- void logAddingMethod(event_type_t TypeId, size_t newSize);
-
- private:
- const char* const name;
- };
-} // namespace _impl
-
-//! \brief A family of event factories to create events from CS API responses
+//! \brief The base class for event metatypes
//!
-//! Each of these factories, as instantiated by event base types (Event,
-//! RoomEvent etc.) is capable of producing an event object derived from
-//! \p BaseEventT, using the JSON payload and the event type passed to its
-//! make() method. Don't use these directly to make events; use loadEvent()
-//! overloads as the frontend for these. Never instantiate new factories
-//! outside of base event classes.
-//! \sa loadEvent, setupFactory, Event::factory, RoomEvent::factory,
-//! StateEventBase::factory
-template <typename BaseEventT>
-class EventFactory : public _impl::EventFactoryBase {
-private:
- using method_t = event_ptr_tt<BaseEventT> (*)(const QJsonObject&,
- const QString&);
- std::vector<method_t> methods {};
-
- template <class EventT>
- static event_ptr_tt<BaseEventT> makeIfMatches(const QJsonObject& json,
- const QString& matrixType)
+//! You should not normally have to use this directly, unless you need to devise
+//! a whole new kind of event metatypes.
+class QUOTIENT_API AbstractEventMetaType {
+public:
+ // The public fields here are const and are not to be changeable anyway.
+ // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
+ const char* const className;
+ const event_type_t matrixId;
+ const AbstractEventMetaType* const baseType = nullptr;
+ // NOLINTEND(misc-non-private-member-variables-in-classes)
+
+ explicit AbstractEventMetaType(const char* className)
+ : className(className)
+ {}
+ explicit AbstractEventMetaType(const char* className, event_type_t matrixId,
+ AbstractEventMetaType& nearestBase)
+ : className(className), matrixId(matrixId), baseType(&nearestBase)
{
- // If your matrix event type is not all ASCII, it's your problem
- // (see https://github.com/matrix-org/matrix-doc/pull/2758)
- return EventT::TypeId == matrixType ? makeEvent<EventT>(json) : nullptr;
+ nearestBase.addDerived(this);
}
+ void addDerived(AbstractEventMetaType *newType);
+
+ virtual ~AbstractEventMetaType() = default;
+
+protected:
+ // Allow template specialisations to call into one another
+ template <class EventT>
+ friend class EventMetaType;
+
+ // The returned value indicates whether a generic object has to be created
+ // on the top level when `event` is empty, instead of returning nullptr
+ virtual bool doLoadFrom(const QJsonObject& fullJson, const QString& type,
+ Event*& event) const = 0;
+
+private:
+ std::vector<const AbstractEventMetaType*> derivedTypes{};
+ Q_DISABLE_COPY_MOVE(AbstractEventMetaType)
+};
+
+// Any event metatype is unique (note Q_DISABLE_COPY_MOVE above) so can be
+// identified by its address
+inline bool operator==(const AbstractEventMetaType& lhs,
+ const AbstractEventMetaType& rhs)
+{
+ return &lhs == &rhs;
+}
+
+//! \brief A family of event meta-types to load and match events
+//!
+//! TL;DR for the loadFrom() story:
+//! - for base event types, use QUO_BASE_EVENT and, if you have additional
+//! validation (e.g., JSON has to contain a certain key - see StateEvent
+//! for a real example), define it in the static EventT::isValid() member
+//! function accepting QJsonObject and returning bool.
+//! - for leaf (specific) event types - simply use QUO_EVENT and it will do
+//! everything necessary, including the TypeId definition.
+//! \sa QUO_EVENT, QUO_BASE_EVENT
+template <class EventT>
+class QUOTIENT_API EventMetaType : public AbstractEventMetaType {
+ // Above: can't constrain EventT to be EventClass because it's incomplete
+ // at the point of EventMetaType<EventT> instantiation.
public:
- explicit EventFactory(const char* fName)
- : EventFactoryBase { fName }
- {}
+ using AbstractEventMetaType::AbstractEventMetaType;
- //! \brief Add a method to create events of a given type
+ //! \brief Try to load an event from JSON, with dynamic type resolution
//!
- //! Adds a standard factory method (makeIfMatches) for \p EventT so that
- //! event objects of this type can be created dynamically by loadEvent.
- //! The caller is responsible for ensuring this method is called only
- //! once per type.
- //! \sa loadEvent, Quotient::loadEvent
- template <class EventT>
- const auto& addMethod()
+ //! The generic logic defined in this class template and invoked applies to
+ //! all event types defined in the library and boils down to the following:
+ //! 1.
+ //! a. If EventT has TypeId defined (which normally is a case of
+ //! all leaf - specific - event types, via QUO_EVENT macro) and
+ //! \p type doesn't exactly match it, nullptr is immediately returned.
+ //! b. In absence of TypeId, an event type is assumed to be a base;
+ //! its derivedTypes are examined, and this algorithm is applied
+ //! recursively on each.
+ //! 2. Optional validation: if EventT (or, due to the way inheritance works,
+ //! any of its base event types) has a static isValid() predicate and
+ //! the event JSON does not satisfy it, nullptr is immediately returned
+ //! to the upper level or to the loadFrom() caller. This is how existence
+ //! of `state_key` is checked in any type derived from StateEvent.
+ //! 3. If step 1b above returned non-nullptr, immediately return it.
+ //! 4.
+ //! a. If EventT::isValid() or EventT::TypeId (either, or both) exist and
+ //! are satisfied (see steps 1a and 2 above), an object of this type
+ //! is created from the passed JSON and returned. In case of a base
+ //! event type, this will be a generic (aka "unknown") event.
+ //! b. If neither exists, a generic event is only created and returned
+ //! when on the top level (i.e., outside of recursion into
+ //! derivedTypes); lower levels return nullptr instead and the type
+ //! lookup continues. The latter is a case of a derived base event
+ //! metatype (e.g. RoomEvent) called from its base event metatype
+ //! (i.e., Event). If no matching type derived from RoomEvent is found,
+ //! the nested lookup returns nullptr rather than a generic RoomEvent,
+ //! so that other types derived from Event could be examined.
+ event_ptr_tt<EventT> loadFrom(const QJsonObject& fullJson,
+ const QString& type) const
{
- const auto m = &makeIfMatches<EventT>;
- const auto it = std::find(methods.cbegin(), methods.cend(), m);
- if (it != methods.cend())
- return *it;
- logAddingMethod(EventT::TypeId, methods.size() + 1);
- return methods.emplace_back(m);
+ Event* event = nullptr;
+ const bool goodEnough = doLoadFrom(fullJson, type, event);
+ if (!event && goodEnough)
+ return event_ptr_tt<EventT>{ new EventT(fullJson) };
+ return event_ptr_tt<EventT>{ static_cast<EventT*>(event) };
}
- auto loadEvent(const QJsonObject& json, const QString& matrixType)
+private:
+ bool doLoadFrom(const QJsonObject& fullJson, const QString& type,
+ Event*& event) const override
{
- for (const auto& f : methods)
- if (auto e = f(json, matrixType))
- return e;
- return makeEvent<BaseEventT>(UnknownEventTypeId, json);
+ if constexpr (requires { EventT::TypeId; }) {
+ if (EventT::TypeId != type)
+ return false;
+ } else {
+ for (const auto& p : derivedTypes) {
+ p->doLoadFrom(fullJson, type, event);
+ if (event) {
+ Q_ASSERT(is<EventT>(*event));
+ return false;
+ }
+ }
+ }
+ if constexpr (requires { EventT::isValid; }) {
+ if (!EventT::isValid(fullJson))
+ return false;
+ } else if constexpr (!requires { EventT::TypeId; })
+ return true; // Create a generic event object if on the top level
+ event = new EventT(fullJson);
+ return false;
}
};
-//! \brief Point of customisation to dynamically load events
+// === Event creation facilities ===
+
+//! \brief Create an event of arbitrary type from its arguments
//!
-//! The default specialisation of this calls BaseEventT::factory.loadEvent()
-//! and if that fails (i.e. returns nullptr) creates an unknown event of
-//! BaseEventT. Other specialisations may reuse other factories, add validations
-//! common to BaseEventT events, and so on.
-template <class BaseEventT>
-event_ptr_tt<BaseEventT> doLoadEvent(const QJsonObject& json,
- const QString& matrixType)
+//! This should not be used to load events from JSON - use loadEvent() for that.
+//! \sa loadEvent
+template <EventClass EventT, typename... ArgTs>
+inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args)
{
- return BaseEventT::factory.loadEvent(json, matrixType);
+ return std::make_unique<EventT>(std::forward<ArgTs>(args)...);
}
+template <EventClass EventT>
+constexpr const auto& mostSpecificMetaType()
+{
+ if constexpr (requires { EventT::MetaType; })
+ return EventT::MetaType;
+ else
+ return EventT::BaseMetaType;
+}
+
+//! \brief Create an event with proper type from a JSON object
+//!
+//! Use this factory template to detect the type from the JSON object
+//! contents (the detected event type should derive from the template
+//! parameter type) and create an event object of that type.
+template <EventClass EventT>
+inline event_ptr_tt<EventT> loadEvent(const QJsonObject& fullJson)
+{
+ return mostSpecificMetaType<EventT>().loadFrom(
+ fullJson, fullJson[TypeKeyL].toString());
+}
+
+//! \brief Create an event from a type string and content JSON
+//!
+//! Use this template to resolve the C++ type from the Matrix type string in
+//! \p matrixType and create an event of that type by passing all parameters
+//! to BaseEventT::basicJson().
+template <EventClass EventT>
+inline event_ptr_tt<EventT> loadEvent(const QString& matrixType,
+ const auto&... otherBasicJsonParams)
+{
+ return mostSpecificMetaType<EventT>().loadFrom(
+ EventT::basicJson(matrixType, otherBasicJsonParams...), matrixType);
+}
+
+template <EventClass EventT>
+struct JsonConverter<event_ptr_tt<EventT>>
+ : JsonObjectUnpacker<event_ptr_tt<EventT>> {
+ // No dump() to avoid any ambiguity on whether a given export to JSON uses
+ // fullJson() or only contentJson()
+ using JsonObjectUnpacker<event_ptr_tt<EventT>>::load;
+ static auto load(const QJsonObject& jo)
+ {
+ return loadEvent<EventT>(jo);
+ }
+};
+
// === Event ===
class QUOTIENT_API Event {
public:
using Type = event_type_t;
- static inline EventFactory<Event> factory { "Event" };
+ static inline EventMetaType<Event> BaseMetaType { "Event" };
+ virtual const AbstractEventMetaType& metaType() const
+ {
+ return BaseMetaType;
+ }
- explicit Event(Type type, const QJsonObject& json);
- explicit Event(Type type, event_mtype_t matrixType,
- const QJsonObject& contentJson = {});
Q_DISABLE_COPY(Event)
- Event(Event&&) = default;
+ Event(Event&&) noexcept = default;
Event& operator=(Event&&) = delete;
virtual ~Event();
@@ -193,8 +285,26 @@ public:
return { { TypeKey, matrixType }, { ContentKey, content } };
}
- Type type() const { return _type; }
+ //! \brief Event Matrix type, as identified by its metatype object
+ //!
+ //! For generic/unknown events it will contain a descriptive/generic string
+ //! defined by the respective base event type (that can be empty).
+ //! \sa matrixType
+ Type type() const { return metaType().matrixId; }
+
+ //! \brief Exact Matrix type stored in JSON
+ //!
+ //! Coincides with the result of type() (but is slower) for events defined
+ //! in C++ (not necessarily in the library); for generic/unknown events
+ //! the returned value will be different.
QString matrixType() const;
+
+ template <EventClass EventT>
+ bool is() const
+ {
+ return Quotient::is<EventT>(*this);
+ }
+
[[deprecated("Use fullJson() and stringify it with QJsonDocument::toJson() "
"or by other means")]]
QByteArray originalJson() const;
@@ -212,6 +322,11 @@ public:
const QJsonObject contentJson() const;
+ //! \brief Get a part of the content object, assuming a given type
+ //!
+ //! This retrieves the value under `content.<key>` from the event JSON and
+ //! then converts it to \p T using fromJson().
+ //! \sa contentJson, fromJson
template <typename T, typename KeyT>
const T contentPart(KeyT&& key) const
{
@@ -227,6 +342,11 @@ public:
const QJsonObject unsignedJson() const;
+ //! \brief Get a part of the unsigned object, assuming a given type
+ //!
+ //! This retrieves the value under `unsigned.<key>` from the event JSON and
+ //! then converts it to \p T using fromJson().
+ //! \sa unsignedJson, fromJson
template <typename T, typename KeyT>
const T unsignedPart(KeyT&& key) const
{
@@ -236,28 +356,138 @@ public:
friend QUOTIENT_API QDebug operator<<(QDebug dbg, const Event& e)
{
QDebugStateSaver _dss { dbg };
- dbg.noquote().nospace() << e.matrixType() << '(' << e.type() << "): ";
+ dbg.noquote().nospace()
+ << e.matrixType() << '(' << e.metaType().className << "): ";
e.dumpTo(dbg);
return dbg;
}
- virtual bool isStateEvent() const { return false; }
- virtual bool isCallEvent() const { return false; }
+ // State events are quite special in Matrix; so isStateEvent() is here,
+ // as an exception. For other base events, Event::is<>() and
+ // Quotient::is<>() should be used; don't add is* methods here
+ bool isStateEvent() const;
+ [[deprecated("Use is<CallEvent>() instead")]] bool isCallEvent() const;
protected:
+ friend class EventMetaType<Event>; // To access the below constructor
+
+ explicit Event(const QJsonObject& json);
+
QJsonObject& editJson() { return _json; }
virtual void dumpTo(QDebug dbg) const;
private:
- Type _type;
QJsonObject _json;
};
using EventPtr = event_ptr_tt<Event>;
-template <typename EventT>
+template <EventClass EventT>
using EventsArray = std::vector<event_ptr_tt<EventT>>;
using Events = EventsArray<Event>;
+// === Facilities for event class definitions ===
+
+//! \brief A template base class to derive your event type from
+//!
+//! This simple class template generates commonly used event constructor
+//! signatures and the content() method with the appropriate return type.
+//! The generic version here is only used with non-trivial \p ContentT (if you
+//! don't need to create an event from its content structure, just go and derive
+//! straight from the respective \p EventBaseT instead of using EventTemplate);
+//! specialisations may override that and provide useful semantics even without
+//! \p ContentT (see EventTemplate<CallEvent>, e.g.).
+//!
+//! The template uses CRTP to pick the event type id from the actual class;
+//! it will fail to compile if \p EventT doesn't provide TypeId. It also uses
+//! the base event type's basicJson(); if you need extra keys to be inserted
+//! you may want to bypass this template as writing the code to that effect in
+//! your class will likely be clearer and more concise.
+//! \sa https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
+//! \sa DEFINE_SIMPLE_EVENT
+template <typename EventT, EventClass BaseEventT, typename ContentT = void>
+class EventTemplate : public BaseEventT {
+ // Above: can't constrain EventT to be EventClass because it's incomplete
+ // by CRTP definition.
+public:
+ static_assert(
+ !std::is_same_v<ContentT, void>,
+ "If you see this, you tried to use EventTemplate with the default"
+ " ContentT type, which is void. This default is only used with explicit"
+ " specialisations (see CallEvent, e.g.). Otherwise, if you don't intend"
+ " to use the content part of EventTemplate then you don't need"
+ " EventTemplate; just use the base event class directly");
+ using content_type = ContentT;
+
+ explicit EventTemplate(const QJsonObject& json)
+ : BaseEventT(json)
+ {}
+ explicit EventTemplate(const ContentT& c)
+ : BaseEventT(EventT::basicJson(EventT::TypeId, toJson(c)))
+ {}
+
+ ContentT content() const { return fromJson<ContentT>(this->contentJson()); }
+};
+
+//! \brief Supply event metatype information in base event types
+//!
+//! Use this macro in a public section of your base event class to provide
+//! type identity and enable dynamic loading of generic events of that type.
+//! Do _not_ add this macro if your class is an intermediate wrapper and is not
+//! supposed to be instantiated on its own. Provides BaseMetaType static field
+//! initialised by parameters passed to the macro, and a metaType() override
+//! pointing to that BaseMetaType.
+//! \sa EventMetaType, EventMetaType::SuppressLoadDerived
+#define QUO_BASE_EVENT(CppType_, ...) \
+ friend class EventMetaType<CppType_>; \
+ static inline EventMetaType<CppType_> BaseMetaType{ \
+ #CppType_ __VA_OPT__(,) __VA_ARGS__ }; \
+ const AbstractEventMetaType& metaType() const override \
+ { \
+ return BaseMetaType; \
+ } \
+ // End of macro
+
+//! Supply event metatype information in (specific) event types
+//!
+//! Use this macro in a public section of your event class to provide type
+//! identity and enable dynamic loading of generic events of that type.
+//! Do _not_ use this macro if your class is an intermediate wrapper and is not
+//! supposed to be instantiated on its own. Provides MetaType static field
+//! initialised as described below; a metaType() override pointing to it; and
+//! the TypeId static field that is equal to MetaType.matrixId.
+//!
+//! The first two macro parameters are used as the first two EventMetaType
+//! constructor parameters; the third EventMetaType parameter is always
+//! BaseMetaType; and additional base types can be passed in extra macro
+//! parameters if you need to include the same event type in more than one
+//! event factory hierarchy (e.g., EncryptedEvent).
+//! \sa EventMetaType
+#define QUO_EVENT(CppType_, MatrixType_, ...) \
+ static inline const auto& TypeId = MatrixType_##_ls; \
+ friend class EventMetaType<CppType_>; \
+ static inline const EventMetaType<CppType_> MetaType{ \
+ #CppType_, TypeId, BaseMetaType __VA_OPT__(,) __VA_ARGS__ \
+ }; \
+ const AbstractEventMetaType& metaType() const override \
+ { \
+ return MetaType; \
+ } \
+ [[deprecated("Use " #CppType_ "::TypeId directly instead")]] \
+ static constexpr const char* matrixTypeId() { return MatrixType_; } \
+ [[deprecated("Use " #CppType_ "::TypeId directly instead")]] \
+ static event_type_t typeId() { return TypeId; } \
+ // End of macro
+
+//! \deprecated This is the old name for what is now known as QUO_EVENT
+#define DEFINE_EVENT_TYPEID(Type_, Id_) QUO_EVENT(Type_, Id_)
+
+#define QUO_CONTENT_GETTER_X(PartType_, PartName_, JsonKey_) \
+ PartType_ PartName_() const \
+ { \
+ static const auto PartName_##JsonKey = JsonKey_; \
+ return contentPart<PartType_>(PartName_##JsonKey); \
+ }
+
//! \brief Define an inline method obtaining a content part
//!
//! This macro adds a const method that extracts a JSON value at the key
@@ -266,32 +496,12 @@ using Events = EventsArray<Event>;
//! \code
//! contentPart<PartType_>(Quotient::toSnakeCase(#PartName_##_ls));
//! \endcode
-#define QUO_CONTENT_GETTER(PartType_, PartName_) \
- PartType_ PartName_() const \
- { \
- static const auto JsonKey = toSnakeCase(#PartName_##_ls); \
- return contentPart<PartType_>(JsonKey); \
- }
-
-// === Facilities for event class definitions ===
+#define QUO_CONTENT_GETTER(PartType_, PartName_) \
+ QUO_CONTENT_GETTER_X(PartType_, PartName_, toSnakeCase(#PartName_##_ls))
-// This macro should be used in a public section of an event class to
-// provide matrixTypeId() and typeId().
-#define DEFINE_EVENT_TYPEID(Id_, Type_) \
- static constexpr event_type_t TypeId = Id_##_ls; \
- [[deprecated("Use " #Type_ "::TypeId directly instead")]] \
- static constexpr event_mtype_t matrixTypeId() { return Id_; } \
- [[deprecated("Use " #Type_ "::TypeId directly instead")]] \
- static event_type_t typeId() { return TypeId; } \
- // End of macro
-
-// This macro should be put after an event class definition (in .h or .cpp)
-// to enable its deserialisation from a /sync and other
-// polymorphic event arrays
-#define REGISTER_EVENT_TYPE(Type_) \
- [[maybe_unused]] inline const auto& factoryMethodFor##Type_ = \
- Type_::factory.addMethod<Type_>(); \
- // End of macro
+//! \deprecated This macro was used after an event class definition
+//! to enable its dynamic loading; it is completely superseded by QUO_EVENT
+#define REGISTER_EVENT_TYPE(Type_)
/// \brief Define a new event class with a single key-value pair in the content
///
@@ -301,35 +511,36 @@ using Events = EventsArray<Event>;
/// To retrieve the value the getter uses a JSON key name that corresponds to
/// its own (getter's) name but written in snake_case. \p GetterName_ must be
/// in camelCase, no quotes (an identifier, not a literal).
-#define DEFINE_SIMPLE_EVENT(Name_, Base_, TypeId_, ValueType_, GetterName_) \
- class QUOTIENT_API Name_ : public Base_ { \
- public: \
- using content_type = ValueType_; \
- DEFINE_EVENT_TYPEID(TypeId_, Name_) \
- explicit Name_(const QJsonObject& obj) : Base_(TypeId, obj) {} \
- explicit Name_(const content_type& content) \
- : Name_(Base_::basicJson(TypeId, { { JsonKey, toJson(content) } })) \
- {} \
- auto GetterName_() const \
- { \
- return contentPart<content_type>(JsonKey); \
- } \
- static inline const auto JsonKey = toSnakeCase(#GetterName_##_ls); \
- }; \
- REGISTER_EVENT_TYPE(Name_) \
+#define DEFINE_SIMPLE_EVENT(Name_, Base_, TypeId_, ValueType_, GetterName_, \
+ JsonKey_) \
+ constexpr auto Name_##ContentKey = JsonKey_##_ls; \
+ class QUOTIENT_API Name_ \
+ : public EventTemplate< \
+ Name_, Base_, \
+ EventContent::SingleKeyValue<ValueType_, Name_##ContentKey>> { \
+ public: \
+ QUO_EVENT(Name_, TypeId_) \
+ using value_type = ValueType_; \
+ using EventTemplate::EventTemplate; \
+ QUO_CONTENT_GETTER_X(ValueType_, GetterName_, Name_##ContentKey) \
+ }; \
// End of macro
// === is<>(), eventCast<>() and switchOnType<>() ===
-template <class EventT>
+template <EventClass EventT>
inline bool is(const Event& e)
{
- return e.type() == typeId<EventT>();
-}
-
-inline bool isUnknown(const Event& e)
-{
- return e.type() == UnknownEventTypeId;
+ if constexpr (requires { EventT::MetaType; }) {
+ return &e.metaType() == &EventT::MetaType;
+ } else {
+ const auto* p = &e.metaType();
+ do {
+ if (p == &EventT::BaseMetaType)
+ return true;
+ } while ((p = p->baseType) != nullptr);
+ return false;
+ }
}
//! \brief Cast the event pointer down in a type-safe way
@@ -339,7 +550,7 @@ inline bool isUnknown(const Event& e)
//! can be either "dumb" (BaseEventT*) or "smart" (`event_ptr_tt<>`). This
//! overload doesn't affect the event ownership - if the original pointer owns
//! the event it must outlive the downcast pointer to keep it from dangling.
-template <class EventT, typename BasePtrT>
+template <EventClass EventT, typename BasePtrT>
inline auto eventCast(const BasePtrT& eptr)
-> decltype(static_cast<EventT*>(&*eptr))
{
@@ -362,7 +573,7 @@ inline auto eventCast(const BasePtrT& eptr)
//! after calling this overload; if it is a temporary, this normally
//! leads to the event getting deleted along with the end of
//! the temporary's lifetime.
-template <class EventT, typename BaseEventT>
+template <EventClass EventT, typename BaseEventT>
inline auto eventCast(event_ptr_tt<BaseEventT>&& eptr)
{
return eptr && is<std::decay_t<EventT>>(*eptr)
@@ -371,12 +582,15 @@ inline auto eventCast(event_ptr_tt<BaseEventT>&& eptr)
}
namespace _impl {
- template <typename FnT, class BaseT>
- concept Invocable_With_Downcast =
+ template <typename FnT, typename BaseT>
+ concept Invocable_With_Downcast = requires
+ {
+ requires EventClass<BaseT>;
std::is_base_of_v<BaseT, std::remove_cvref_t<fn_arg_t<FnT>>>;
+ };
}
-template <class BaseT, typename TailT>
+template <EventClass BaseT, typename TailT>
inline auto switchOnType(const BaseT& event, TailT&& tail)
{
if constexpr (std::is_invocable_v<TailT, BaseT>) {
@@ -391,7 +605,7 @@ inline auto switchOnType(const BaseT& event, TailT&& tail)
}
}
-template <class BaseT, typename FnT1, typename... FnTs>
+template <EventClass BaseT, typename FnT1, typename... FnTs>
inline auto switchOnType(const BaseT& event, FnT1&& fn1, FnTs&&... fns)
{
using event_type1 = fn_arg_t<FnT1>;
@@ -400,7 +614,7 @@ inline auto switchOnType(const BaseT& event, FnT1&& fn1, FnTs&&... fns)
return switchOnType(event, std::forward<FnTs>(fns)...);
}
-template <class BaseT, typename... FnTs>
+template <EventClass BaseT, typename... FnTs>
[[deprecated("The new name for visit() is switchOnType()")]] //
inline auto visit(const BaseT& event, FnTs&&... fns)
{
diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h
index 4c639efa..b4ac154c 100644
--- a/lib/events/eventloader.h
+++ b/lib/events/eventloader.h
@@ -6,40 +6,8 @@
#include "stateevent.h"
namespace Quotient {
-
-/*! Create an event with proper type from a JSON object
- *
- * Use this factory template to detect the type from the JSON object
- * contents (the detected event type should derive from the template
- * parameter type) and create an event object of that type.
- */
-template <typename BaseEventT>
-inline event_ptr_tt<BaseEventT> loadEvent(const QJsonObject& fullJson)
-{
- return doLoadEvent<BaseEventT>(fullJson, fullJson[TypeKeyL].toString());
-}
-
-//! \brief Create an event from a type string and content JSON
-//!
-//! Use this template to resolve the C++ type from the Matrix type string in
-//! \p matrixType and create an event of that type by passing all parameters
-//! to BaseEventT::basicJson().
-template <typename BaseEventT, typename... BasicJsonParamTs>
-inline event_ptr_tt<BaseEventT> loadEvent(
- const QString& matrixType, const BasicJsonParamTs&... basicJsonParams)
-{
- return doLoadEvent<BaseEventT>(
- BaseEventT::basicJson(matrixType, basicJsonParams...), matrixType);
+struct [[deprecated(
+ "This header is obsolete since libQuotient 0.7; include a header with"
+ " the respective event type definition instead")]] EventLoaderH;
+StateEventPtr eventLoaderH(EventLoaderH&);
}
-
-template <typename EventT>
-struct JsonConverter<event_ptr_tt<EventT>>
- : JsonObjectUnpacker<event_ptr_tt<EventT>> {
- using JsonObjectUnpacker<event_ptr_tt<EventT>>::load;
- static auto load(const QJsonObject& jo)
- {
- return loadEvent<EventT>(jo);
- }
-};
-
-} // namespace Quotient
diff --git a/lib/events/keyverificationevent.h b/lib/events/keyverificationevent.h
index 5b587522..0ffd8b2c 100644
--- a/lib/events/keyverificationevent.h
+++ b/lib/events/keyverificationevent.h
@@ -13,11 +13,9 @@ static constexpr auto SasV1Method = "m.sas.v1"_ls;
/// Typically sent as a to-device event.
class QUOTIENT_API KeyVerificationRequestEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.key.verification.request", KeyVerificationRequestEvent)
+ QUO_EVENT(KeyVerificationRequestEvent, "m.key.verification.request")
- explicit KeyVerificationRequestEvent(const QJsonObject& obj)
- : Event(TypeId, obj)
- {}
+ using Event::Event;
KeyVerificationRequestEvent(const QString& transactionId,
const QString& fromDevice,
const QStringList& methods,
@@ -45,15 +43,12 @@ public:
/// by the receiver.
QUO_CONTENT_GETTER(QDateTime, timestamp)
};
-REGISTER_EVENT_TYPE(KeyVerificationRequestEvent)
class QUOTIENT_API KeyVerificationReadyEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.key.verification.ready", KeyVerificationReadyEvent)
+ QUO_EVENT(KeyVerificationReadyEvent, "m.key.verification.ready")
- explicit KeyVerificationReadyEvent(const QJsonObject& obj)
- : Event(TypeId, obj)
- {}
+ using Event::Event;
KeyVerificationReadyEvent(const QString& transactionId,
const QString& fromDevice,
const QStringList& methods)
@@ -72,17 +67,13 @@ public:
/// The verification methods supported by the sender.
QUO_CONTENT_GETTER(QStringList, methods)
};
-REGISTER_EVENT_TYPE(KeyVerificationReadyEvent)
-
/// Begins a key verification process.
class QUOTIENT_API KeyVerificationStartEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.key.verification.start", KeyVerificationStartEvent)
+ QUO_EVENT(KeyVerificationStartEvent, "m.key.verification.start")
- explicit KeyVerificationStartEvent(const QJsonObject& obj)
- : Event(TypeId, obj)
- {}
+ using Event::Event;
KeyVerificationStartEvent(const QString& transactionId,
const QString& fromDevice)
: KeyVerificationStartEvent(
@@ -146,17 +137,14 @@ public:
return contentPart<QString>("short_authentification_string"_ls);
}
};
-REGISTER_EVENT_TYPE(KeyVerificationStartEvent)
/// Accepts a previously sent m.key.verification.start message.
/// Typically sent as a to-device event.
class QUOTIENT_API KeyVerificationAcceptEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.key.verification.accept", KeyVerificationAcceptEvent)
+ QUO_EVENT(KeyVerificationAcceptEvent, "m.key.verification.accept")
- explicit KeyVerificationAcceptEvent(const QJsonObject& obj)
- : Event(TypeId, obj)
- {}
+ using Event::Event;
KeyVerificationAcceptEvent(const QString& transactionId,
const QString& commitment)
: KeyVerificationAcceptEvent(basicJson(
@@ -199,15 +187,12 @@ public:
/// canonical JSON representation of the m.key.verification.start message.
QUO_CONTENT_GETTER(QString, commitment)
};
-REGISTER_EVENT_TYPE(KeyVerificationAcceptEvent)
class QUOTIENT_API KeyVerificationCancelEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.key.verification.cancel", KeyVerificationCancelEvent)
+ QUO_EVENT(KeyVerificationCancelEvent, "m.key.verification.cancel")
- explicit KeyVerificationCancelEvent(const QJsonObject& obj)
- : Event(TypeId, obj)
- {}
+ using Event::Event;
KeyVerificationCancelEvent(const QString& transactionId,
const QString& reason)
: KeyVerificationCancelEvent(
@@ -228,17 +213,14 @@ public:
/// The error code for why the process/request was cancelled by the user.
QUO_CONTENT_GETTER(QString, code)
};
-REGISTER_EVENT_TYPE(KeyVerificationCancelEvent)
/// Sends the ephemeral public key for a device to the partner device.
/// Typically sent as a to-device event.
class QUOTIENT_API KeyVerificationKeyEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.key.verification.key", KeyVerificationKeyEvent)
+ QUO_EVENT(KeyVerificationKeyEvent, "m.key.verification.key")
- explicit KeyVerificationKeyEvent(const QJsonObject& obj)
- : Event(TypeId, obj)
- {}
+ using Event::Event;
KeyVerificationKeyEvent(const QString& transactionId, const QString& key)
: KeyVerificationKeyEvent(
basicJson(TypeId, { { "transaction_id"_ls, transactionId },
@@ -251,16 +233,13 @@ public:
/// The device's ephemeral public key, encoded as unpadded base64.
QUO_CONTENT_GETTER(QString, key)
};
-REGISTER_EVENT_TYPE(KeyVerificationKeyEvent)
/// Sends the MAC of a device's key to the partner device.
class QUOTIENT_API KeyVerificationMacEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.key.verification.mac", KeyVerificationMacEvent)
+ QUO_EVENT(KeyVerificationMacEvent, "m.key.verification.mac")
- explicit KeyVerificationMacEvent(const QJsonObject& obj)
- : Event(TypeId, obj)
- {}
+ using Event::Event;
KeyVerificationMacEvent(const QString& transactionId, const QString& keys,
const QJsonObject& mac)
: KeyVerificationMacEvent(
@@ -280,15 +259,12 @@ public:
return contentPart<QHash<QString, QString>>("mac"_ls);
}
};
-REGISTER_EVENT_TYPE(KeyVerificationMacEvent)
class QUOTIENT_API KeyVerificationDoneEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.key.verification.done", KeyVerificationDoneEvent)
+ QUO_EVENT(KeyVerificationDoneEvent, "m.key.verification.done")
- explicit KeyVerificationDoneEvent(const QJsonObject& obj)
- : Event(TypeId, obj)
- {}
+ using Event::Event;
explicit KeyVerificationDoneEvent(const QString& transactionId)
: KeyVerificationDoneEvent(
basicJson(TypeId, { { "transaction_id"_ls, transactionId } }))
@@ -297,6 +273,4 @@ public:
/// The same transactionId as before
QUO_CONTENT_GETTER(QString, transactionId)
};
-REGISTER_EVENT_TYPE(KeyVerificationDoneEvent)
-
} // namespace Quotient
diff --git a/lib/events/reactionevent.h b/lib/events/reactionevent.h
index b3cb3ca7..8d873441 100644
--- a/lib/events/reactionevent.h
+++ b/lib/events/reactionevent.h
@@ -8,20 +8,7 @@
namespace Quotient {
-class QUOTIENT_API ReactionEvent : public RoomEvent {
-public:
- DEFINE_EVENT_TYPEID("m.reaction", ReactionEvent)
-
- explicit ReactionEvent(const EventRelation& value)
- : RoomEvent(typeId(), matrixTypeId(),
- { { QStringLiteral("m.relates_to"), toJson(value) } })
- {}
- explicit ReactionEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj) {}
- EventRelation relation() const
- {
- return contentPart<EventRelation>(RelatesToKey);
- }
-};
-REGISTER_EVENT_TYPE(ReactionEvent)
+DEFINE_SIMPLE_EVENT(ReactionEvent, RoomEvent, "m.reaction", EventRelation,
+ relation, "m.relates_to")
} // namespace Quotient
diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp
index 7f06d99f..d8f9fa0b 100644
--- a/lib/events/receiptevent.cpp
+++ b/lib/events/receiptevent.cpp
@@ -28,7 +28,7 @@ using namespace Quotient;
// map lookups are not used and vectors are massively faster. Same goes for
// de-/serialization of ReceiptsForEvent::receipts.
// (XXX: would this be generally preferred across CS API JSON maps?..)
-QJsonObject toJson(const EventsWithReceipts& ewrs)
+QJsonObject Quotient::toJson(const EventsWithReceipts& ewrs)
{
QJsonObject json;
for (const auto& e : ewrs) {
@@ -41,20 +41,16 @@ QJsonObject toJson(const EventsWithReceipts& ewrs)
return json;
}
-ReceiptEvent::ReceiptEvent(const EventsWithReceipts &ewrs)
- : Event(typeId(), matrixTypeId(), toJson(ewrs))
-{}
-
-EventsWithReceipts ReceiptEvent::eventsWithReceipts() const
+template<>
+EventsWithReceipts Quotient::fromJson(const QJsonObject& json)
{
EventsWithReceipts result;
- const auto& contents = contentJson();
- result.reserve(contents.size());
- for (auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt) {
+ result.reserve(json.size());
+ for (auto eventIt = json.begin(); eventIt != json.end(); ++eventIt) {
if (eventIt.key().isEmpty()) {
qCWarning(EPHEMERAL)
<< "ReceiptEvent has an empty event id, skipping";
- qCDebug(EPHEMERAL) << "ReceiptEvent content follows:\n" << contents;
+ qCDebug(EPHEMERAL) << "ReceiptEvent content follows:\n" << json;
continue;
}
const auto reads =
diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h
index 5e077e47..b87e00f6 100644
--- a/lib/events/receiptevent.h
+++ b/lib/events/receiptevent.h
@@ -19,13 +19,17 @@ struct ReceiptsForEvent {
};
using EventsWithReceipts = QVector<ReceiptsForEvent>;
-class QUOTIENT_API ReceiptEvent : public Event {
+template <>
+QUOTIENT_API EventsWithReceipts fromJson(const QJsonObject& json);
+QUOTIENT_API QJsonObject toJson(const EventsWithReceipts& ewrs);
+
+class QUOTIENT_API ReceiptEvent
+ : public EventTemplate<ReceiptEvent, Event, EventsWithReceipts> {
public:
- DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent)
- explicit ReceiptEvent(const EventsWithReceipts& ewrs);
- explicit ReceiptEvent(const QJsonObject& obj) : Event(typeId(), obj) {}
+ QUO_EVENT(ReceiptEvent, "m.receipt")
+ using EventTemplate::EventTemplate;
- EventsWithReceipts eventsWithReceipts() const;
+ [[deprecated("Use content() instead")]]
+ EventsWithReceipts eventsWithReceipts() const { return content(); }
};
-REGISTER_EVENT_TYPE(ReceiptEvent)
} // namespace Quotient
diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h
index 63617e54..a2e0b73b 100644
--- a/lib/events/redactionevent.h
+++ b/lib/events/redactionevent.h
@@ -8,10 +8,9 @@
namespace Quotient {
class QUOTIENT_API RedactionEvent : public RoomEvent {
public:
- DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent)
+ QUO_EVENT(RedactionEvent, "m.room.redaction")
- explicit RedactionEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj)
- {}
+ using RoomEvent::RoomEvent;
QString redactedEvent() const
{
@@ -19,5 +18,4 @@ public:
}
QUO_CONTENT_GETTER(QString, reason)
};
-REGISTER_EVENT_TYPE(RedactionEvent)
} // namespace Quotient
diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h
index af291696..1986f852 100644
--- a/lib/events/roomavatarevent.h
+++ b/lib/events/roomavatarevent.h
@@ -8,28 +8,16 @@
namespace Quotient {
class QUOTIENT_API RoomAvatarEvent
- : public StateEvent<EventContent::ImageContent> {
+ : public KeylessStateEventBase<RoomAvatarEvent,
+ EventContent::ImageContent> {
// It's a bit of an overkill to use a full-fledged ImageContent
// because in reality m.room.avatar usually only has a single URL,
// without a thumbnail. But The Spec says there be thumbnails, and
- // we follow The Spec.
+ // we follow The Spec (and ImageContent is very convenient to reuse here).
public:
- DEFINE_EVENT_TYPEID("m.room.avatar", RoomAvatarEvent)
- explicit RoomAvatarEvent(const QJsonObject& obj) : StateEvent(typeId(), obj)
- {}
- explicit RoomAvatarEvent(const EventContent::ImageContent& avatar)
- : StateEvent(typeId(), matrixTypeId(), QString(), avatar)
- {}
- // A replica of EventContent::ImageInfo constructor
- explicit RoomAvatarEvent(const QUrl& mxcUrl, qint64 fileSize = -1,
- QMimeType mimeType = {},
- const QSize& imageSize = {},
- const QString& originalFilename = {})
- : RoomAvatarEvent(EventContent::ImageContent {
- mxcUrl, fileSize, mimeType, imageSize, originalFilename })
- {}
+ QUO_EVENT(RoomAvatarEvent, "m.room.avatar")
+ using KeylessStateEventBase::KeylessStateEventBase;
QUrl url() const { return content().url(); }
};
-REGISTER_EVENT_TYPE(RoomAvatarEvent)
} // namespace Quotient
diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h
index 60ca68ac..c73bc92a 100644
--- a/lib/events/roomcanonicalaliasevent.h
+++ b/lib/events/roomcanonicalaliasevent.h
@@ -32,28 +32,13 @@ inline auto toJson(const EventContent::AliasesEventContent& c)
}
class QUOTIENT_API RoomCanonicalAliasEvent
- : public StateEvent<EventContent::AliasesEventContent> {
+ : public KeylessStateEventBase<RoomCanonicalAliasEvent,
+ EventContent::AliasesEventContent> {
public:
- DEFINE_EVENT_TYPEID("m.room.canonical_alias", RoomCanonicalAliasEvent)
-
- explicit RoomCanonicalAliasEvent(const QJsonObject& obj)
- : StateEvent(typeId(), obj)
- { }
-
- explicit RoomCanonicalAliasEvent(const QString& canonicalAlias,
- const QStringList& altAliases = {})
- : StateEvent(typeId(), matrixTypeId(), {},
- canonicalAlias, altAliases)
- { }
-
- explicit RoomCanonicalAliasEvent(QString&& canonicalAlias,
- QStringList&& altAliases = {})
- : StateEvent(typeId(), matrixTypeId(), {},
- std::move(canonicalAlias), std::move(altAliases))
- { }
+ QUO_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias")
+ using KeylessStateEventBase::KeylessStateEventBase;
QString alias() const { return content().canonicalAlias; }
QStringList altAliases() const { return content().altAliases; }
};
-REGISTER_EVENT_TYPE(RoomCanonicalAliasEvent)
} // namespace Quotient
diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h
index 989030ac..5968e187 100644
--- a/lib/events/roomcreateevent.h
+++ b/lib/events/roomcreateevent.h
@@ -7,13 +7,11 @@
#include "quotient_common.h"
namespace Quotient {
-class QUOTIENT_API RoomCreateEvent : public StateEventBase {
+class QUOTIENT_API RoomCreateEvent : public StateEvent {
public:
- DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent)
+ QUO_EVENT(RoomCreateEvent, "m.room.create")
- explicit RoomCreateEvent(const QJsonObject& obj)
- : StateEventBase(typeId(), obj)
- {}
+ using StateEvent::StateEvent;
struct Predecessor {
QString roomId;
@@ -26,5 +24,4 @@ public:
bool isUpgrade() const;
RoomType roomType() const;
};
-REGISTER_EVENT_TYPE(RoomCreateEvent)
} // namespace Quotient
diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp
index e695e0ec..e98cb591 100644
--- a/lib/events/roomevent.cpp
+++ b/lib/events/roomevent.cpp
@@ -8,16 +8,11 @@
using namespace Quotient;
-RoomEvent::RoomEvent(Type type, event_mtype_t matrixType,
- const QJsonObject& contentJson)
- : Event(type, matrixType, contentJson)
-{}
-
-RoomEvent::RoomEvent(Type type, const QJsonObject& json) : Event(type, json)
+RoomEvent::RoomEvent(const QJsonObject& json) : Event(json)
{
if (const auto redaction = unsignedPart<QJsonObject>(RedactedCauseKeyL);
!redaction.isEmpty())
- _redactedBecause = makeEvent<RedactionEvent>(redaction);
+ _redactedBecause = loadEvent<RedactionEvent>(redaction);
}
RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job
@@ -101,28 +96,6 @@ void RoomEvent::dumpTo(QDebug dbg) const
dbg << " (made at " << originTimestamp().toString(Qt::ISODate) << ')';
}
-QJsonObject CallEventBase::basicJson(const QString& matrixType,
- const QString& callId, int version,
- QJsonObject contentJson)
-{
- contentJson.insert(QStringLiteral("call_id"), callId);
- contentJson.insert(QStringLiteral("version"), version);
- return RoomEvent::basicJson(matrixType, contentJson);
-}
-
-CallEventBase::CallEventBase(Type type, event_mtype_t matrixType,
- const QString& callId, int version,
- const QJsonObject& contentJson)
- : RoomEvent(type, basicJson(matrixType, callId, version, contentJson))
-{}
-
-CallEventBase::CallEventBase(Type type, const QJsonObject& json)
- : RoomEvent(type, json)
-{
- if (callId().isEmpty())
- qCWarning(EVENTS) << id() << "is a call event with an empty call id";
-}
-
#ifdef Quotient_E2EE_ENABLED
void RoomEvent::setOriginalEvent(event_ptr_tt<RoomEvent>&& originalEvent)
{
diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h
index 9461340b..203434f6 100644
--- a/lib/events/roomevent.h
+++ b/lib/events/roomevent.h
@@ -10,17 +10,13 @@
namespace Quotient {
class RedactionEvent;
-/** This class corresponds to m.room.* events */
+// That check could look into Event and find most stuff already deleted...
+// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
class QUOTIENT_API RoomEvent : public Event {
public:
- static inline EventFactory<RoomEvent> factory { "RoomEvent" };
+ QUO_BASE_EVENT(RoomEvent, {}, Event::BaseMetaType)
- // RedactionEvent is an incomplete type here so we cannot inline
- // constructors and destructors and we cannot use 'using'.
- RoomEvent(Type type, event_mtype_t matrixType,
- const QJsonObject& contentJson = {});
- RoomEvent(Type type, const QJsonObject& json);
- ~RoomEvent() override;
+ ~RoomEvent() override; // Don't inline this - see the private section
QString id() const;
QDateTime originTimestamp() const;
@@ -67,9 +63,12 @@ public:
#endif
protected:
+ explicit RoomEvent(const QJsonObject& json);
void dumpTo(QDebug dbg) const override;
private:
+ // RedactionEvent is an incomplete type here so we cannot inline
+ // constructors using it and also destructors (with 'using', in particular).
event_ptr_tt<RedactionEvent> _redactedBecause;
#ifdef Quotient_E2EE_ENABLED
@@ -80,30 +79,6 @@ using RoomEventPtr = event_ptr_tt<RoomEvent>;
using RoomEvents = EventsArray<RoomEvent>;
using RoomEventsRange = Range<RoomEvents>;
-template <>
-inline EventPtr doLoadEvent(const QJsonObject& json, const QString& matrixType)
-{
- if (matrixType == "m.room.encrypted")
- return RoomEvent::factory.loadEvent(json, matrixType);
- return Event::factory.loadEvent(json, matrixType);
-}
-
-class QUOTIENT_API CallEventBase : public RoomEvent {
-public:
- CallEventBase(Type type, event_mtype_t matrixType, const QString& callId,
- int version, const QJsonObject& contentJson = {});
- CallEventBase(Type type, const QJsonObject& json);
- ~CallEventBase() override = default;
- bool isCallEvent() const override { return true; }
-
- QUO_CONTENT_GETTER(QString, callId)
- QUO_CONTENT_GETTER(int, version)
-
-protected:
- static QJsonObject basicJson(const QString& matrixType,
- const QString& callId, int version,
- QJsonObject contentJson = {});
-};
} // namespace Quotient
Q_DECLARE_METATYPE(Quotient::RoomEvent*)
Q_DECLARE_METATYPE(const Quotient::RoomEvent*)
diff --git a/lib/events/roomkeyevent.cpp b/lib/events/roomkeyevent.cpp
deleted file mode 100644
index 3a8601d1..00000000
--- a/lib/events/roomkeyevent.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#include "roomkeyevent.h"
-
-using namespace Quotient;
-
-RoomKeyEvent::RoomKeyEvent(const QJsonObject &obj) : Event(TypeId, obj)
-{
- if (roomId().isEmpty())
- qCWarning(E2EE) << "Room key event has empty room id";
-}
-
-RoomKeyEvent::RoomKeyEvent(const QString& algorithm, const QString& roomId,
- const QString& sessionId, const QString& sessionKey)
- : Event(TypeId, basicJson(TypeId, {
- { "algorithm", algorithm },
- { "room_id", roomId },
- { "session_id", sessionId },
- { "session_key", sessionKey },
- }))
-{}
diff --git a/lib/events/roomkeyevent.h b/lib/events/roomkeyevent.h
index 0dfdf383..dad5df8b 100644
--- a/lib/events/roomkeyevent.h
+++ b/lib/events/roomkeyevent.h
@@ -9,11 +9,18 @@ namespace Quotient {
class QUOTIENT_API RoomKeyEvent : public Event
{
public:
- DEFINE_EVENT_TYPEID("m.room_key", RoomKeyEvent)
+ QUO_EVENT(RoomKeyEvent, "m.room_key")
- explicit RoomKeyEvent(const QJsonObject& obj);
+ using Event::Event;
explicit RoomKeyEvent(const QString& algorithm, const QString& roomId,
- const QString& sessionId, const QString& sessionKey);
+ const QString& sessionId, const QString& sessionKey)
+ : Event(basicJson(TypeId, {
+ { "algorithm", algorithm },
+ { "room_id", roomId },
+ { "session_id", sessionId },
+ { "session_key", sessionKey },
+ }))
+ {}
QUO_CONTENT_GETTER(QString, algorithm)
QUO_CONTENT_GETTER(QString, roomId)
@@ -23,5 +30,4 @@ public:
return contentPart<QString>("session_key"_ls).toLatin1();
}
};
-REGISTER_EVENT_TYPE(RoomKeyEvent)
} // namespace Quotient
diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp
index 953ff8ae..4e7eae1b 100644
--- a/lib/events/roommemberevent.cpp
+++ b/lib/events/roommemberevent.cpp
@@ -3,8 +3,7 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "roommemberevent.h"
-
-#include <QtCore/QtAlgorithms>
+#include "logging.h"
namespace Quotient {
template <>
diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h
index dd33ea6b..9f063136 100644
--- a/lib/events/roommemberevent.h
+++ b/lib/events/roommemberevent.h
@@ -28,32 +28,16 @@ public:
using MembershipType [[deprecated("Use Membership instead")]] = Membership;
-class QUOTIENT_API RoomMemberEvent : public StateEvent<MemberEventContent> {
+class QUOTIENT_API RoomMemberEvent
+ : public KeyedStateEventBase<RoomMemberEvent, MemberEventContent> {
Q_GADGET
public:
- DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent)
+ QUO_EVENT(RoomMemberEvent, "m.room.member")
using MembershipType
[[deprecated("Use Quotient::Membership instead")]] = Membership;
- explicit RoomMemberEvent(const QJsonObject& obj) : StateEvent(typeId(), obj)
- {}
- RoomMemberEvent(const QString& userId, MemberEventContent&& content)
- : StateEvent(typeId(), matrixTypeId(), userId, std::move(content))
- {}
-
- //! \brief A special constructor to create unknown RoomMemberEvents
- //!
- //! This is needed in order to use RoomMemberEvent as a "base event class"
- //! in cases like GetMembersByRoomJob when RoomMemberEvents (rather than
- //! RoomEvents or StateEvents) are resolved from JSON. For such cases
- //! loadEvent\<> requires an underlying class to have a specialisation of
- //! EventFactory\<> and be constructible with unknownTypeId() instead of
- //! its genuine id. Don't use directly.
- //! \sa EventFactory, loadEvent, GetMembersByRoomJob
- RoomMemberEvent(Type type, const QJsonObject& fullJson)
- : StateEvent(type, fullJson)
- {}
+ using KeyedStateEventBase::KeyedStateEventBase;
Membership membership() const { return content().membership; }
QString userId() const { return stateKey(); }
@@ -79,14 +63,4 @@ public:
bool isRename() const;
bool isAvatarUpdate() const;
};
-
-template <>
-inline event_ptr_tt<RoomMemberEvent>
-doLoadEvent<RoomMemberEvent>(const QJsonObject& json, const QString& matrixType)
-{
- if (matrixType == QLatin1String(RoomMemberEvent::matrixTypeId()))
- return makeEvent<RoomMemberEvent>(json);
- return makeEvent<RoomMemberEvent>(unknownEventTypeId(), json);
-}
-REGISTER_EVENT_TYPE(RoomMemberEvent)
} // namespace Quotient
diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp
index 2a6ae93c..df4840b3 100644
--- a/lib/events/roommessageevent.cpp
+++ b/lib/events/roommessageevent.cpp
@@ -128,8 +128,8 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody,
RoomMessageEvent::RoomMessageEvent(const QString& plainBody,
const QString& jsonMsgType,
TypedBase* content)
- : RoomEvent(typeId(), matrixTypeId(),
- assembleContentJson(plainBody, jsonMsgType, content))
+ : RoomEvent(
+ basicJson(TypeId, assembleContentJson(plainBody, jsonMsgType, content)))
, _content(content)
{}
@@ -175,7 +175,7 @@ RoomMessageEvent::RoomMessageEvent(const QString& plainBody,
#endif
RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj)
- : RoomEvent(typeId(), obj), _content(nullptr)
+ : RoomEvent(obj), _content(nullptr)
{
if (isRedacted())
return;
diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h
index 6968ad70..889fc4dc 100644
--- a/lib/events/roommessageevent.h
+++ b/lib/events/roommessageevent.h
@@ -20,7 +20,7 @@ namespace MessageEventContent = EventContent; // Back-compatibility
class QUOTIENT_API RoomMessageEvent : public RoomEvent {
Q_GADGET
public:
- DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent)
+ QUO_EVENT(RoomMessageEvent, "m.room.message")
enum class MsgType {
Text,
@@ -94,7 +94,7 @@ private:
Q_ENUM(MsgType)
};
-REGISTER_EVENT_TYPE(RoomMessageEvent)
+
using MessageEventType = RoomMessageEvent::MsgType;
namespace EventContent {
diff --git a/lib/events/roompowerlevelsevent.h b/lib/events/roompowerlevelsevent.h
index a1638a27..6150980a 100644
--- a/lib/events/roompowerlevelsevent.h
+++ b/lib/events/roompowerlevelsevent.h
@@ -31,16 +31,11 @@ struct QUOTIENT_API PowerLevelsEventContent {
};
class QUOTIENT_API RoomPowerLevelsEvent
- : public StateEvent<PowerLevelsEventContent> {
+ : public KeylessStateEventBase<RoomPowerLevelsEvent, PowerLevelsEventContent> {
public:
- DEFINE_EVENT_TYPEID("m.room.power_levels", RoomPowerLevelsEvent)
+ QUO_EVENT(RoomPowerLevelsEvent, "m.room.power_levels")
- explicit RoomPowerLevelsEvent(PowerLevelsEventContent&& content)
- : StateEvent(typeId(), matrixTypeId(), QString(), std::move(content))
- {}
- explicit RoomPowerLevelsEvent(const QJsonObject& obj)
- : StateEvent(typeId(), obj)
- {}
+ using KeylessStateEventBase::KeylessStateEventBase;
int invite() const { return content().invite; }
int kick() const { return content().kick; }
@@ -61,5 +56,4 @@ public:
int powerLevelForState(const QString& eventId) const;
int powerLevelForUser(const QString& userId) const;
};
-REGISTER_EVENT_TYPE(RoomPowerLevelsEvent)
} // namespace Quotient
diff --git a/lib/events/roomtombstoneevent.h b/lib/events/roomtombstoneevent.h
index 15d26923..c85b4dfd 100644
--- a/lib/events/roomtombstoneevent.h
+++ b/lib/events/roomtombstoneevent.h
@@ -6,16 +6,13 @@
#include "stateevent.h"
namespace Quotient {
-class QUOTIENT_API RoomTombstoneEvent : public StateEventBase {
+class QUOTIENT_API RoomTombstoneEvent : public StateEvent {
public:
- DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent)
+ QUO_EVENT(RoomTombstoneEvent, "m.room.tombstone")
- explicit RoomTombstoneEvent(const QJsonObject& obj)
- : StateEventBase(typeId(), obj)
- {}
+ using StateEvent::StateEvent;
QString serverMessage() const;
QString successorRoomId() const;
};
-REGISTER_EVENT_TYPE(RoomTombstoneEvent)
} // namespace Quotient
diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h
index a8eaab56..2a0d3817 100644
--- a/lib/events/simplestateevents.h
+++ b/lib/events/simplestateevents.h
@@ -7,26 +7,18 @@
#include "single_key_value.h"
namespace Quotient {
-#define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _ValueType, _ContentKey) \
- constexpr auto _Name##Key = #_ContentKey##_ls; \
- class QUOTIENT_API _Name \
- : public StateEvent< \
- EventContent::SingleKeyValue<_ValueType, &_Name##Key>> { \
- public: \
- using value_type = _ValueType; \
- DEFINE_EVENT_TYPEID(_TypeId, _Name) \
- template <typename T> \
- explicit _Name(T&& value) \
- : StateEvent(TypeId, matrixTypeId(), QString(), \
- std::forward<T>(value)) \
- {} \
- explicit _Name(QJsonObject obj) \
- : StateEvent(TypeId, std::move(obj)) \
- {} \
- auto _ContentKey() const { return content().value; } \
- }; \
- REGISTER_EVENT_TYPE(_Name) \
- // End of macro
+#define DEFINE_SIMPLE_STATE_EVENT(Name_, TypeId_, ValueType_, ContentKey_) \
+ constexpr auto Name_##Key = #ContentKey_##_ls; \
+ class QUOTIENT_API Name_ \
+ : public KeylessStateEventBase< \
+ Name_, EventContent::SingleKeyValue<ValueType_, Name_##Key>> { \
+ public: \
+ using value_type = ValueType_; \
+ QUO_EVENT(Name_, TypeId_) \
+ using KeylessStateEventBase::KeylessStateEventBase; \
+ auto ContentKey_() const { return content().value; } \
+ }; \
+// End of macro
DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name)
DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic)
@@ -35,13 +27,14 @@ DEFINE_SIMPLE_STATE_EVENT(RoomPinnedEvent, "m.room.pinned_messages",
constexpr auto RoomAliasesEventKey = "aliases"_ls;
class QUOTIENT_API RoomAliasesEvent
- : public StateEvent<
- EventContent::SingleKeyValue<QStringList, &RoomAliasesEventKey>> {
+ : public KeyedStateEventBase<
+ RoomAliasesEvent,
+ EventContent::SingleKeyValue<QStringList, RoomAliasesEventKey>>
+{
public:
- DEFINE_EVENT_TYPEID("m.room.aliases", RoomAliasesEvent)
- explicit RoomAliasesEvent(const QJsonObject& obj)
- : StateEvent(typeId(), obj)
- {}
+ QUO_EVENT(RoomAliasesEvent, "m.room.aliases")
+ using KeyedStateEventBase::KeyedStateEventBase;
+
Q_DECL_DEPRECATED_X(
"m.room.aliases events are deprecated by the Matrix spec; use"
" RoomCanonicalAliasEvent::altAliases() to get non-authoritative aliases")
diff --git a/lib/events/single_key_value.h b/lib/events/single_key_value.h
index 75ca8cd2..ca2bd331 100644
--- a/lib/events/single_key_value.h
+++ b/lib/events/single_key_value.h
@@ -5,13 +5,22 @@
namespace Quotient {
namespace EventContent {
- template <typename T, const QLatin1String* KeyStr>
+ template <typename T, const QLatin1String& KeyStr>
struct SingleKeyValue {
+ // NOLINTBEGIN(google-explicit-constructor): that check should learn
+ // about explicit(false)
+ QUO_IMPLICIT SingleKeyValue(const T& v = {})
+ : value { v }
+ {}
+ QUO_IMPLICIT SingleKeyValue(T&& v)
+ : value { std::move(v) }
+ {}
+ // NOLINTEND(google-explicit-constructor)
T value;
};
} // namespace EventContent
-template <typename ValueT, const QLatin1String* KeyStr>
+template <typename ValueT, const QLatin1String& KeyStr>
struct JsonConverter<EventContent::SingleKeyValue<ValueT, KeyStr>> {
using content_type = EventContent::SingleKeyValue<ValueT, KeyStr>;
static content_type load(const QJsonValue& jv)
@@ -22,6 +31,6 @@ struct JsonConverter<EventContent::SingleKeyValue<ValueT, KeyStr>> {
{
return { { JsonKey, toJson(c.value) } };
}
- static inline const auto JsonKey = toSnakeCase(*KeyStr);
+ static inline const auto JsonKey = toSnakeCase(KeyStr);
};
} // namespace Quotient
diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp
index 1df24df0..72ecd5ad 100644
--- a/lib/events/stateevent.cpp
+++ b/lib/events/stateevent.cpp
@@ -2,35 +2,33 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "stateevent.h"
+#include "logging.h"
using namespace Quotient;
-StateEventBase::StateEventBase(Type type, const QJsonObject& json)
- : RoomEvent(json.contains(StateKeyKeyL) ? type : UnknownEventTypeId, json)
+StateEvent::StateEvent(const QJsonObject& json)
+ : RoomEvent(json)
{
- if (Event::type() == UnknownEventTypeId && !json.contains(StateKeyKeyL))
- qWarning(EVENTS) << "Attempt to create a state event with no stateKey -"
- "forcing the event type to unknown to avoid damage";
+ Q_ASSERT_X(json.contains(StateKeyKeyL), __FUNCTION__,
+ "Attempt to create a state event without state key");
}
-StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType,
- const QString& stateKey,
+StateEvent::StateEvent(Event::Type type, const QString& stateKey,
const QJsonObject& contentJson)
- : RoomEvent(type, basicJson(type, stateKey, contentJson))
+ : RoomEvent(basicJson(type, stateKey, contentJson))
{}
-bool StateEventBase::repeatsState() const
+bool StateEvent::repeatsState() const
{
- const auto prevContentJson = unsignedPart<QJsonObject>(PrevContentKeyL);
- return fullJson().value(ContentKeyL) == prevContentJson;
+ return contentJson() == unsignedPart<QJsonObject>(PrevContentKeyL);
}
-QString StateEventBase::replacedState() const
+QString StateEvent::replacedState() const
{
return unsignedPart<QString>("replaces_state"_ls);
}
-void StateEventBase::dumpTo(QDebug dbg) const
+void StateEvent::dumpTo(QDebug dbg) const
{
if (!stateKey().isEmpty())
dbg << '<' << stateKey() << "> ";
diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h
index 9f1d7118..992ec2e2 100644
--- a/lib/events/stateevent.h
+++ b/lib/events/stateevent.h
@@ -7,15 +7,25 @@
namespace Quotient {
-class QUOTIENT_API StateEventBase : public RoomEvent {
+class QUOTIENT_API StateEvent : public RoomEvent {
public:
- static inline EventFactory<StateEventBase> factory { "StateEvent" };
+ QUO_BASE_EVENT(StateEvent, "json.contains('state_key')"_ls,
+ RoomEvent::BaseMetaType)
+ static bool isValid(const QJsonObject& fullJson)
+ {
+ return fullJson.contains(StateKeyKeyL);
+ }
+
+ //! \brief Static setting of whether a given even type uses state keys
+ //!
+ //! Most event types don't use a state key; overriding this to `true`
+ //! for a given type changes the calls across Quotient to include state key
+ //! in their signatures; otherwise, state key is still accessible but
+ //! constructors and calls in, e.g., RoomStateView don't include it.
+ static constexpr auto needsStateKey = false;
- StateEventBase(Type type, const QJsonObject& json);
- StateEventBase(Type type, event_mtype_t matrixType,
- const QString& stateKey = {},
- const QJsonObject& contentJson = {});
- ~StateEventBase() override = default;
+ explicit StateEvent(Type type, const QString& stateKey = {},
+ const QJsonObject& contentJson = {});
//! Make a minimal correct Matrix state event JSON
static QJsonObject basicJson(const QString& matrixTypeId,
@@ -27,43 +37,24 @@ public:
{ ContentKey, contentJson } };
}
- bool isStateEvent() const override { return true; }
QString replacedState() const;
- void dumpTo(QDebug dbg) const override;
-
virtual bool repeatsState() const;
+
+protected:
+ explicit StateEvent(const QJsonObject& json);
+ void dumpTo(QDebug dbg) const override;
};
-using StateEventPtr = event_ptr_tt<StateEventBase>;
-using StateEvents = EventsArray<StateEventBase>;
+using StateEventBase
+ [[deprecated("StateEventBase is StateEvent now")]] = StateEvent;
+using StateEventPtr = event_ptr_tt<StateEvent>;
+using StateEvents = EventsArray<StateEvent>;
-[[deprecated("Use StateEventBase::basicJson() instead")]]
+[[deprecated("Use StateEvent::basicJson() instead")]]
inline QJsonObject basicStateEventJson(const QString& matrixTypeId,
const QJsonObject& content,
const QString& stateKey = {})
{
- return StateEventBase::basicJson(matrixTypeId, stateKey, content);
-}
-
-//! \brief Override RoomEvent factory with that from StateEventBase if JSON has
-//! stateKey
-//!
-//! This means in particular that an event with a type known to RoomEvent but
-//! having stateKey set (even to an empty value) will be treated as a state
-//! event and most likely end up as unknown (consider, e.g., m.room.message
-//! that has stateKey set).
-template <>
-inline RoomEventPtr doLoadEvent(const QJsonObject& json,
- const QString& matrixType)
-{
- if (json.contains(StateKeyKeyL))
- return StateEventBase::factory.loadEvent(json, matrixType);
- return RoomEvent::factory.loadEvent(json, matrixType);
-}
-
-template <>
-inline bool is<StateEventBase>(const Event& e)
-{
- return e.isStateEvent();
+ return StateEvent::basicJson(matrixTypeId, stateKey, content);
}
/**
@@ -74,64 +65,87 @@ inline bool is<StateEventBase>(const Event& e)
*/
using StateEventKey = std::pair<QString, QString>;
-template <typename ContentT>
-struct Prev {
- template <typename... ContentParamTs>
- explicit Prev(const QJsonObject& unsignedJson,
- ContentParamTs&&... contentParams)
- : senderId(unsignedJson.value("prev_sender"_ls).toString())
- , content(fromJson<ContentT>(unsignedJson.value(PrevContentKeyL)),
- std::forward<ContentParamTs>(contentParams)...)
- {}
-
- QString senderId;
- ContentT content;
-};
-
-template <typename ContentT>
-class StateEvent : public StateEventBase {
+template <typename EventT, typename ContentT>
+class EventTemplate<EventT, StateEvent, ContentT>
+ : public StateEvent {
public:
using content_type = ContentT;
+ struct Prev {
+ explicit Prev() = default;
+ explicit Prev(const QJsonObject& unsignedJson)
+ : senderId(fromJson<QString>(unsignedJson["prev_sender"_ls]))
+ , content(
+ fromJson<Omittable<ContentT>>(unsignedJson[PrevContentKeyL]))
+ {}
+
+ QString senderId;
+ Omittable<ContentT> content;
+ };
+
+ explicit EventTemplate(const QJsonObject& fullJson)
+ : StateEvent(fullJson)
+ , _content(fromJson<ContentT>(Event::contentJson()))
+ , _prev(unsignedJson())
+ {}
template <typename... ContentParamTs>
- explicit StateEvent(Type type, const QJsonObject& fullJson,
- ContentParamTs&&... contentParams)
- : StateEventBase(type, fullJson)
- , _content(fromJson<ContentT>(contentJson()),
- std::forward<ContentParamTs>(contentParams)...)
- {
- const auto& unsignedData = unsignedJson();
- if (unsignedData.contains(PrevContentKeyL))
- _prev = std::make_unique<Prev<ContentT>>(
- unsignedData, std::forward<ContentParamTs>(contentParams)...);
- }
- template <typename... ContentParamTs>
- explicit StateEvent(Type type, event_mtype_t matrixType,
- const QString& stateKey,
- ContentParamTs&&... contentParams)
- : StateEventBase(type, matrixType, stateKey)
- , _content{std::forward<ContentParamTs>(contentParams)...}
+ explicit EventTemplate(const QString& stateKey,
+ ContentParamTs&&... contentParams)
+ : StateEvent(EventT::TypeId, stateKey)
+ , _content { std::forward<ContentParamTs>(contentParams)... }
{
editJson().insert(ContentKey, toJson(_content));
}
const ContentT& content() const { return _content; }
+
template <typename VisitorT>
void editContent(VisitorT&& visitor)
{
visitor(_content);
editJson()[ContentKeyL] = toJson(_content);
}
- const ContentT* prevContent() const
- {
- return _prev ? &_prev->content : nullptr;
- }
- QString prevSenderId() const { return _prev ? _prev->senderId : QString(); }
+ const Omittable<ContentT>& prevContent() const { return _prev.content; }
+ QString prevSenderId() const { return _prev.senderId; }
private:
ContentT _content;
- std::unique_ptr<Prev<ContentT>> _prev;
+ Prev _prev;
+};
+
+template <typename EventT, typename ContentT>
+class KeyedStateEventBase
+ : public EventTemplate<EventT, StateEvent, ContentT> {
+public:
+ static constexpr auto needsStateKey = true;
+
+ using EventTemplate<EventT, StateEvent, ContentT>::EventTemplate;
+};
+
+template <typename EvT>
+concept Keyed_State_Event = EvT::needsStateKey;
+
+template <typename EventT, typename ContentT>
+class KeylessStateEventBase
+ : public EventTemplate<EventT, StateEvent, ContentT> {
+private:
+ using base_type = EventTemplate<EventT, StateEvent, ContentT>;
+
+public:
+ template <typename... ContentParamTs>
+ explicit KeylessStateEventBase(ContentParamTs&&... contentParams)
+ : base_type(QString(), std::forward<ContentParamTs>(contentParams)...)
+ {}
+
+protected:
+ explicit KeylessStateEventBase(const QJsonObject& fullJson)
+ : base_type(fullJson)
+ {}
};
+
+template <typename EvT>
+concept Keyless_State_Event = !EvT::needsStateKey;
+
} // namespace Quotient
-Q_DECLARE_METATYPE(Quotient::StateEventBase*)
-Q_DECLARE_METATYPE(const Quotient::StateEventBase*)
+Q_DECLARE_METATYPE(Quotient::StateEvent*)
+Q_DECLARE_METATYPE(const Quotient::StateEvent*)
diff --git a/lib/events/stickerevent.h b/lib/events/stickerevent.h
index e378422d..67905481 100644
--- a/lib/events/stickerevent.h
+++ b/lib/events/stickerevent.h
@@ -14,7 +14,7 @@ namespace Quotient {
class QUOTIENT_API StickerEvent : public RoomEvent
{
public:
- DEFINE_EVENT_TYPEID("m.sticker", StickerEvent)
+ QUO_EVENT(StickerEvent, "m.sticker")
explicit StickerEvent(const QJsonObject& obj)
: RoomEvent(TypeId, obj)
@@ -45,5 +45,4 @@ public:
private:
EventContent::ImageContent m_imageContent;
};
-REGISTER_EVENT_TYPE(StickerEvent)
} // namespace Quotient
diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp
deleted file mode 100644
index 7e5d7ee6..00000000
--- a/lib/events/typingevent.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Kitsune Ral <Kitsune-Ral@users.sf.net>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#include "typingevent.h"
-
-using namespace Quotient;
-
-QStringList TypingEvent::users() const
-{
- return contentPart<QStringList>("user_ids"_ls);
-}
diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h
index 522f7e42..b56475af 100644
--- a/lib/events/typingevent.h
+++ b/lib/events/typingevent.h
@@ -6,13 +6,5 @@
#include "event.h"
namespace Quotient {
-class QUOTIENT_API TypingEvent : public Event {
-public:
- DEFINE_EVENT_TYPEID("m.typing", TypingEvent)
-
- explicit TypingEvent(const QJsonObject& obj) : Event(typeId(), obj) {}
-
- QStringList users() const;
-};
-REGISTER_EVENT_TYPE(TypingEvent)
+DEFINE_SIMPLE_EVENT(TypingEvent, Event, "m.typing", QStringList, users, "user_ids")
} // namespace Quotient
diff --git a/lib/expected.h b/lib/expected.h
index 7b9e7f1d..81e186ea 100644
--- a/lib/expected.h
+++ b/lib/expected.h
@@ -21,11 +21,12 @@ public:
using error_type = E;
Expected() = default;
- explicit Expected(const Expected&) = default;
- explicit Expected(Expected&&) noexcept = default;
+ Expected(const Expected&) = default;
+ Expected(Expected&&) noexcept = default;
+ ~Expected() = default;
template <typename X, typename = enable_if_constructible_t<X>>
- Expected(X&& x)
+ QUO_IMPLICIT Expected(X&& x) // NOLINT(google-explicit-constructor)
: data(std::forward<X>(x))
{}
diff --git a/lib/room.cpp b/lib/room.cpp
index ebc6e6af..24e348e3 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -26,33 +26,30 @@
#include "csapi/inviting.h"
#include "csapi/kicking.h"
#include "csapi/leaving.h"
+#include "csapi/read_markers.h"
#include "csapi/receipts.h"
#include "csapi/redaction.h"
#include "csapi/room_send.h"
#include "csapi/room_state.h"
#include "csapi/room_upgrades.h"
#include "csapi/rooms.h"
-#include "csapi/read_markers.h"
#include "csapi/tags.h"
-#include "events/callanswerevent.h"
-#include "events/callcandidatesevent.h"
-#include "events/callhangupevent.h"
-#include "events/callinviteevent.h"
+#include "events/callevents.h"
#include "events/encryptionevent.h"
#include "events/reactionevent.h"
#include "events/receiptevent.h"
#include "events/redactionevent.h"
#include "events/roomavatarevent.h"
+#include "events/roomcanonicalaliasevent.h"
#include "events/roomcreateevent.h"
#include "events/roommemberevent.h"
+#include "events/roompowerlevelsevent.h"
#include "events/roomtombstoneevent.h"
#include "events/simplestateevents.h"
#include "events/typingevent.h"
-#include "events/roompowerlevelsevent.h"
#include "jobs/downloadfilejob.h"
#include "jobs/mediathumbnailjob.h"
-#include "events/roomcanonicalaliasevent.h"
#include <QtCore/QDir>
#include <QtCore/QHash>
@@ -211,7 +208,7 @@ public:
void getPreviousContent(int limit = 10, const QString &filter = {});
- const StateEventBase* getCurrentState(const StateEventKey& evtKey) const
+ const StateEvent* getCurrentState(const StateEventKey& evtKey) const
{
const auto* evt = currentState.value(evtKey, nullptr);
if (!evt) {
@@ -219,9 +216,8 @@ public:
// In the absence of a real event, make a stub as-if an event
// with empty content has been received. Event classes should be
// prepared for empty/invalid/malicious content anyway.
- stubbedState.emplace(evtKey,
- loadEvent<StateEventBase>(evtKey.first,
- evtKey.second));
+ stubbedState.emplace(
+ evtKey, loadEvent<StateEvent>(evtKey.first, evtKey.second));
qCDebug(STATE) << "A new stub event created for key {"
<< evtKey.first << evtKey.second << "}";
qCDebug(STATE) << "Stubbed state size:" << stubbedState.size();
@@ -597,7 +593,7 @@ bool Room::allHistoryLoaded() const
QString Room::name() const
{
- return currentState().queryOr(&RoomNameEvent::name, QString());
+ return currentState().content<RoomNameEvent>().value;
}
QStringList Room::aliases() const
@@ -613,8 +609,7 @@ QStringList Room::aliases() const
QStringList Room::altAliases() const
{
- return currentState().queryOr(&RoomCanonicalAliasEvent::altAliases,
- QStringList());
+ return currentState().content<RoomCanonicalAliasEvent>().altAliases;
}
QString Room::canonicalAlias() const
@@ -1353,8 +1348,8 @@ void Room::setTags(TagsMap newTags, ActionScope applyOn)
d->setTags(move(newTags));
connection()->callApi<SetAccountDataPerRoomJob>(
- localUser()->id(), id(), TagEvent::matrixTypeId(),
- TagEvent(d->tags).contentJson());
+ localUser()->id(), id(), TagEvent::TypeId,
+ Quotient::toJson(TagEvent::content_type { d->tags }));
if (propagate) {
for (auto* r = this; (r = r->successor(joinStates));)
@@ -1569,8 +1564,8 @@ bool Room::usesEncryption() const
.isEmpty();
}
-const StateEventBase* Room::getCurrentState(const QString& evtType,
- const QString& stateKey) const
+const StateEvent* Room::getCurrentState(const QString& evtType,
+ const QString& stateKey) const
{
return d->getCurrentState({ evtType, stateKey });
}
@@ -2283,7 +2278,7 @@ QString Room::postJson(const QString& matrixType,
return d->sendEvent(loadEvent<RoomEvent>(matrixType, eventContent));
}
-SetRoomStateWithKeyJob* Room::setState(const StateEventBase& evt)
+SetRoomStateWithKeyJob* Room::setState(const StateEvent& evt)
{
return setState(evt.matrixType(), evt.stateKey(), evt.contentJson());
}
@@ -2321,7 +2316,7 @@ void Room::setTopic(const QString& newTopic)
bool isEchoEvent(const RoomEventPtr& le, const PendingEventItem& re)
{
- if (le->type() != re->type())
+ if (le->metaType() != re->metaType())
return false;
if (!re->id().isEmpty())
@@ -2635,10 +2630,10 @@ RoomEventPtr makeRedacted(const RoomEvent& target,
QStringLiteral("membership") };
// clang-format on
- static const std::pair<event_type_t, QStringList> keepContentKeysMap[] {
- { RoomMemberEvent::typeId(), { QStringLiteral("membership") } },
- { RoomCreateEvent::typeId(), { QStringLiteral("creator") } },
- { RoomPowerLevelsEvent::typeId(),
+ static const std::pair<event_type_t, QStringList> keepContentKeysMap[]{
+ { RoomMemberEvent::TypeId, { QStringLiteral("membership") } },
+ { RoomCreateEvent::TypeId, { QStringLiteral("creator") } },
+ { RoomPowerLevelsEvent::TypeId,
{ QStringLiteral("ban"), QStringLiteral("events"),
QStringLiteral("events_default"), QStringLiteral("kick"),
QStringLiteral("redact"), QStringLiteral("state_default"),
@@ -2917,7 +2912,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
if (q->supportsCalls())
for (auto it = from; it != syncEdge(); ++it)
- if (const auto* evt = it->viewAs<CallEventBase>())
+ if (const auto* evt = it->viewAs<CallEvent>())
emit q->callEvent(q, evt);
if (totalInserted > 0) {
@@ -3099,7 +3094,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e)
// Change the state
const auto* const oldStateEvent =
- std::exchange(curStateEvent, static_cast<const StateEventBase*>(&e));
+ std::exchange(curStateEvent, static_cast<const StateEvent*>(&e));
Q_ASSERT(!oldStateEvent
|| (oldStateEvent->matrixType() == e.matrixType()
&& oldStateEvent->stateKey() == e.stateKey()));
@@ -3294,7 +3289,7 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event)
}
if (auto* evt = eventCast<const ReadMarkerEvent>(event))
- changes |= d->setFullyReadMarker(evt->event_id());
+ changes |= d->setFullyReadMarker(evt->eventId());
// For all account data events
auto& currentData = d->accountData[event->matrixType()];
diff --git a/lib/room.h b/lib/room.h
index 44504691..5eb66b8b 100644
--- a/lib/room.h
+++ b/lib/room.h
@@ -757,9 +757,8 @@ public:
*/
[[deprecated("Use currentState().get() instead; "
"make sure to check its result for nullptrs")]] //
- const Quotient::StateEventBase*
- getCurrentState(const QString& evtType,
- const QString& stateKey = {}) const;
+ const StateEvent* getCurrentState(const QString& evtType,
+ const QString& stateKey = {}) const;
/// Get a state event with the given event type and state key
/*! This is a typesafe overload that accepts a C++ event type instead of
@@ -770,11 +769,10 @@ public:
"make sure to check its result for nullptrs")]] //
const EvT* getCurrentState(const QString& stateKey = {}) const
{
- QT_IGNORE_DEPRECATIONS(
- const auto* evt = eventCast<const EvT>(
- getCurrentState(EvT::matrixTypeId(), stateKey));)
+ QT_IGNORE_DEPRECATIONS(const auto* evt = eventCast<const EvT>(
+ getCurrentState(EvT::TypeId, stateKey));)
Q_ASSERT(evt);
- Q_ASSERT(evt->matrixTypeId() == EvT::matrixTypeId()
+ Q_ASSERT(evt->matrixType() == EvT::TypeId
&& evt->stateKey() == stateKey);
return evt;
}
@@ -783,16 +781,21 @@ public:
RoomStateView currentState() const;
//! Send a request to update the room state with the given event
- SetRoomStateWithKeyJob* setState(const StateEventBase& evt);
+ SetRoomStateWithKeyJob* setState(const StateEvent& evt);
//! \brief Set a state event of the given type with the given arguments
//!
//! This typesafe overload attempts to send a state event with the type
//! \p EvT and the content defined by \p args. Specifically, the function
- //! creates a temporary object of type \p EvT passing \p args to
- //! the constructor, and sends a request to the homeserver using
- //! the Matrix event type defined by \p EvT and the event content produced
- //! via EvT::contentJson().
+ //! constructs a temporary object of type \p EvT with its content
+ //! list-initialised from \p args, and sends a request to the homeserver
+ //! using the Matrix event type defined by \p EvT and the event content
+ //! produced via EvT::contentJson().
+ //!
+ //! \note This call is not suitable for events that assume non-empty
+ //! stateKey, such as member events; for those you have to create
+ //! a temporary event object yourself and use the setState() overload
+ //! that accepts StateEvent const-ref.
template <typename EvT, typename... ArgTs>
auto setState(ArgTs&&... args)
{
diff --git a/lib/roomstateview.cpp b/lib/roomstateview.cpp
index 94c88eee..be0f7c6c 100644
--- a/lib/roomstateview.cpp
+++ b/lib/roomstateview.cpp
@@ -5,8 +5,8 @@
using namespace Quotient;
-const StateEventBase* RoomStateView::get(const QString& evtType,
- const QString& stateKey) const
+const StateEvent* RoomStateView::get(const QString& evtType,
+ const QString& stateKey) const
{
return value({ evtType, stateKey });
}
@@ -23,10 +23,10 @@ QJsonObject RoomStateView::contentJson(const QString& evtType,
return queryOr(evtType, stateKey, &Event::contentJson, QJsonObject());
}
-const QVector<const StateEventBase*>
-RoomStateView::eventsOfType(const QString& evtType) const
+const QVector<const StateEvent*> RoomStateView::eventsOfType(
+ const QString& evtType) const
{
- auto vals = QVector<const StateEventBase*>();
+ auto vals = QVector<const StateEvent*>();
for (auto it = cbegin(); it != cend(); ++it)
if (it.key().first == evtType)
vals.append(it.value());
diff --git a/lib/roomstateview.h b/lib/roomstateview.h
index 29cce00e..c5261a1e 100644
--- a/lib/roomstateview.h
+++ b/lib/roomstateview.h
@@ -11,11 +11,21 @@ namespace Quotient {
class Room;
+// NB: Both concepts below expect EvT::needsStateKey to exist so you can't
+// express one via negation of the other (there's still an invalid case of
+// a non-state event where needsStateKey is not even defined).
+
+template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>>
+concept Keyed_State_Fn = EvT::needsStateKey;
+
+template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>>
+concept Keyless_State_Fn = !EvT::needsStateKey;
+
class QUOTIENT_API RoomStateView
- : private QHash<StateEventKey, const StateEventBase*> {
+ : private QHash<StateEventKey, const StateEvent*> {
Q_GADGET
public:
- const QHash<StateEventKey, const StateEventBase*>& events() const
+ const QHash<StateEventKey, const StateEvent*>& events() const
{
return *this;
}
@@ -30,37 +40,74 @@ public:
//! have to check that it has_value() before using. Alternatively
//! you can now use queryCurrentState() to access state safely.
//! \sa getCurrentStateContentJson
- const StateEventBase* get(const QString& evtType,
- const QString& stateKey = {}) const;
+ const StateEvent* get(const QString& evtType,
+ const QString& stateKey = {}) const;
//! \brief Get a state event with the given event type and state key
//!
//! This is a typesafe overload that accepts a C++ event type instead of
- //! its Matrix name.
- //! \warning In libQuotient 0.7 the return type changed to an Omittable with
- //! a reference wrapper inside - you have to check that it
- //! has_value() before using. Alternatively you can now use
- //! queryCurrentState() to access state safely.
- template <typename EvT>
+ //! its Matrix name. It is only defined for events with state key (i.e.,
+ //! derived from KeyedStateEvent).
+ template <Keyed_State_Event EvT>
const EvT* get(const QString& stateKey = {}) const
{
- static_assert(std::is_base_of_v<StateEventBase, EvT>);
- if (const auto* evt = get(EvT::matrixTypeId(), stateKey)) {
- Q_ASSERT(evt->matrixType() == EvT::matrixTypeId()
+ if (const auto* evt = get(EvT::TypeId, stateKey)) {
+ Q_ASSERT(evt->matrixType() == EvT::TypeId
&& evt->stateKey() == stateKey);
return eventCast<const EvT>(evt);
}
return nullptr;
}
+ //! \brief Get a state event with the given event type
+ //!
+ //! This is a typesafe overload that accepts a C++ event type instead of
+ //! its Matrix name. This overload only defined for events that do not use
+ //! state key (i.e., derived from KeylessStateEvent).
+ template <Keyless_State_Event EvT>
+ const EvT* get() const
+ {
+ if (const auto* evt = get(EvT::TypeId)) {
+ Q_ASSERT(evt->matrixType() == EvT::TypeId);
+ return eventCast<const EvT>(evt);
+ }
+ return nullptr;
+ }
+
using QHash::contains;
bool contains(const QString& evtType, const QString& stateKey = {}) const;
- template <typename EvT>
+ template <Keyed_State_Event EvT>
bool contains(const QString& stateKey = {}) const
{
- return contains(EvT::matrixTypeId(), stateKey);
+ return contains(EvT::TypeId, stateKey);
+ }
+
+ template <Keyless_State_Event EvT>
+ bool contains() const
+ {
+ return contains(EvT::TypeId);
+ }
+
+ template <Keyed_State_Event EvT>
+ auto content(const QString& stateKey,
+ typename EvT::content_type defaultValue = {}) const
+ {
+ // EventBase<>::content is special in that it returns a const-ref,
+ // and lift() inside queryOr() can't wrap that in a temporary Omittable.
+ if (const auto evt = get<EvT>(stateKey))
+ return evt->content();
+ return std::move(defaultValue);
+ }
+
+ template <Keyless_State_Event EvT>
+ auto content(typename EvT::content_type defaultValue = {}) const
+ {
+ // Same as above
+ if (const auto evt = get<EvT>())
+ return evt->content();
+ return defaultValue;
}
//! \brief Get the content of the current state event with the given
@@ -75,51 +122,87 @@ public:
//!
//! This method returns all known state events that have occured in
//! the room of the given type.
- const QVector<const StateEventBase*>
- eventsOfType(const QString& evtType) const;
+ const QVector<const StateEvent*> eventsOfType(const QString& evtType) const;
+ //! \brief Run a function on a state event with the given type and key
+ //!
+ //! Use this overload when there's no predefined event type or the event
+ //! type is unknown at compile time.
+ //! \return an Omittable with either the result of the function call, or
+ //! with `none` if the event is not found or the function fails
template <typename FnT>
auto query(const QString& evtType, const QString& stateKey, FnT&& fn) const
{
return lift(std::forward<FnT>(fn), get(evtType, stateKey));
}
- template <typename FnT>
+ //! \brief Run a function on a state event with the given type and key
+ //!
+ //! This is an overload for keyed state events (those that have
+ //! `needsStateKey == true`) with type defined at compile time.
+ //! \return an Omittable with either the result of the function call, or
+ //! with `none` if the event is not found or the function fails
+ template <Keyed_State_Fn FnT>
auto query(const QString& stateKey, FnT&& fn) const
{
using EventT = std::decay_t<fn_arg_t<FnT>>;
- static_assert(std::is_base_of_v<StateEventBase, EventT>);
return lift(std::forward<FnT>(fn), get<EventT>(stateKey));
}
+ //! \brief Run a function on a keyless state event with the given type
+ //!
+ //! This is an overload for keyless state events (those having
+ //! `needsStateKey == false`) with type defined at compile time.
+ //! \return an Omittable with either the result of the function call, or
+ //! with `none` if the event is not found or the function fails
+ template <Keyless_State_Fn FnT>
+ auto query(FnT&& fn) const
+ {
+ using EventT = std::decay_t<fn_arg_t<FnT>>;
+ return lift(std::forward<FnT>(fn), get<EventT>());
+ }
+
+ //! \brief Same as query() but with a fallback value
+ //!
+ //! This is a shortcut for `query().value_or()`, passing respective
+ //! arguments to the respective functions. This is an overload for the case
+ //! when the event type cannot be fixed at compile time.
+ //! \return the result of \p fn execution, or \p fallback if the requested
+ //! event doesn't exist or the function fails
template <typename FnT, typename FallbackT>
auto queryOr(const QString& evtType, const QString& stateKey, FnT&& fn,
FallbackT&& fallback) const
{
- return lift(std::forward<FnT>(fn), get(evtType, stateKey))
+ return query(evtType, stateKey, std::forward<FnT>(fn))
.value_or(std::forward<FallbackT>(fallback));
}
- template <typename FnT>
- auto query(FnT&& fn) const
- {
- return query({}, std::forward<FnT>(fn));
- }
-
+ //! \brief Same as query() but with a fallback value
+ //!
+ //! This is a shortcut for `query().value_or()`, passing respective
+ //! arguments to the respective functions. This is an overload for the case
+ //! when the event type cannot be fixed at compile time.
+ //! \return the result of \p fn execution, or \p fallback if the requested
+ //! event doesn't exist or the function fails
template <typename FnT, typename FallbackT>
auto queryOr(const QString& stateKey, FnT&& fn, FallbackT&& fallback) const
{
- using EventT = std::decay_t<fn_arg_t<FnT>>;
- static_assert(std::is_base_of_v<StateEventBase, EventT>);
- return lift(std::forward<FnT>(fn), get<EventT>(stateKey))
+ return query(stateKey, std::forward<FnT>(fn))
.value_or(std::forward<FallbackT>(fallback));
}
+ //! \brief Same as query() but with a fallback value
+ //!
+ //! This is a shortcut for `query().value_or()`, passing respective
+ //! arguments to the respective functions. This is an overload for the case
+ //! when the event type cannot be fixed at compile time.
+ //! \return the result of \p fn execution, or \p fallback if the requested
+ //! event doesn't exist or the function fails
template <typename FnT, typename FallbackT>
auto queryOr(FnT&& fn, FallbackT&& fallback) const
{
- return queryOr({}, std::forward<FnT>(fn),
- std::forward<FallbackT>(fallback));
+ return query(std::forward<FnT>(fn))
+ .value_or(std::forward<FallbackT>(fallback));
}
private:
diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp
index 93416bc4..ec7203af 100644
--- a/lib/syncdata.cpp
+++ b/lib/syncdata.cpp
@@ -3,7 +3,7 @@
#include "syncdata.h"
-#include "events/eventloader.h"
+#include "logging.h"
#include <QtCore/QFile>
#include <QtCore/QFileInfo>