diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | connection.cpp | 156 | ||||
-rw-r--r-- | connection.h | 49 | ||||
-rw-r--r-- | converters.h | 18 | ||||
-rw-r--r-- | events/directchatevent.cpp | 36 | ||||
-rw-r--r-- | events/directchatevent.h | 34 | ||||
-rw-r--r-- | events/event.cpp | 3 | ||||
-rw-r--r-- | events/roommemberevent.cpp | 1 | ||||
-rw-r--r-- | events/roommemberevent.h | 2 | ||||
-rw-r--r-- | examples/qmc-example.cpp | 78 | ||||
-rw-r--r-- | libqmatrixclient.pri | 2 | ||||
-rw-r--r-- | room.cpp | 5 | ||||
-rw-r--r-- | user.cpp | 6 | ||||
-rw-r--r-- | user.h | 1 |
14 files changed, 346 insertions, 46 deletions
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/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<const User*, QString>; + class Connection::Private { public: @@ -64,6 +68,8 @@ class Connection::Private QHash<QPair<QString, bool>, Room*> roomMap; QVector<QString> roomIdsToForget; QMap<QString, User*> userMap; + DirectChatsMap directChats; + QHash<QString, QVariantHash> 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<DirectChatEvent*>(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<Room*> 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<SetAccountDataJob>(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<QString, User*> 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); diff --git a/connection.h b/connection.h index 1e9df5e2..e046d4a0 100644 --- a/connection.h +++ b/connection.h @@ -89,12 +89,26 @@ namespace QMatrixClient /** Get the list of rooms with the specified tag */ QVector<Room*> roomsWithTag(const QString& tagName) const; + /** Mark the room as a direct chat with the user */ + void addToDirectChats(const Room* room, const User* user); + + /** Unmark the room from direct chats + * This function removes the room from direct chats either for + * a specific \p user or for all users if \p user in nullptr. + */ + void removeFromDirectChats(const Room* room, + const User* user = nullptr); + + /** Check whether the room is a direct chat */ + bool isDirectChat(const Room* room) const; + QMap<QString, User*> users() const; // FIXME: Convert Q_INVOKABLEs to Q_PROPERTIES // (breaks back-compatibility) QUrl homeserver() const; Q_INVOKABLE User* user(const QString& userId); + const User* user() const; User* user(); QString userId() const; QString deviceId() const; @@ -223,7 +237,21 @@ namespace QMatrixClient const QVector<CreateRoomJob::Invite3pid>& invite3pids = {}, const QJsonObject creationContent = {}); - /** Create a direct chat with a single user, optional name and topic */ + /** Get a direct chat with a single user + * This method may return synchronously or asynchoronously depending + * on whether a direct chat room with the respective person exists + * already. + * + * \sa directChatAvailable + */ + Q_INVOKABLE void requestDirectChat(const QString& userId); + + /** Create a direct chat with a single user, optional name and topic + * A room will always be created, unlike in requestDirectChat. + * It is advised to use requestDirectChat as a default way of getting + * one-on-one with a person, and only use createDirectChat when + * a new creation is explicitly desired. + */ CreateRoomJob* createDirectChat(const QString& userId, const QString& topic = {}, const QString& name = {}); @@ -341,13 +369,30 @@ namespace QMatrixClient /** The room object is about to be deleted */ void aboutToDeleteRoom(Room* room); - /** The room has just been created by createRoom or createDirectChat + /** The room has just been created by createRoom or requestDirectChat + * * This signal is not emitted in usual room state transitions, * only as an outcome of room creation operations invoked by * the client. + * \note requestDirectChat doesn't necessarily create a new chat; + * use directChatAvailable signal if you just need to obtain + * a direct chat room. */ void createdRoom(Room* room); + /** The direct chat room is ready for using + * This signal is emitted upon any successful outcome from + * requestDirectChat. + */ + void directChatAvailable(Room* directChat); + + /** The list of direct chats has changed + * This signal is emitted every time when the mapping of users + * to direct chat rooms is changed (because of either local updates + * or a different list arrived from the server). + */ + void directChatsListChanged(); + void cacheStateChanged(); protected: diff --git a/converters.h b/converters.h index 96efe5f8..bba298e0 100644 --- a/converters.h +++ b/converters.h @@ -24,14 +24,16 @@ namespace QMatrixClient { - template <typename T> - inline QJsonValue toJson(T&& val) - { - return QJsonValue(std::forward<T>(val)); - } + // This catches anything implicitly convertible to QJsonValue/Object/Array + inline QJsonValue toJson(const QJsonValue& val) { return val; } + inline QJsonObject toJson(const QJsonObject& o) { return o; } + inline QJsonArray toJson(const QJsonArray& arr) { return arr; } +#ifdef _MSC_VER // MSVC gets lost and doesn't know which overload to use + inline QJsonValue toJson(const QString& s) { return s; } +#endif template <typename T> - inline QJsonValue toJson(const QVector<T>& vals) + inline QJsonArray toJson(const QVector<T>& vals) { QJsonArray ar; for (const auto& v: vals) @@ -39,7 +41,7 @@ namespace QMatrixClient return ar; } - inline QJsonValue toJson(const QStringList& strings) + inline QJsonArray toJson(const QStringList& strings) { return QJsonArray::fromStringList(strings); } @@ -50,7 +52,7 @@ namespace QMatrixClient } template <typename T> - inline QJsonValue toJson(const QHash<QString, T>& hashMap) + inline QJsonObject toJson(const QHash<QString, T>& hashMap) { QJsonObject json; for (auto it = hashMap.begin(); it != hashMap.end(); ++it) 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 <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 "directchatevent.h" + +#include "converters.h" + +using namespace QMatrixClient; + +DirectChatEvent::DirectChatEvent(const QJsonObject& obj) + : Event(Type::DirectChat, obj) +{ } + +QMultiHash<QString, QString> DirectChatEvent::usersToDirectChats() const +{ + QMultiHash<QString, QString> 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 <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 +{ + class DirectChatEvent : public Event + { + public: + explicit DirectChatEvent(const QJsonObject& obj); + + QMultiHash<QString, QString> 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<Event>(const QJsonObject& obj) return EventPtr(move(e)); return EventPtr { makeIfMatches<Event, - TypingEvent, ReceiptEvent, TagEvent, ReadMarkerEvent>( + TypingEvent, ReceiptEvent, TagEvent, ReadMarkerEvent, DirectChatEvent>( obj, obj["type"].toString()) }; } 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<MembershipType>(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; } diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index 513e7efa..5ea91856 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -26,6 +26,8 @@ class QMCTest : public QObject void addAndRemoveTag(); void sendAndRedact(); void checkRedactionOutcome(QString evtIdToRedact, RoomEventsRange events); + void markDirectChat(); + void checkDirectChatOutcome(); void finalize(); private: @@ -37,11 +39,15 @@ class QMCTest : public QObject }; #define QMC_CHECK(description, condition) \ +{ \ cout << (description) \ << (!!(condition) ? " successul" : " FAILED") << endl; \ targetRoom->postMessage(origin % ": " % QStringLiteral(description) % \ (!!(condition) ? QStringLiteral(" successful") : \ - QStringLiteral(" FAILED")), MessageEventType::Notice) + QStringLiteral(" FAILED")), \ + !!(condition) ? MessageEventType::Notice : MessageEventType::Text); \ + --semaphor; \ +} QMCTest::QMCTest(Connection* conn, const QString& testRoomName, QString source) : c(conn), origin(std::move(source)) @@ -56,25 +62,15 @@ QMCTest::QMCTest(Connection* conn, const QString& testRoomName, QString source) connect(c.data(), &Connection::syncDone, c.data(), [this] { cout << "Sync complete, " << semaphor << " tests in the air" << endl; if (semaphor) - { -// if (targetRoom) -// targetRoom->postMessage( -// QString("%1: sync done, %2 test(s) in the air") -// .arg(origin).arg(semaphor), -// MessageEventType::Notice); c->sync(10000); - } - else + else if (targetRoom) { - if (targetRoom) - { - auto j = c->callApi<SendEventJob>(targetRoom->id(), - RoomMessageEvent(origin % ": All tests finished")); - connect(j, &BaseJob::finished, this, &QMCTest::finalize); - } - else - finalize(); + auto j = c->callApi<SendEventJob>(targetRoom->id(), + RoomMessageEvent(origin % ": All tests finished")); + connect(j, &BaseJob::finished, this, &QMCTest::finalize); } + else + finalize(); }); // Big countdown watchdog QTimer::singleShot(180000, this, &QMCTest::finalize); @@ -118,13 +114,13 @@ void QMCTest::onNewRoom(Room* r, const QString& testRoomName) void QMCTest::doTests() { - addAndRemoveTag(); - sendAndRedact(); + ++semaphor; addAndRemoveTag(); + ++semaphor; sendAndRedact(); + ++semaphor; markDirectChat(); } void QMCTest::addAndRemoveTag() { - ++semaphor; static const auto TestTag = QStringLiteral("org.qmatrixclient.test"); // Pre-requisite if (targetRoom->tags().contains(TestTag)) @@ -139,7 +135,6 @@ void QMCTest::addAndRemoveTag() cout << "Test tag set, removing it now" << endl; targetRoom->removeTag(TestTag); QMC_CHECK("Tagging test", !targetRoom->tags().contains(TestTag)); - --semaphor; QObject::disconnect(targetRoom, &Room::tagsChanged, nullptr, nullptr); } }); @@ -150,7 +145,6 @@ void QMCTest::addAndRemoveTag() void QMCTest::sendAndRedact() { - ++semaphor; cout << "Sending a message to redact" << endl; auto* job = targetRoom->connection()->callApi<SendEventJob>(targetRoom->id(), RoomMessageEvent(origin % ": Message to redact")); @@ -190,7 +184,6 @@ void QMCTest::checkRedactionOutcome(QString evtIdToRedact, } cout << "The sync brought already redacted message" << endl; QMC_CHECK("Redaction", true); - --semaphor; // Not disconnecting because there are other connections from this class // to aboutToAddNewMessages checkSucceeded = true; @@ -210,13 +203,50 @@ void QMCTest::checkRedactionOutcome(QString evtIdToRedact, QMC_CHECK("Redaction", oldEvent->id() == evtIdToRedact && newEvent->isRedacted() && newEvent->redactionReason() == origin); - --semaphor; checkSucceeded = true; disconnect(targetRoom, &Room::replacedEvent, nullptr, nullptr); }); } +void QMCTest::markDirectChat() +{ + if (c->isDirectChat(targetRoom)) + { + cout << "Warning: the room is already a direct chat," + " only unmarking will be tested" << endl; + checkDirectChatOutcome(); + } + cout << "Marking the room as a direct chat" << endl; + c->addToDirectChats(targetRoom, c->user()); + connect(c.data(), &Connection::directChatsListChanged, + this, &QMCTest::checkDirectChatOutcome); +} + +void QMCTest::checkDirectChatOutcome() +{ + if (!c->isDirectChat(targetRoom)) + { + cout << "Room not (yet?) added to direct chats, waiting" << endl; + return; + } + + cout << "Room marked as a direct chat, unmarking now" << endl; + disconnect(c.data(), &Connection::directChatsListChanged, nullptr, nullptr); + c->removeFromDirectChats(targetRoom, c->user()); + connect(c.data(), &Connection::directChatsListChanged, this, [this] { + if (c->isDirectChat(targetRoom)) + { + cout << "Room not (yet?) removed from direct chats, waiting" << endl; + return; + } + + QMC_CHECK("Direct chat test", !c->isDirectChat(targetRoom)); + disconnect(c.data(), &Connection::directChatsListChanged, + nullptr, nullptr); + }); +} + void QMCTest::finalize() { if (semaphor) 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 \ @@ -1424,6 +1424,11 @@ void Room::processStateEvents(const RoomEvents& events) auto memberEvent = static_cast<RoomMemberEvent*>(event); auto u = user(memberEvent->userId()); u->processEvent(memberEvent, this); + if (u == localUser() && memberJoinState(u) == JoinState::Invite + && memberEvent->isDirect()) + connection()->addToDirectChats(this, + user(memberEvent->senderId())); + if( memberEvent->membership() == MembershipType::Join ) { if (memberJoinState(u) != JoinState::Join) @@ -287,6 +287,12 @@ bool User::setAvatar(QIODevice* source) std::bind(&Private::setAvatarOnServer, d.data(), _1, this)); } +void User::requestDirectChat() +{ + Q_ASSERT(d->connection); + d->connection->requestDirectChat(d->userId); +} + void User::Private::setAvatarOnServer(QString contentUri, User* q) { auto* j = connection->callApi<SetAvatarUrlJob>(userId, contentUri); @@ -101,6 +101,7 @@ namespace QMatrixClient void rename(const QString& newName, const Room* r); bool setAvatar(const QString& fileName); bool setAvatar(QIODevice* source); + void requestDirectChat(); signals: void nameAboutToChange(QString newName, QString oldName, |