From f7ee38b6c1d6c809feddd91647d83e6c6fdd3837 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 23 Mar 2018 15:40:54 +0900 Subject: Dealing with direct chats Receiving and caching m.direct (and other non-room account data along the way); Connection::addToDirectChats/removeFromDirectChats (might get siblings in Room eventually but not now), Connection/User::requestDirectChat. Closes #163. --- connection.cpp | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 11 deletions(-) (limited to 'connection.cpp') diff --git a/connection.cpp b/connection.cpp index b3f1ceb8..fb946392 100644 --- a/connection.cpp +++ b/connection.cpp @@ -20,12 +20,14 @@ #include "connectiondata.h" #include "user.h" #include "events/event.h" +#include "events/directchatevent.h" #include "room.h" #include "settings.h" #include "jobs/generated/login.h" #include "jobs/generated/logout.h" #include "jobs/generated/receipts.h" #include "jobs/generated/leaving.h" +#include "jobs/generated/account-data.h" #include "jobs/sendeventjob.h" #include "jobs/joinroomjob.h" #include "jobs/roommessagesjob.h" @@ -45,6 +47,8 @@ using namespace QMatrixClient; +using DirectChatsMap = QMultiHash; + class Connection::Private { public: @@ -64,6 +68,8 @@ class Connection::Private QHash, Room*> roomMap; QVector roomIdsToForget; QMap userMap; + DirectChatsMap directChats; + QHash accountData; QString userId; SyncJob* syncJob = nullptr; @@ -74,6 +80,7 @@ class Connection::Private void connectWithToken(const QString& user, const QString& accessToken, const QString& deviceId); + void applyDirectChatUpdates(const DirectChatsMap& newMap); }; Connection::Connection(const QUrl& server, QObject* parent) @@ -258,7 +265,7 @@ void Connection::sync(int timeout) void Connection::onSyncSuccess(SyncData &&data) { d->data->setLastEvent(data.nextBatch()); - for( auto&& roomData: data.takeRoomData() ) + for (auto&& roomData: data.takeRoomData()) { const auto forgetIdx = d->roomIdsToForget.indexOf(roomData.roomId); if (forgetIdx != -1) @@ -279,7 +286,29 @@ void Connection::onSyncSuccess(SyncData &&data) { r->updateData(std::move(roomData)); QCoreApplication::processEvents(); } - + for (auto&& accountEvent: data.takeAccountData()) + { + if (accountEvent->type() == EventType::DirectChat) + { + DirectChatsMap newDirectChats; + const auto* event = static_cast(accountEvent.get()); + auto usersToDCs = event->usersToDirectChats(); + 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(); + } + continue; + } + d->accountData[accountEvent->jsonType()] = + accountEvent->contentJson().toVariantHash(); + } } void Connection::stopSync() @@ -405,6 +434,42 @@ CreateRoomJob* Connection::createRoom(RoomVisibility visibility, return job; } +void Connection::requestDirectChat(const QString& userId) +{ + auto roomId = d->directChats.value(user(userId)); + if (roomId.isEmpty()) + { + auto j = createDirectChat(userId); + connect(j, &BaseJob::success, this, [this,j,userId,roomId] { + qCDebug(MAIN) << "Direct chat with" << userId + << "has been created as" << roomId; + emit directChatAvailable(roomMap().value({j->roomId(), false})); + }); + return; + } + + auto room = roomMap().value({roomId, false}, nullptr); + if (room) + { + Q_ASSERT(room->id() == roomId); + qCDebug(MAIN) << "Requested direct chat with" << userId + << "is already available as" << room->id(); + emit directChatAvailable(room); + return; + } + room = roomMap().value({roomId, true}, nullptr); + if (room) + { + Q_ASSERT(room->id() == roomId); + auto j = joinRoom(room->id()); + connect(j, &BaseJob::success, this, [this,j,roomId,userId] { + qCDebug(MAIN) << "Joined the already invited direct chat with" + << userId << "as" << roomId; + emit directChatAvailable(roomMap().value({roomId, false})); + }); + } +} + CreateRoomJob* Connection::createDirectChat(const QString& userId, const QString& topic, const QString& name) { @@ -470,11 +535,14 @@ User* Connection::user(const QString& userId) return user; } -User *Connection::user() +const User* Connection::user() const { - if( d->userId.isEmpty() ) - return nullptr; - return user(d->userId); + return d->userId.isEmpty() ? nullptr : d->userMap.value(d->userId, nullptr); +} + +User* Connection::user() +{ + return d->userId.isEmpty() ? nullptr : user(d->userId); } QString Connection::userId() const @@ -556,6 +624,56 @@ QVector Connection::roomsWithTag(const QString& tagName) const return rooms; } +QJsonObject toJson(const DirectChatsMap& directChats) +{ + QJsonObject json; + for (auto it = directChats.keyBegin(); it != directChats.keyEnd(); ++it) + json.insert((*it)->id(), toJson(directChats.values(*it))); + return json; +} + +void Connection::Private::applyDirectChatUpdates(const DirectChatsMap& newMap) +{ + auto j = q->callApi(userId, "m.direct", toJson(newMap)); + connect(j, &BaseJob::success, q, [this, newMap] { + if (directChats != newMap) + { + directChats = newMap; + emit q->directChatsListChanged(); + } + }); +} + +void Connection::addToDirectChats(const Room* room, const User* user) +{ + Q_ASSERT(room != nullptr && user != nullptr); + if (d->directChats.contains(user, room->id())) + return; + auto newMap = d->directChats; + newMap.insert(user, room->id()); + d->applyDirectChatUpdates(newMap); +} + +void Connection::removeFromDirectChats(const Room* room, const User* user) +{ + Q_ASSERT(room != nullptr); + if ((user != nullptr && !d->directChats.contains(user, room->id())) || + d->directChats.key(room->id()) == nullptr) + return; + DirectChatsMap newMap; + for (auto it = d->directChats.begin(); it != d->directChats.end(); ++it) + { + if (it.value() != room->id() || (user != nullptr && it.key() != user)) + newMap.insert(it.key(), it.value()); + } + d->applyDirectChatUpdates(newMap); +} + +bool Connection::isDirectChat(const Room* room) const +{ + return d->directChats.key(room->id()) != nullptr; +} + QMap Connection::users() const { return d->userMap; @@ -639,7 +757,7 @@ void Connection::setHomeserver(const QUrl& url) emit homeserverChanged(homeserver()); } -static constexpr int CACHE_VERSION_MAJOR = 5; +static constexpr int CACHE_VERSION_MAJOR = 6; static constexpr int CACHE_VERSION_MINOR = 0; void Connection::saveState(const QUrl &toFile) const @@ -665,7 +783,7 @@ void Connection::saveState(const QUrl &toFile) const return; } - QJsonObject roomObj; + QJsonObject rootObj; { QJsonObject rooms; QJsonObject inviteRooms; @@ -681,15 +799,31 @@ void Connection::saveState(const QUrl &toFile) const qCDebug(PROFILER) << "processEvents() borrowed" << et1; } + QJsonObject roomObj; if (!rooms.isEmpty()) roomObj.insert("join", rooms); if (!inviteRooms.isEmpty()) roomObj.insert("invite", inviteRooms); + + rootObj.insert("next_batch", d->data->lastEvent()); + rootObj.insert("rooms", roomObj); } + { + QJsonArray accountDataEvents { + QJsonObject { + { QStringLiteral("type"), QStringLiteral("m.direct") }, + { QStringLiteral("content"), toJson(d->directChats) } + } + }; - QJsonObject rootObj; - rootObj.insert("next_batch", d->data->lastEvent()); - rootObj.insert("rooms", roomObj); + for (auto it = d->accountData.begin(); it != d->accountData.end(); ++it) + accountDataEvents.append(QJsonObject { + {"type", it.key()}, + {"content", QJsonObject::fromVariantHash(it.value())} + }); + rootObj.insert("account_data", + QJsonObject {{ QStringLiteral("events"), accountDataEvents }}); + } QJsonObject versionObj; versionObj.insert("major", CACHE_VERSION_MAJOR); -- cgit v1.2.3