aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitsune Ral <Kitsune-Ral@users.sf.net>2018-03-05 18:25:13 +0900
committerKitsune Ral <Kitsune-Ral@users.sf.net>2018-03-05 18:25:13 +0900
commit658f79dc57a2528878cf29ba3f69095b9e6d18e5 (patch)
tree25c06c7d52a197c145116db8c5e06487e963fc16
parent089a23093dd8e73b4e1e5b1a2aa3935028066faa (diff)
parent2e7627528308da7629f1293757de2fb4bb22a7ad (diff)
downloadlibquotient-658f79dc57a2528878cf29ba3f69095b9e6d18e5.tar.gz
libquotient-658f79dc57a2528878cf29ba3f69095b9e6d18e5.zip
Merge branch 'kitsune-save-room-tags'
-rw-r--r--.travis.yml2
-rw-r--r--CMakeLists.txt1
-rw-r--r--connection.cpp6
-rw-r--r--converters.h56
-rw-r--r--events/accountdataevents.h78
-rw-r--r--events/event.cpp5
-rw-r--r--events/event.h2
-rw-r--r--events/tagevent.cpp71
-rw-r--r--events/tagevent.h74
-rw-r--r--examples/qmc-example.cpp108
-rw-r--r--jobs/generated/account-data.cpp28
-rw-r--r--jobs/generated/account-data.h27
-rw-r--r--libqmatrixclient.pri3
-rw-r--r--room.cpp69
-rw-r--r--room.h29
15 files changed, 351 insertions, 208 deletions
diff --git a/.travis.yml b/.travis.yml
index 001ba11f..c9002c13 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,7 +31,7 @@ script:
- cd ..
- qmake qmc-example.pro "CONFIG += debug" "CONFIG -= app_bundle" "QMAKE_CC = $CC" "QMAKE_CXX = $CXX"
- make all
-- $VALGRIND ./qmc-example "$QMC_TEST_USER" "$QMC_TEST_PWD" '#qmc-test:matrix.org'
+- $VALGRIND ./qmc-example "$QMC_TEST_USER" "$QMC_TEST_PWD" '#qmc-test:matrix.org' "Travis CI job $TRAVIS_JOB_NUMBER"
notifications:
webhooks:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d7762e17..e95c72d0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -60,7 +60,6 @@ set(libqmatrixclient_SRCS
events/roomavatarevent.cpp
events/typingevent.cpp
events/receiptevent.cpp
- events/tagevent.cpp
jobs/requestdata.cpp
jobs/basejob.cpp
jobs/checkauthmethods.cpp
diff --git a/connection.cpp b/connection.cpp
index a57bd8b4..80685dd1 100644
--- a/connection.cpp
+++ b/connection.cpp
@@ -539,12 +539,12 @@ QHash<QString, QVector<Room*>> Connection::tagsToRooms() const
QStringList Connection::tagNames() const
{
- QStringList tags ({"m.favourite"});
+ QStringList tags ({FavouriteTag});
for (auto* r: d->roomMap)
for (const auto& tag: r->tagNames())
- if (tag != "m.lowpriority" && !tags.contains(tag))
+ if (tag != LowPriorityTag && !tags.contains(tag))
tags.push_back(tag);
- tags.push_back("m.lowpriority");
+ tags.push_back(LowPriorityTag);
return tags;
}
diff --git a/converters.h b/converters.h
index 00d1d339..96efe5f8 100644
--- a/converters.h
+++ b/converters.h
@@ -46,17 +46,22 @@ namespace QMatrixClient
inline QJsonValue toJson(const QByteArray& bytes)
{
-#if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
- return QJsonValue(QLatin1String(bytes.constData()));
-#else
return QJsonValue(bytes.constData());
-#endif
+ }
+
+ template <typename T>
+ inline QJsonValue toJson(const QHash<QString, T>& hashMap)
+ {
+ QJsonObject json;
+ for (auto it = hashMap.begin(); it != hashMap.end(); ++it)
+ json.insert(it.key(), toJson(it.value()));
+ return json;
}
template <typename T>
struct FromJson
{
- T operator()(QJsonValue jv) const { return static_cast<T>(jv); }
+ T operator()(const QJsonValue& jv) const { return static_cast<T>(jv); }
};
template <typename T>
@@ -67,32 +72,32 @@ namespace QMatrixClient
template <> struct FromJson<bool>
{
- bool operator()(QJsonValue jv) const { return jv.toBool(); }
+ bool operator()(const QJsonValue& jv) const { return jv.toBool(); }
};
template <> struct FromJson<int>
{
- int operator()(QJsonValue jv) const { return jv.toInt(); }
+ int operator()(const QJsonValue& jv) const { return jv.toInt(); }
};
template <> struct FromJson<double>
{
- double operator()(QJsonValue jv) const { return jv.toDouble(); }
+ double operator()(const QJsonValue& jv) const { return jv.toDouble(); }
};
template <> struct FromJson<qint64>
{
- qint64 operator()(QJsonValue jv) const { return qint64(jv.toDouble()); }
+ qint64 operator()(const QJsonValue& jv) const { return qint64(jv.toDouble()); }
};
template <> struct FromJson<QString>
{
- QString operator()(QJsonValue jv) const { return jv.toString(); }
+ QString operator()(const QJsonValue& jv) const { return jv.toString(); }
};
template <> struct FromJson<QDateTime>
{
- QDateTime operator()(QJsonValue jv) const
+ QDateTime operator()(const QJsonValue& jv) const
{
return QDateTime::fromMSecsSinceEpoch(fromJson<qint64>(jv), Qt::UTC);
}
@@ -100,7 +105,7 @@ namespace QMatrixClient
template <> struct FromJson<QDate>
{
- QDate operator()(QJsonValue jv) const
+ QDate operator()(const QJsonValue& jv) const
{
return fromJson<QDateTime>(jv).date();
}
@@ -108,17 +113,23 @@ namespace QMatrixClient
template <> struct FromJson<QJsonObject>
{
- QJsonObject operator()(QJsonValue jv) const { return jv.toObject(); }
+ QJsonObject operator()(const QJsonValue& jv) const
+ {
+ return jv.toObject();
+ }
};
template <> struct FromJson<QJsonArray>
{
- QJsonArray operator()(QJsonValue jv) const { return jv.toArray(); }
+ QJsonArray operator()(const QJsonValue& jv) const
+ {
+ return jv.toArray();
+ }
};
template <typename T> struct FromJson<QVector<T>>
{
- QVector<T> operator()(QJsonValue jv) const
+ QVector<T> operator()(const QJsonValue& jv) const
{
const auto jsonArray = jv.toArray();
QVector<T> vect; vect.resize(jsonArray.size());
@@ -130,7 +141,7 @@ namespace QMatrixClient
template <typename T> struct FromJson<QList<T>>
{
- QList<T> operator()(QJsonValue jv) const
+ QList<T> operator()(const QJsonValue& jv) const
{
const auto jsonArray = jv.toArray();
QList<T> sl; sl.reserve(jsonArray.size());
@@ -144,10 +155,21 @@ namespace QMatrixClient
template <> struct FromJson<QByteArray>
{
- QByteArray operator()(QJsonValue jv) const
+ inline QByteArray operator()(const QJsonValue& jv) const
{
return fromJson<QString>(jv).toLatin1();
}
};
+ template <typename T> struct FromJson<QHash<QString, T>>
+ {
+ QHash<QString, T> operator()(const QJsonValue& jv) const
+ {
+ const auto json = jv.toObject();
+ QHash<QString, T> h; h.reserve(json.size());
+ for (auto it = json.begin(); it != json.end(); ++it)
+ h.insert(it.key(), fromJson<T>(it.value()));
+ return h;
+ }
+ };
} // namespace QMatrixClient
diff --git a/events/accountdataevents.h b/events/accountdataevents.h
new file mode 100644
index 00000000..f3ba27bb
--- /dev/null
+++ b/events/accountdataevents.h
@@ -0,0 +1,78 @@
+#include <utility>
+
+/******************************************************************************
+ * Copyright (C) 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include "event.h"
+#include "eventcontent.h"
+
+namespace QMatrixClient
+{
+ static constexpr const char* FavouriteTag = "m.favourite";
+ static constexpr const char* LowPriorityTag = "m.lowpriority";
+
+ struct TagRecord
+ {
+ TagRecord (QString order = {}) : order(std::move(order)) { }
+ explicit TagRecord(const QJsonValue& jv)
+ : order(jv.toObject().value("order").toString())
+ { }
+
+ QString order;
+
+ bool operator==(const TagRecord& other) const
+ { return order == other.order; }
+ bool operator!=(const TagRecord& other) const
+ { return !operator==(other); }
+ };
+
+ inline QJsonValue toJson(const TagRecord& rec)
+ {
+ return QJsonObject {{ QStringLiteral("order"), rec.order }};
+ }
+
+ using TagsMap = QHash<QString, TagRecord>;
+
+#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _EnumType, _ContentType, _ContentKey) \
+ class _Name : public Event \
+ { \
+ public: \
+ static constexpr const char* TypeId = _TypeId; \
+ static const char* typeId() { return TypeId; } \
+ explicit _Name(const QJsonObject& obj) \
+ : Event((_EnumType), obj) \
+ , _content(contentJson(), QStringLiteral(#_ContentKey)) \
+ { } \
+ template <typename... Ts> \
+ explicit _Name(Ts&&... contentArgs) \
+ : Event(_EnumType) \
+ , _content(QStringLiteral(#_ContentKey), \
+ std::forward<Ts>(contentArgs)...) \
+ { } \
+ const _ContentType& _ContentKey() const { return _content.value; } \
+ QJsonObject toJson() const { return _content.toJson(); } \
+ protected: \
+ EventContent::SimpleContent<_ContentType> _content; \
+ };
+
+ DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", EventType::Tag, TagsMap, tags)
+ DEFINE_SIMPLE_EVENT(ReadMarkerEvent, "m.fully_read", EventType::ReadMarker,
+ QString, event_id)
+}
diff --git a/events/event.cpp b/events/event.cpp
index 74a2c3d7..f3e965e2 100644
--- a/events/event.cpp
+++ b/events/event.cpp
@@ -24,7 +24,7 @@
#include "roomavatarevent.h"
#include "typingevent.h"
#include "receiptevent.h"
-#include "tagevent.h"
+#include "accountdataevents.h"
#include "redactionevent.h"
#include "logging.h"
@@ -88,7 +88,8 @@ EventPtr _impl::doMakeEvent<Event>(const QJsonObject& obj)
return EventPtr(move(e));
return EventPtr { makeIfMatches<Event,
- TypingEvent, ReceiptEvent, TagEvent>(obj, obj["type"].toString()) };
+ TypingEvent, ReceiptEvent, TagEvent, ReadMarkerEvent>(
+ obj, obj["type"].toString()) };
}
RoomEvent::RoomEvent(Event::Type type) : Event(type) { }
diff --git a/events/event.h b/events/event.h
index f0ca2d15..eccfec41 100644
--- a/events/event.h
+++ b/events/event.h
@@ -45,7 +45,7 @@ namespace QMatrixClient
enum class Type : quint16
{
Unknown = 0,
- Typing, Receipt, Tag, DirectChat,
+ Typing, Receipt, Tag, DirectChat, ReadMarker,
RoomEventBase = 0x1000,
RoomMessage = RoomEventBase + 1,
RoomEncryptedMessage, Redaction,
diff --git a/events/tagevent.cpp b/events/tagevent.cpp
deleted file mode 100644
index c643ac62..00000000
--- a/events/tagevent.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2018 Kitsune Ral <kitsune-ral@users.sf.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "tagevent.h"
-
-using namespace QMatrixClient;
-
-TagRecord::TagRecord(const QJsonObject& json)
- : order(json.value("order").toString())
-{ }
-
-TagEvent::TagEvent()
- : Event(Type::Tag)
-{
- // TODO: Support getting a list of tags and saving it
-}
-
-TagEvent::TagEvent(const QJsonObject& obj)
- : Event(Type::Tag, obj)
-{
- Q_ASSERT(obj["type"].toString() == TypeId);
-}
-
-QStringList TagEvent::tagNames() const
-{
- return tagsObject().keys();
-}
-
-QHash<QString, TagRecord> TagEvent::tags() const
-{
- QHash<QString, TagRecord> result;
- auto allTags = tagsObject();
- for (auto it = allTags.begin(); it != allTags.end(); ++ it)
- result.insert(it.key(), TagRecord(it.value().toObject()));
- return result;
-}
-
-bool TagEvent::empty() const
-{
- return tagsObject().empty();
-}
-
-bool TagEvent::contains(const QString& name) const
-{
- return tagsObject().contains(name);
-}
-
-TagRecord TagEvent::recordForTag(const QString& name) const
-{
- return TagRecord(tagsObject().value(name).toObject());
-}
-
-QJsonObject TagEvent::tagsObject() const
-{
- return contentJson().value("tags").toObject();
-}
diff --git a/events/tagevent.h b/events/tagevent.h
deleted file mode 100644
index 26fe8788..00000000
--- a/events/tagevent.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2018 Kitsune Ral <kitsune-ral@users.sf.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#pragma once
-
-#include "event.h"
-
-namespace QMatrixClient
-{
- static constexpr const char* FavouriteTag = "m.favourite";
- static constexpr const char* LowPriorityTag = "m.lowpriority";
-
- struct TagRecord
- {
- explicit TagRecord(const QJsonObject& json = {});
-
- QString order;
- };
-
- class TagEvent : public Event
- {
- public:
- TagEvent();
- explicit TagEvent(const QJsonObject& obj);
-
- /** Get the list of tag names */
- QStringList tagNames() const;
-
- /** Get the list of tags along with information on each */
- QHash<QString, TagRecord> tags() const;
-
- /** Check if the event lists no tags */
- bool empty() const;
-
- /** Check whether the tags list contains the specified name */
- bool contains(const QString& name) const;
-
- /** Get the record for the given tag name */
- TagRecord recordForTag(const QString& name) const;
-
- /** Get the whole tags content as a JSON object
- * It's NOT recommended to use this method directly from client code.
- * Use other convenience methods provided by the class.
- */
- QJsonObject tagsObject() const;
-
- static constexpr const char * TypeId = "m.tag";
- };
-
- using TagEventPtr = event_ptr_tt<TagEvent>;
-
- inline QJsonValue toJson(const TagEventPtr& tagEvent)
- {
- return QJsonObject {{ "type", "m.tag" },
- // TODO: Replace tagsObject() with a genuine list of tags
- // (or make the needed JSON upon TagEvent creation)
- { "content", QJsonObject {{ "tags", tagEvent->tagsObject() }} }};
- }
-}
diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp
index e0aabca9..f63b32a2 100644
--- a/examples/qmc-example.cpp
+++ b/examples/qmc-example.cpp
@@ -2,18 +2,68 @@
#include "connection.h"
#include "room.h"
#include "user.h"
+#include "jobs/sendeventjob.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QStringBuilder>
+#include <QtCore/QTimer>
#include <iostream>
using namespace QMatrixClient;
using std::cout;
using std::endl;
-using std::bind;
using namespace std::placeholders;
-void onNewRoom(Room* r, const char* targetRoomName)
+static int semaphor = 0;
+static Room* targetRoom = nullptr;
+
+#define QMC_CHECK(origin, description, condition) \
+ cout << (description) \
+ << (!!(condition) ? " successul" : " FAILED") << endl; \
+ targetRoom->postMessage(QString(origin) % ": " % QStringLiteral(description) % \
+ (!!(condition) ? QStringLiteral(" successful") : \
+ QStringLiteral(" FAILED")), MessageEventType::Notice)
+
+void addAndRemoveTag(const char* origin)
+{
+ ++semaphor;
+ static const auto TestTag = QStringLiteral("org.qmatrixclient.test");
+ QObject::connect(targetRoom, &Room::tagsChanged, targetRoom, [=] {
+ cout << "Room " << targetRoom->id().toStdString()
+ << ", tag(s) changed:" << endl
+ << " " << targetRoom->tagNames().join(", ").toStdString() << endl;
+ if (targetRoom->tags().contains(TestTag))
+ {
+ targetRoom->removeTag(TestTag);
+ QMC_CHECK(origin, "Tagging test",
+ !targetRoom->tags().contains(TestTag));
+ --semaphor;
+ QObject::disconnect(targetRoom, &Room::tagsChanged, nullptr, nullptr);
+ }
+ });
+ // The reverse order because tagsChanged is emitted synchronously.
+ targetRoom->addTag(TestTag);
+}
+
+void sendAndRedact(const char* origin)
+{
+ ++semaphor;
+ auto* job = targetRoom->connection()->callApi<SendEventJob>(targetRoom->id(),
+ RoomMessageEvent("Message to redact"));
+ QObject::connect(job, &BaseJob::success, targetRoom, [job] {
+ targetRoom->redactEvent(job->eventId(), "qmc-example");
+ });
+ QObject::connect(targetRoom, &Room::replacedEvent, targetRoom,
+ [=] (const RoomEvent* newEvent) {
+ QMC_CHECK(origin, "Redaction", newEvent->isRedacted() &&
+ newEvent->redactionReason() == "qmc-example");
+ --semaphor;
+ QObject::disconnect(targetRoom, &Room::replacedEvent,
+ nullptr, nullptr);
+ });
+}
+
+void onNewRoom(Room* r, const char* targetRoomName, const char* origin)
{
cout << "New room: " << r->id().toStdString() << endl;
QObject::connect(r, &Room::namesChanged, [=] {
@@ -24,11 +74,15 @@ void onNewRoom(Room* r, const char* targetRoomName)
if (targetRoomName && (r->name() == targetRoomName ||
r->canonicalAlias() == targetRoomName))
{
- r->postMessage(
- "This is a test message from an example application\n"
- "The current user is " % r->localUser()->fullName(r) % "\n" %
- QStringLiteral("This room has %1 member(s)")
- .arg(r->memberCount()) % "\n" %
+ cout << "Found the target room, proceeding for tests" << endl;
+ targetRoom = r;
+ addAndRemoveTag(origin);
+ sendAndRedact(origin);
+ targetRoom->postMessage(
+ "This is a test notice from an example application\n"
+ "Origin: " % QString(origin) % "\n"
+ "The current user is " %
+ targetRoom->localUser()->fullName(targetRoom) % "\n" %
// "The room is " %
// (r->isDirectChat() ? "" : "not ") % "a direct chat\n" %
"Have a good day",
@@ -36,11 +90,7 @@ void onNewRoom(Room* r, const char* targetRoomName)
);
}
});
- QObject::connect(r, &Room::tagsChanged, [=] {
- cout << "Room " << r->id().toStdString() << ", tag(s) changed:" << endl
- << " " << r->tagNames().join(", ").toStdString() << endl << endl;
- });
- QObject::connect(r, &Room::aboutToAddNewMessages, [=] (RoomEventsRange timeline) {
+ QObject::connect(r, &Room::aboutToAddNewMessages, [r] (RoomEventsRange timeline) {
cout << timeline.size() << " new event(s) in room "
<< r->id().toStdString() << endl;
// for (const auto& item: timeline)
@@ -56,13 +106,15 @@ void onNewRoom(Room* r, const char* targetRoomName)
void finalize(Connection* conn)
{
+ if (semaphor)
+ cout << "One or more tests FAILED" << endl;
cout << "Logging out" << endl;
conn->logout();
QObject::connect(conn, &Connection::loggedOut, QCoreApplication::instance(),
[conn] {
conn->deleteLater();
- QCoreApplication::instance()->processEvents();
- QCoreApplication::instance()->quit();
+ QCoreApplication::processEvents();
+ QCoreApplication::exit(semaphor);
});
}
@@ -83,10 +135,30 @@ int main(int argc, char* argv[])
});
const char* targetRoomName = argc >= 4 ? argv[3] : nullptr;
if (targetRoomName)
- cout << "Target room name: " << targetRoomName;
+ cout << "Target room name: " << targetRoomName << endl;
+ const char* origin = argc >= 5 ? argv[4] : nullptr;
+ if (origin)
+ cout << "Origin for the test message: " << origin << endl;
QObject::connect(conn, &Connection::newRoom,
- bind(onNewRoom, _1, targetRoomName));
- QObject::connect(conn, &Connection::syncDone,
- bind(finalize, conn));
+ [=](Room* room) { onNewRoom(room, targetRoomName, origin); });
+ QObject::connect(conn, &Connection::syncDone, conn, [conn] {
+ cout << "Sync complete, " << semaphor << " tests in the air" << endl;
+ if (semaphor)
+ conn->sync(10000);
+ else
+ {
+ if (targetRoom)
+ {
+ auto j = conn->callApi<SendEventJob>(targetRoom->id(),
+ RoomMessageEvent("All tests finished"));
+ QObject::connect(j, &BaseJob::finished,
+ conn, [conn] { finalize(conn); });
+ }
+ else
+ finalize(conn);
+ }
+ });
+ // Big red countdown
+ QTimer::singleShot(180000, conn, [conn] { finalize(conn); });
return app.exec();
}
diff --git a/jobs/generated/account-data.cpp b/jobs/generated/account-data.cpp
new file mode 100644
index 00000000..35ee94c0
--- /dev/null
+++ b/jobs/generated/account-data.cpp
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "account-data.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+SetAccountDataJob::SetAccountDataJob(const QString& userId, const QString& type, const QJsonObject& content)
+ : BaseJob(HttpVerb::Put, "SetAccountDataJob",
+ basePath % "/user/" % userId % "/account_data/" % type)
+{
+ setRequestData(Data(content));
+}
+
+SetAccountDataPerRoomJob::SetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type, const QJsonObject& content)
+ : BaseJob(HttpVerb::Put, "SetAccountDataPerRoomJob",
+ basePath % "/user/" % userId % "/rooms/" % roomId % "/account_data/" % type)
+{
+ setRequestData(Data(content));
+}
+
diff --git a/jobs/generated/account-data.h b/jobs/generated/account-data.h
new file mode 100644
index 00000000..69ad9fb4
--- /dev/null
+++ b/jobs/generated/account-data.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+#include <QtCore/QJsonObject>
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class SetAccountDataJob : public BaseJob
+ {
+ public:
+ explicit SetAccountDataJob(const QString& userId, const QString& type, const QJsonObject& content = {});
+ };
+
+ class SetAccountDataPerRoomJob : public BaseJob
+ {
+ public:
+ explicit SetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type, const QJsonObject& content = {});
+ };
+} // namespace QMatrixClient
diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri
index 7cfa94a1..c7b95617 100644
--- a/libqmatrixclient.pri
+++ b/libqmatrixclient.pri
@@ -24,7 +24,7 @@ HEADERS += \
$$PWD/events/roomavatarevent.h \
$$PWD/events/typingevent.h \
$$PWD/events/receiptevent.h \
- $$PWD/events/tagevent.h \
+ $$PWD/events/accountdataevents.h \
$$PWD/events/redactionevent.h \
$$PWD/jobs/requestdata.h \
$$PWD/jobs/basejob.h \
@@ -56,7 +56,6 @@ SOURCES += \
$$PWD/events/roommemberevent.cpp \
$$PWD/events/typingevent.cpp \
$$PWD/events/receiptevent.cpp \
- $$PWD/events/tagevent.cpp \
$$PWD/jobs/requestdata.cpp \
$$PWD/jobs/basejob.cpp \
$$PWD/jobs/checkauthmethods.cpp \
diff --git a/room.cpp b/room.cpp
index cfa705bb..cb94ddb6 100644
--- a/room.cpp
+++ b/room.cpp
@@ -24,6 +24,7 @@
#include "jobs/generated/leaving.h"
#include "jobs/generated/receipts.h"
#include "jobs/generated/redaction.h"
+#include "jobs/generated/account-data.h"
#include "jobs/setroomstatejob.h"
#include "events/simplestateevents.h"
#include "events/roomavatarevent.h"
@@ -64,7 +65,6 @@ enum EventsPlacement : int { Older = -1, Newer = 1 };
# define WORKAROUND_EXTENDED_INITIALIZER_LIST
#endif
-
class Room::Private
{
public:
@@ -105,7 +105,7 @@ class Room::Private
QString firstDisplayedEventId;
QString lastDisplayedEventId;
QHash<const User*, QString> lastReadEventIds;
- TagEventPtr tags = std::make_unique<TagEvent>();
+ TagsMap tags;
QHash<QString, QVariantHash> accountData;
QString prevBatch;
QPointer<RoomMessagesJob> roomMessagesJob;
@@ -202,6 +202,13 @@ class Room::Private
*/
void processRedaction(RoomEventPtr redactionEvent);
+ template <typename EvT>
+ SetAccountDataPerRoomJob* setAccountData(const EvT& event)
+ {
+ return connection->callApi<SetAccountDataPerRoomJob>(
+ connection->userId(), id, EvT::typeId(), event.toJson());
+ }
+
QJsonObject toJson() const;
private:
@@ -566,27 +573,56 @@ void Room::resetHighlightCount()
QStringList Room::tagNames() const
{
- return d->tags->tagNames();
+ return d->tags.keys();
}
-QHash<QString, TagRecord> Room::tags() const
+TagsMap Room::tags() const
{
- return d->tags->tags();
+ return d->tags;
}
TagRecord Room::tag(const QString& name) const
{
- return d->tags->recordForTag(name);
+ return d->tags.value(name);
+}
+
+void Room::addTag(const QString& name, const TagRecord& record)
+{
+ if (d->tags.contains(name))
+ return;
+
+ d->tags.insert(name, record);
+ d->setAccountData(TagEvent(d->tags));
+ emit tagsChanged();
+}
+
+void Room::removeTag(const QString& name)
+{
+ if (!d->tags.contains(name))
+ return;
+
+ d->tags.remove(name);
+ d->setAccountData(TagEvent(d->tags));
+ emit tagsChanged();
+}
+
+void Room::setTags(const TagsMap& newTags)
+{
+ if (newTags == d->tags)
+ return;
+ d->tags = newTags;
+ d->setAccountData(TagEvent(d->tags));
+ emit tagsChanged();
}
bool Room::isFavourite() const
{
- return d->tags->contains(FavouriteTag);
+ return d->tags.contains(FavouriteTag);
}
bool Room::isLowPriority() const
{
- return d->tags->contains(LowPriorityTag);
+ return d->tags.contains(LowPriorityTag);
}
const RoomMessageEvent*
@@ -1477,13 +1513,19 @@ void Room::processAccountDataEvent(EventPtr event)
switch (event->type())
{
case EventType::Tag:
- d->tags.reset(static_cast<TagEvent*>(event.release()));
+ {
+ auto newTags = static_cast<TagEvent*>(event.get())->tags();
+ if (newTags == d->tags)
+ break;
+ d->tags = newTags;
qCDebug(MAIN) << "Room" << id() << "is tagged with: "
<< tagNames().join(", ");
emit tagsChanged();
break;
+ }
default:
- d->accountData[event->jsonType()] = event->contentJson().toVariantHash();
+ d->accountData[event->jsonType()] =
+ event->contentJson().toVariantHash();
}
}
@@ -1636,8 +1678,11 @@ QJsonObject Room::Private::toJson() const
}
QJsonArray accountDataEvents;
- if (!tags->empty())
- accountDataEvents.append(QMatrixClient::toJson(tags));
+ if (!tags.empty())
+ accountDataEvents.append(QJsonObject(
+ { { QStringLiteral("type"), QStringLiteral("m.tag") }
+ , { QStringLiteral("content"), TagEvent(tags).toJson() }
+ }));
if (!accountData.empty())
{
diff --git a/room.h b/room.h
index 71d5c433..0eb5ecc3 100644
--- a/room.h
+++ b/room.h
@@ -20,13 +20,9 @@
#include "jobs/syncjob.h"
#include "events/roommessageevent.h"
-#include "events/tagevent.h"
+#include "events/accountdataevents.h"
#include "joinstate.h"
-#include <QtCore/QList>
-#include <QtCore/QStringList>
-#include <QtCore/QObject>
-#include <QtCore/QJsonObject>
#include <QtGui/QPixmap>
#include <memory>
@@ -241,9 +237,30 @@ namespace QMatrixClient
Q_INVOKABLE void resetHighlightCount();
QStringList tagNames() const;
- QHash<QString, TagRecord> tags() const;
+ TagsMap tags() const;
TagRecord tag(const QString& name) const;
+ /** Add a new tag to this room
+ * If this room already has this tag, nothing happens. If it's a new
+ * tag for the room, the respective tag record is added to the set
+ * of tags and the new set is sent to the server to update other
+ * clients.
+ */
+ void addTag(const QString& name, const TagRecord& record = {});
+
+ /** Remove a tag from the room */
+ void removeTag(const QString& name);
+
+ /** Overwrite the room's tags
+ * This completely replaces the existing room's tags with a set
+ * of new ones and updates the new set on the server. Unlike
+ * most other methods in Room, this one sends a signal about changes
+ * immediately, not waiting for confirmation from the server
+ * (because tags are saved in account data rather than in shared
+ * room state).
+ */
+ void setTags(const TagsMap& newTags);
+
/** Check whether the list of tags has m.favourite */
bool isFavourite() const;
/** Check whether the list of tags has m.lowpriority */