From 646ee63846c8985b6222ae1096ccc970a1834ce5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 1 Mar 2018 20:12:43 +0900 Subject: Fix tags saving/restoring (finally) Closes #134. --- events/tagevent.cpp | 21 +++++++++++++++++++++ events/tagevent.h | 27 +++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) (limited to 'events') diff --git a/events/tagevent.cpp b/events/tagevent.cpp index c6297003..886a10c6 100644 --- a/events/tagevent.cpp +++ b/events/tagevent.cpp @@ -24,6 +24,12 @@ 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) { @@ -44,6 +50,21 @@ QHash TagEvent::tags() const 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 index 44a7e49a..26fe8788 100644 --- a/events/tagevent.h +++ b/events/tagevent.h @@ -35,6 +35,7 @@ namespace QMatrixClient class TagEvent : public Event { public: + TagEvent(); explicit TagEvent(const QJsonObject& obj); /** Get the list of tag names */ @@ -43,9 +44,31 @@ namespace QMatrixClient /** Get the list of tags along with information on each */ QHash tags() const; - static constexpr const char * TypeId = "m.tag"; + /** 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; - protected: + /** 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; + + 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() }} }}; + } } -- cgit v1.2.3 From 4916c0b65f8415db1e189e7a9963fce71d3b8b71 Mon Sep 17 00:00:00 2001 From: Roman Plášil Date: Thu, 1 Mar 2018 23:46:35 +0800 Subject: Improve compatibility with gcc 4.9 to be able to build for Android with QtCreator --- events/tagevent.cpp | 2 +- jobs/syncjob.cpp | 2 +- room.cpp | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) (limited to 'events') diff --git a/events/tagevent.cpp b/events/tagevent.cpp index 886a10c6..c643ac62 100644 --- a/events/tagevent.cpp +++ b/events/tagevent.cpp @@ -44,7 +44,7 @@ QStringList TagEvent::tagNames() const QHash TagEvent::tags() const { QHash result; - auto allTags { tagsObject() }; + auto allTags = tagsObject(); for (auto it = allTags.begin(); it != allTags.end(); ++ it) result.insert(it.key(), TagRecord(it.value().toObject())); return result; diff --git a/jobs/syncjob.cpp b/jobs/syncjob.cpp index fd8bfc1a..6b3f3acf 100644 --- a/jobs/syncjob.cpp +++ b/jobs/syncjob.cpp @@ -68,7 +68,7 @@ BaseJob::Status SyncData::parseJson(const QJsonDocument &data) { QElapsedTimer et; et.start(); - auto json { data.object() }; + auto json = data.object(); nextBatch_ = json.value("next_batch").toString(); // TODO: presence accountData.fromJson(json); diff --git a/room.cpp b/room.cpp index ae3360dc..bbad694d 100644 --- a/room.cpp +++ b/room.cpp @@ -52,9 +52,19 @@ using namespace QMatrixClient; using namespace std::placeholders; +#if !(defined __GLIBCXX__ && __GLIBCXX__ <= 20150123) +using std::llround; +#endif enum EventsPlacement : int { Older = -1, Newer = 1 }; +// A workaround for MSVC 2015 that fails with "error C2440: 'return': +// cannot convert from 'initializer list' to 'QMatrixClient::FileTransferInfo'" +#if (defined(_MSC_VER) && _MSC_VER < 1910) || (defined(__GNUC__) && __GNUC__ <= 4) +# define WORKAROUND_EXTENDED_INITIALIZER_LIST +#endif + + class Room::Private { public: @@ -102,7 +112,7 @@ class Room::Private struct FileTransferPrivateInfo { -#if (defined(_MSC_VER) && _MSC_VER < 1910) || (defined(__GNUC__) && __GNUC__ <= 4) +#ifdef WORKAROUND_EXTENDED_INITIALIZER_LIST FileTransferPrivateInfo() = default; FileTransferPrivateInfo(BaseJob* j, QString fileName) : job(j), localFileInfo(fileName) @@ -647,13 +657,11 @@ FileTransferInfo Room::fileTransferInfo(const QString& id) const if (total > INT_MAX) { // JavaScript doesn't deal with 64-bit integers; scale down if necessary - progress = std::llround(double(progress) / total * INT_MAX); + progress = llround(double(progress) / total * INT_MAX); total = INT_MAX; } -#if (defined(_MSC_VER) && _MSC_VER < 1910) || (defined(__GNUC__) && __GNUC__ <= 4) - // A workaround for MSVC 2015 that fails with "error C2440: 'return': - // cannot convert from 'initializer list' to 'QMatrixClient::FileTransferInfo'" +#ifdef WORKAROUND_EXTENDED_INITIALIZER_LIST FileTransferInfo fti; fti.status = infoIt->status; fti.progress = int(progress); -- cgit v1.2.3 From 089a23093dd8e73b4e1e5b1a2aa3935028066faa Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 4 Mar 2018 18:44:47 +0900 Subject: simplestateevents.h: minor tweaks --- events/simplestateevents.h | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) (limited to 'events') diff --git a/events/simplestateevents.h b/events/simplestateevents.h index d5841bdc..6b0cd51a 100644 --- a/events/simplestateevents.h +++ b/events/simplestateevents.h @@ -19,36 +19,35 @@ #pragma once #include "event.h" - #include "eventcontent.h" namespace QMatrixClient { -#define DECLARE_SIMPLE_STATE_EVENT(_Name, _TypeId, _EnumType, _ContentType, _ContentKey) \ +#define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _EnumType, _ContentType, _ContentKey) \ class _Name \ : public StateEvent> \ { \ public: \ static constexpr const char* TypeId = _TypeId; \ explicit _Name(const QJsonObject& obj) \ - : StateEvent(_EnumType, obj, #_ContentKey) \ + : StateEvent(_EnumType, obj, QStringLiteral(#_ContentKey)) \ { } \ template \ explicit _Name(T&& value) \ - : StateEvent(_EnumType, #_ContentKey, \ + : StateEvent(_EnumType, QStringLiteral(#_ContentKey), \ std::forward(value)) \ { } \ - _ContentType _ContentKey() const { return content().value; } \ + const _ContentType& _ContentKey() const { return content().value; } \ }; - DECLARE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", - Event::Type::RoomName, QString, name) - DECLARE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", - Event::Type::RoomAliases, QStringList, aliases) - DECLARE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", - Event::Type::RoomCanonicalAlias, QString, alias) - DECLARE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", - Event::Type::RoomTopic, QString, topic) - DECLARE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", - Event::Type::RoomEncryption, QString, algorithm) + DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", + Event::Type::RoomName, QString, name) + DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", + Event::Type::RoomAliases, QStringList, aliases) + DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", + Event::Type::RoomCanonicalAlias, QString, alias) + DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", + Event::Type::RoomTopic, QString, topic) + DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", + Event::Type::RoomEncryption, QString, algorithm) } // namespace QMatrixClient -- cgit v1.2.3 From 6ea1fb621488910de055bd3af4d00343a763541a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 5 Mar 2018 10:16:20 +0900 Subject: ReadMarkerEvent; TagEvent remade with less boilerplate code tagevent.h -> accountdataevents.h now has a macro to define more simplistic events along the lines of simplestateevents.h but inheriting from Event instead. TagEvent and ReadMarkerEvent(m.fully_read) are defined using this macro. ReadMarkerEvent is also wired through event.* (but not further yet). --- CMakeLists.txt | 1 - events/accountdataevents.h | 78 ++++++++++++++++++++++++++++++++++++++++++++++ events/event.cpp | 5 +-- events/event.h | 2 +- events/tagevent.cpp | 71 ----------------------------------------- events/tagevent.h | 74 ------------------------------------------- libqmatrixclient.pri | 3 +- room.cpp | 36 ++++++++++++++------- room.h | 10 +++--- 9 files changed, 112 insertions(+), 168 deletions(-) create mode 100644 events/accountdataevents.h delete mode 100644 events/tagevent.cpp delete mode 100644 events/tagevent.h (limited to 'events') 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/events/accountdataevents.h b/events/accountdataevents.h new file mode 100644 index 00000000..78cf9c46 --- /dev/null +++ b/events/accountdataevents.h @@ -0,0 +1,78 @@ +#include + +/****************************************************************************** + * Copyright (C) 2018 Kitsune Ral + * + * 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; + +#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 \ + explicit _Name(Ts&&... contentArgs) \ + : Event(_EnumType) \ + , _content(QStringLiteral(#_ContentKey), \ + std::forward(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(const QJsonObject& obj) return EventPtr(move(e)); return EventPtr { makeIfMatches(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 - * - * 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 TagEvent::tags() const -{ - QHash 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 - * - * 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 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; - - 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/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 e03a2b5b..971e4121 100644 --- a/room.cpp +++ b/room.cpp @@ -65,7 +65,6 @@ enum EventsPlacement : int { Older = -1, Newer = 1 }; # define WORKAROUND_EXTENDED_INITIALIZER_LIST #endif - class Room::Private { public: @@ -106,7 +105,7 @@ class Room::Private QString firstDisplayedEventId; QString lastDisplayedEventId; QHash lastReadEventIds; - TagEventPtr tags = std::make_unique(); + TagsMap tags; QHash accountData; QString prevBatch; QPointer roomMessagesJob; @@ -574,27 +573,36 @@ void Room::resetHighlightCount() QStringList Room::tagNames() const { - return d->tags->tagNames(); + return d->tags.keys(); } -QHash 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::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* @@ -1485,13 +1493,19 @@ void Room::processAccountDataEvent(EventPtr event) switch (event->type()) { case EventType::Tag: - d->tags.reset(static_cast(event.release())); + { + auto newTags = static_cast(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(); } } @@ -1644,7 +1658,7 @@ QJsonObject Room::Private::toJson() const } QJsonArray accountDataEvents; - if (!tags->empty()) + if (!tags.empty()) accountDataEvents.append(QMatrixClient::toJson(tags)); if (!accountData.empty()) diff --git a/room.h b/room.h index 71d5c433..bdd11452 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 -#include -#include -#include #include #include @@ -241,9 +237,11 @@ namespace QMatrixClient Q_INVOKABLE void resetHighlightCount(); QStringList tagNames() const; - QHash tags() const; + TagsMap tags() const; TagRecord tag(const QString& name) const; + 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 */ -- cgit v1.2.3 From f9cd6410623b7ddebf97e248584d1a8e838b4da8 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 5 Mar 2018 11:44:00 +0900 Subject: Room: addTag() and removeTag() Slightly changed TagRecord constructors to match. --- events/accountdataevents.h | 4 ++-- room.cpp | 20 ++++++++++++++++++++ room.h | 19 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) (limited to 'events') diff --git a/events/accountdataevents.h b/events/accountdataevents.h index 78cf9c46..f3ba27bb 100644 --- a/events/accountdataevents.h +++ b/events/accountdataevents.h @@ -30,8 +30,8 @@ namespace QMatrixClient struct TagRecord { - TagRecord (QString order) : order(std::move(order)) { } - explicit TagRecord(const QJsonValue& jv = {}) + TagRecord (QString order = {}) : order(std::move(order)) { } + explicit TagRecord(const QJsonValue& jv) : order(jv.toObject().value("order").toString()) { } diff --git a/room.cpp b/room.cpp index 86d1e6cd..cb94ddb6 100644 --- a/room.cpp +++ b/room.cpp @@ -586,6 +586,26 @@ TagRecord Room::tag(const QString& name) const 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) diff --git a/room.h b/room.h index bdd11452..0eb5ecc3 100644 --- a/room.h +++ b/room.h @@ -240,6 +240,25 @@ namespace QMatrixClient 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 */ -- cgit v1.2.3 From 63efbe26f37819048bb236d4839cc5f25c102785 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 5 Mar 2018 21:06:16 +0900 Subject: Support server-side read marker (m.full_read) Closes #183. There's also the m.read part but it can be done sometime later, as it's pure optimisation. --- events/receiptevent.cpp | 6 ---- events/receiptevent.h | 2 -- jobs/postreadmarkersjob.h | 37 ++++++++++++++++++++ libqmatrixclient.pri | 3 +- room.cpp | 87 +++++++++++++++++++++++++---------------------- 5 files changed, 86 insertions(+), 49 deletions(-) create mode 100644 jobs/postreadmarkersjob.h (limited to 'events') diff --git a/events/receiptevent.cpp b/events/receiptevent.cpp index 3c4d34ee..7555db82 100644 --- a/events/receiptevent.cpp +++ b/events/receiptevent.cpp @@ -66,11 +66,5 @@ ReceiptEvent::ReceiptEvent(const QJsonObject& obj) } _eventsWithReceipts.push_back({eventIt.key(), std::move(receipts)}); } - static const auto UnreadMsgsKey = - QStringLiteral("x-qmatrixclient.unread_messages"); - if (contents.contains(UnreadMsgsKey)) - _unreadMessages = contents["x-qmatrixclient.unread_messages"].toBool(); - else - _unreadMessages = obj["x-qmatrixclient.unread_messages"].toBool(); } diff --git a/events/receiptevent.h b/events/receiptevent.h index 92dace82..5b99ae3f 100644 --- a/events/receiptevent.h +++ b/events/receiptevent.h @@ -41,12 +41,10 @@ namespace QMatrixClient EventsWithReceipts eventsWithReceipts() const { return _eventsWithReceipts; } - bool unreadMessages() const { return _unreadMessages; } static constexpr const char* const TypeId = "m.receipt"; private: EventsWithReceipts _eventsWithReceipts; - bool _unreadMessages; // Spec extension for caching purposes }; } // namespace QMatrixClient diff --git a/jobs/postreadmarkersjob.h b/jobs/postreadmarkersjob.h new file mode 100644 index 00000000..d0198821 --- /dev/null +++ b/jobs/postreadmarkersjob.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * 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 "basejob.h" + +using namespace QMatrixClient; + +class PostReadMarkersJob : public BaseJob +{ + public: + explicit PostReadMarkersJob(const QString& roomId, + const QString& readUpToEventId) + : BaseJob(HttpVerb::Post, "PostReadMarkersJob", + QStringLiteral("_matrix/client/r0/rooms/%1/read_markers") + .arg(roomId)) + { + setRequestData(QJsonObject {{ + QStringLiteral("m.fully_read"), readUpToEventId }}); + } +}; diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index c7b95617..74e9d8c7 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -42,7 +42,8 @@ HEADERS += \ $$PWD/settings.h \ $$PWD/networksettings.h \ $$PWD/networkaccessmanager.h \ - $$PWD/jobs/downloadfilejob.h + $$PWD/jobs/downloadfilejob.h \ + $$PWD/jobs/postreadmarkersjob.h SOURCES += \ $$PWD/connectiondata.cpp \ diff --git a/room.cpp b/room.cpp index 4f818473..beeca6e3 100644 --- a/room.cpp +++ b/room.cpp @@ -36,6 +36,7 @@ #include "jobs/roommessagesjob.h" #include "jobs/mediathumbnailjob.h" #include "jobs/downloadfilejob.h" +#include "jobs/postreadmarkersjob.h" #include "avatar.h" #include "connection.h" #include "user.h" @@ -105,6 +106,7 @@ class Room::Private QString firstDisplayedEventId; QString lastDisplayedEventId; QHash lastReadEventIds; + QString serverReadMarker; TagsMap tags; QHash accountData; QString prevBatch; @@ -202,11 +204,12 @@ class Room::Private */ void processRedaction(RoomEventPtr redactionEvent); - template - SetAccountDataPerRoomJob* setAccountData(const EvT& event) + void broadcastTagUpdates() { - return connection->callApi( - connection->userId(), id, EvT::typeId(), event.toJson()); + connection->callApi( + connection->userId(), id, TagEvent::typeId(), + TagEvent(tags).toJson()); + emit q->tagsChanged(); } QJsonObject toJson() const; @@ -347,7 +350,11 @@ void Room::Private::setLastReadEvent(User* u, const QString& eventId) storedId = eventId; emit q->lastReadEventChanged(u); if (isLocalUser(u)) + { + if (eventId != serverReadMarker) + connection->callApi(id, eventId); emit q->readMarkerMoved(); + } } Room::Private::rev_iter_pair_t @@ -402,9 +409,8 @@ void Room::Private::markMessagesAsRead(Room::rev_iter_t upToMarker) { if ((*markers.second)->senderId() != q->localUser()->id()) { - auto eventId = (*markers.second)->id(); - connection->callApi(id, "m.read", eventId); - setAccountData(ReadMarkerEvent(eventId)); + connection->callApi(id, "m.read", + (*markers.second)->id()); break; } } @@ -593,8 +599,7 @@ void Room::addTag(const QString& name, const TagRecord& record) return; d->tags.insert(name, record); - d->setAccountData(TagEvent(d->tags)); - emit tagsChanged(); + d->broadcastTagUpdates(); } void Room::removeTag(const QString& name) @@ -603,8 +608,7 @@ void Room::removeTag(const QString& name) return; d->tags.remove(name); - d->setAccountData(TagEvent(d->tags)); - emit tagsChanged(); + d->broadcastTagUpdates(); } void Room::setTags(const TagsMap& newTags) @@ -612,8 +616,7 @@ void Room::setTags(const TagsMap& newTags) if (newTags == d->tags) return; d->tags = newTags; - d->setAccountData(TagEvent(d->tags)); - emit tagsChanged(); + d->broadcastTagUpdates(); } bool Room::isFavourite() const @@ -915,10 +918,13 @@ void Room::updateData(SyncRoomData&& data) d->prevBatch = data.timelinePrevBatch; setJoinState(data.joinState); - QElapsedTimer et; + QElapsedTimer et; et.start(); + for (auto&& event: data.accountData) + processAccountDataEvent(move(event)); + if (!data.state.empty()) { - et.start(); + et.restart(); processStateEvents(data.state); qCDebug(PROFILER) << "*** Room::processStateEvents(state):" << data.state.size() << "event(s)," << et; @@ -938,9 +944,6 @@ void Room::updateData(SyncRoomData&& data) for( auto&& ephemeralEvent: data.ephemeral ) processEphemeralEvent(move(ephemeralEvent)); - for (auto&& event: data.accountData) - processAccountDataEvent(move(event)); - if( data.highlightCount != d->highlightCount ) { d->highlightCount = data.highlightCount; @@ -1499,8 +1502,6 @@ void Room::processEphemeralEvent(EventPtr event) qCDebug(PROFILER) << "*** Room::processEphemeralEvent(receipts):" << receiptEvent->eventsWithReceipts().size() << "events with receipts," << et; - if (receiptEvent->unreadMessages()) - d->unreadMessages = true; break; } default: @@ -1524,6 +1525,20 @@ void Room::processAccountDataEvent(EventPtr event) emit tagsChanged(); break; } + case EventType::ReadMarker: + { + const auto* rmEvent = static_cast(event.get()); + const auto& readEventId = rmEvent->event_id(); + qCDebug(MAIN) << "Server-side read marker at " << readEventId; + static const auto UnreadMsgsKey = + QStringLiteral("x-qmatrixclient.unread_messages"); + if (rmEvent->contentJson().contains(UnreadMsgsKey)) + d->unreadMessages = + rmEvent->contentJson().value(UnreadMsgsKey).toBool(); + d->serverReadMarker = readEventId; + markMessagesAsRead(readEventId); + break; + } default: d->accountData[event->jsonType()] = event->contentJson().toVariantHash(); @@ -1659,32 +1674,24 @@ QJsonObject Room::Private::toJson() const QJsonObject {{ QStringLiteral("events"), stateEvents }}); } - if (!q->readMarkerEventId().isEmpty()) - { - result.insert(QStringLiteral("ephemeral"), - QJsonObject {{ QStringLiteral("events"), - QJsonArray { QJsonObject( - { { QStringLiteral("type"), QStringLiteral("m.receipt") } - , { QStringLiteral("content"), QJsonObject( - { { q->readMarkerEventId(), - QJsonObject {{ QStringLiteral("m.read"), - QJsonObject {{ connection->userId(), {} }} }} - } - , { QStringLiteral("x-qmatrixclient.unread_messages"), - unreadMessages } - }) } - } - ) } - }}); - } - QJsonArray accountDataEvents; if (!tags.empty()) accountDataEvents.append(QJsonObject( - { { QStringLiteral("type"), QStringLiteral("m.tag") } + { { QStringLiteral("type"), TagEvent::typeId() } , { QStringLiteral("content"), TagEvent(tags).toJson() } })); + if (!serverReadMarker.isEmpty()) + { + auto contentJson = ReadMarkerEvent(serverReadMarker).toJson(); + contentJson.insert(QStringLiteral("x-qmatrixclient.unread_messages"), + unreadMessages); + accountDataEvents.append(QJsonObject( + { { QStringLiteral("type"), ReadMarkerEvent::typeId() } + , { QStringLiteral("content"), contentJson } + })); + } + if (!accountData.empty()) { for (auto it = accountData.begin(); it != accountData.end(); ++it) -- cgit v1.2.3 From 4e31b0371cea6441dc4281aaa28a9b693a751454 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 27 Feb 2018 11:40:03 +0900 Subject: MemberEventContent/RoomMemberEvent: parse and expose is_direct --- events/roommemberevent.cpp | 1 + events/roommemberevent.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'events') diff --git a/events/roommemberevent.cpp b/events/roommemberevent.cpp index a9e301a4..76b003c2 100644 --- a/events/roommemberevent.cpp +++ b/events/roommemberevent.cpp @@ -51,6 +51,7 @@ namespace QMatrixClient MemberEventContent::MemberEventContent(const QJsonObject& json) : membership(fromJson(json["membership"])) + , isDirect(json["is_direct"].toBool()) , displayName(json["displayname"].toString()) , avatarUrl(json["avatar_url"].toString()) { } diff --git a/events/roommemberevent.h b/events/roommemberevent.h index b9ff0d70..89b970c9 100644 --- a/events/roommemberevent.h +++ b/events/roommemberevent.h @@ -38,6 +38,7 @@ namespace QMatrixClient explicit MemberEventContent(const QJsonObject& json); MembershipType membership; + bool isDirect = false; QString displayName; QUrl avatarUrl; @@ -66,6 +67,7 @@ namespace QMatrixClient MembershipType membership() const { return content().membership; } QString userId() const { return originalJsonObject().value("state_key").toString(); } + bool isDirect() const { return content().isDirect; } QString displayName() const { return content().displayName; } QUrl avatarUrl() const { return content().avatarUrl; } -- cgit v1.2.3 From 962f7f4beb192810afe82bfd4b68613a96a69063 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 27 Feb 2018 11:41:09 +0900 Subject: Introduce DirectChatEvent (parse only, no processing yet) --- CMakeLists.txt | 1 + events/directchatevent.cpp | 36 ++++++++++++++++++++++++++++++++++++ events/directchatevent.h | 34 ++++++++++++++++++++++++++++++++++ events/event.cpp | 3 ++- libqmatrixclient.pri | 2 ++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 events/directchatevent.cpp create mode 100644 events/directchatevent.h (limited to 'events') diff --git a/CMakeLists.txt b/CMakeLists.txt index e95c72d0..82ab2b55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ set(libqmatrixclient_SRCS events/roomavatarevent.cpp events/typingevent.cpp events/receiptevent.cpp + events/directchatevent.cpp jobs/requestdata.cpp jobs/basejob.cpp jobs/checkauthmethods.cpp diff --git a/events/directchatevent.cpp b/events/directchatevent.cpp new file mode 100644 index 00000000..7049d967 --- /dev/null +++ b/events/directchatevent.cpp @@ -0,0 +1,36 @@ +/****************************************************************************** + * Copyright (C) 2018 Kitsune Ral + * + * 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 "directchatevent.h" + +#include "converters.h" + +using namespace QMatrixClient; + +DirectChatEvent::DirectChatEvent(const QJsonObject& obj) + : Event(Type::DirectChat, obj) +{ } + +QMultiHash DirectChatEvent::usersToDirectChats() const +{ + QMultiHash result; + for (auto it = contentJson().begin(); it != contentJson().end(); ++it) + for (auto roomIdValue: it.value().toArray()) + result.insert(it.key(), roomIdValue.toString()); + return result; +} diff --git a/events/directchatevent.h b/events/directchatevent.h new file mode 100644 index 00000000..2b0ad0a0 --- /dev/null +++ b/events/directchatevent.h @@ -0,0 +1,34 @@ +/****************************************************************************** + * Copyright (C) 2018 Kitsune Ral + * + * 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 +{ + class DirectChatEvent : public Event + { + public: + explicit DirectChatEvent(const QJsonObject& obj); + + QMultiHash usersToDirectChats() const; + + static constexpr const char * TypeId = "m.direct"; + }; +} diff --git a/events/event.cpp b/events/event.cpp index f3e965e2..8ddf3945 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -25,6 +25,7 @@ #include "typingevent.h" #include "receiptevent.h" #include "accountdataevents.h" +#include "directchatevent.h" #include "redactionevent.h" #include "logging.h" @@ -88,7 +89,7 @@ EventPtr _impl::doMakeEvent(const QJsonObject& obj) return EventPtr(move(e)); return EventPtr { makeIfMatches( + TypingEvent, ReceiptEvent, TagEvent, ReadMarkerEvent, DirectChatEvent>( obj, obj["type"].toString()) }; } diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 74e9d8c7..144c9dbc 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -25,6 +25,7 @@ HEADERS += \ $$PWD/events/typingevent.h \ $$PWD/events/receiptevent.h \ $$PWD/events/accountdataevents.h \ + $$PWD/events/directchatevent.h \ $$PWD/events/redactionevent.h \ $$PWD/jobs/requestdata.h \ $$PWD/jobs/basejob.h \ @@ -57,6 +58,7 @@ SOURCES += \ $$PWD/events/roommemberevent.cpp \ $$PWD/events/typingevent.cpp \ $$PWD/events/receiptevent.cpp \ + $$PWD/events/directchatevent.cpp \ $$PWD/jobs/requestdata.cpp \ $$PWD/jobs/basejob.cpp \ $$PWD/jobs/checkauthmethods.cpp \ -- cgit v1.2.3