diff options
-rw-r--r-- | examples/qmc-example.cpp | 19 | ||||
-rw-r--r-- | lib/connection.cpp | 81 | ||||
-rw-r--r-- | lib/connection.h | 11 |
3 files changed, 80 insertions, 31 deletions
diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index 23a1bff1..7226c060 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -25,9 +25,11 @@ class QMCTest : public QObject void doTests(); void addAndRemoveTag(); void sendAndRedact(); - void checkRedactionOutcome(QString evtIdToRedact, RoomEventsRange events); + void checkRedactionOutcome(QString evtIdToRedact, + RoomEventsRange events); void markDirectChat(); - void checkDirectChatOutcome(); + void checkDirectChatOutcome( + const Connection::DirectChatsMap& added); void finalize(); private: @@ -211,11 +213,11 @@ void QMCTest::checkRedactionOutcome(QString evtIdToRedact, void QMCTest::markDirectChat() { - if (c->isDirectChat(targetRoom->id())) + if (targetRoom->directChatUsers().contains(c->user())) { cout << "Warning: the room is already a direct chat," " only unmarking will be tested" << endl; - checkDirectChatOutcome(); + checkDirectChatOutcome({{ c->user(), targetRoom->id() }}); } // Connect first because the signal is emitted synchronously. connect(c.data(), &Connection::directChatsListChanged, @@ -224,11 +226,18 @@ void QMCTest::markDirectChat() c->addToDirectChats(targetRoom, c->user()); } -void QMCTest::checkDirectChatOutcome() +void QMCTest::checkDirectChatOutcome(const Connection::DirectChatsMap& added) { disconnect(c.data(), &Connection::directChatsListChanged, nullptr, nullptr); if (!c->isDirectChat(targetRoom->id())) { + cout << "The room has not been marked as a direct chat" << endl; + QMC_CHECK("Direct chat test", false); + return; + } + if (!added.contains(c->user(), targetRoom->id())) + { + cout << "The room has not been listed in new direct chats" << endl; QMC_CHECK("Direct chat test", false); return; } diff --git a/lib/connection.cpp b/lib/connection.cpp index 2d7235b9..a5c74d88 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -47,7 +47,22 @@ using namespace QMatrixClient; -using DirectChatsMap = QMultiHash<const User*, QString>; +// This is very much Qt-specific; STL iterators don't have key() and value() +template <typename HashT, typename Pred> +HashT erase_if(HashT& hashMap, Pred pred) +{ + HashT removals; + for (auto it = hashMap.begin(); it != hashMap.end();) + { + if (pred(it)) + { + removals.insert(it.key(), it.value()); + it = hashMap.erase(it); + } else + ++it; + } + return removals; +} class Connection::Private { @@ -80,7 +95,8 @@ class Connection::Private void connectWithToken(const QString& user, const QString& accessToken, const QString& deviceId); - void broadcastDirectChatUpdates(); + void broadcastDirectChatUpdates(const DirectChatsMap& additions, + const DirectChatsMap& removals); }; Connection::Connection(const QUrl& server, QObject* parent) @@ -290,20 +306,32 @@ void Connection::onSyncSuccess(SyncData &&data) { { if (accountEvent->type() == EventType::DirectChat) { - DirectChatsMap newDirectChats; const auto* event = static_cast<DirectChatEvent*>(accountEvent.get()); auto usersToDCs = event->usersToDirectChats(); + DirectChatsMap removals = + erase_if(d->directChats, [&usersToDCs] (auto it) { + return !usersToDCs.contains(it.key()->id(), it.value()); + }); + if (MAIN().isDebugEnabled()) + for (auto it = removals.begin(); it != removals.end(); ++it) + qCDebug(MAIN) << it.value() + << "is no more a direct chat with" << it.key()->id(); + + DirectChatsMap additions; for (auto it = usersToDCs.begin(); it != usersToDCs.end(); ++it) { - newDirectChats.insert(user(it.key()), it.value()); - qCDebug(MAIN) << "Marked room" << it.value() - << "as a direct chat with" << it.key(); - } - if (newDirectChats != d->directChats) - { - d->directChats = newDirectChats; - emit directChatsListChanged(); + const auto* u = user(it.key()); + if (!d->directChats.contains(u, it.value())) + { + additions.insert(u, it.value()); + d->directChats.insert(u, it.value()); + qCDebug(MAIN) << "Marked room" << it.value() + << "as a direct chat with" << u->id(); + } } + if (!additions.isEmpty() || !removals.isEmpty()) + emit directChatsListChanged(additions, removals); + continue; } d->accountData[accountEvent->jsonType()] = @@ -654,7 +682,12 @@ QVector<Room*> Connection::roomsWithTag(const QString& tagName) const return rooms; } -QJsonObject toJson(const DirectChatsMap& directChats) +Connection::DirectChatsMap Connection::directChats() const +{ + return d->directChats; +} + +QJsonObject toJson(const Connection::DirectChatsMap& directChats) { QJsonObject json; for (auto it = directChats.keyBegin(); it != directChats.keyEnd(); ++it) @@ -662,11 +695,12 @@ QJsonObject toJson(const DirectChatsMap& directChats) return json; } -void Connection::Private::broadcastDirectChatUpdates() +void Connection::Private::broadcastDirectChatUpdates(const DirectChatsMap& additions, + const DirectChatsMap& removals) { q->callApi<SetAccountDataJob>(userId, QStringLiteral("m.direct"), toJson(directChats)); - emit q->directChatsListChanged(); + emit q->directChatsListChanged(additions, removals); } void Connection::addToDirectChats(const Room* room, const User* user) @@ -675,7 +709,8 @@ void Connection::addToDirectChats(const Room* room, const User* user) if (d->directChats.contains(user, room->id())) return; d->directChats.insert(user, room->id()); - d->broadcastDirectChatUpdates(); + DirectChatsMap additions { { user, room->id() } }; + d->broadcastDirectChatUpdates(additions, {}); } void Connection::removeFromDirectChats(const QString& roomId, const User* user) @@ -684,17 +719,17 @@ void Connection::removeFromDirectChats(const QString& roomId, const User* user) if ((user != nullptr && !d->directChats.contains(user, roomId)) || d->directChats.key(roomId) == nullptr) return; + + DirectChatsMap removals; if (user != nullptr) + { + removals.insert(user, roomId); d->directChats.remove(user, roomId); + } else - for (auto it = d->directChats.begin(); it != d->directChats.end();) - { - if (it.value() == roomId) - it = d->directChats.erase(it); - else - ++it; - } - d->broadcastDirectChatUpdates(); + removals = erase_if(d->directChats, + [&roomId] (auto it) { return it.value() == roomId; }); + d->broadcastDirectChatUpdates({}, removals); } bool Connection::isDirectChat(const QString& roomId) const diff --git a/lib/connection.h b/lib/connection.h index c6d543ec..016c7e3c 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -64,6 +64,8 @@ namespace QMatrixClient using user_factory_t = std::function<User*(Connection*, const QString&)>; + using DirectChatsMap = QMultiHash<const User*, QString>; + enum RoomVisibility { PublishRoom, UnpublishRoom }; // FIXME: Should go inside CreateRoomJob explicit Connection(QObject* parent = nullptr); @@ -114,16 +116,18 @@ namespace QMatrixClient /** Check whether the room id corresponds to a direct chat */ bool isDirectChat(const QString& roomId) const; + /** Get the whole map from users to direct chat rooms */ + DirectChatsMap directChats() const; + /** Retrieve the list of users the room is a direct chat with * @return The list of users for which this room is marked as * a direct chat; an empty list if the room is not a direct chat */ QList<const User*> directChatUsers(const Room* room) const; + /** Get the full list of users known to this account */ QMap<QString, User*> users() const; - // FIXME: Convert Q_INVOKABLEs to Q_PROPERTIES - // (breaks back-compatibility) QUrl homeserver() const; Q_INVOKABLE Room* room(const QString& roomId, JoinStates states = JoinState::Invite|JoinState::Join) const; @@ -421,7 +425,8 @@ namespace QMatrixClient * to direct chat rooms is changed (because of either local updates * or a different list arrived from the server). */ - void directChatsListChanged(); + void directChatsListChanged(DirectChatsMap additions, + DirectChatsMap removals); void cacheStateChanged(); |