aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--connection.cpp156
-rw-r--r--connection.h49
-rw-r--r--converters.h18
-rw-r--r--events/directchatevent.cpp36
-rw-r--r--events/directchatevent.h34
-rw-r--r--events/event.cpp3
-rw-r--r--events/roommemberevent.cpp1
-rw-r--r--events/roommemberevent.h2
-rw-r--r--examples/qmc-example.cpp78
-rw-r--r--libqmatrixclient.pri2
-rw-r--r--room.cpp5
-rw-r--r--user.cpp6
-rw-r--r--user.h1
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 \
diff --git a/room.cpp b/room.cpp
index 6c8d762b..48c27ba0 100644
--- a/room.cpp
+++ b/room.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)
diff --git a/user.cpp b/user.cpp
index cfcb2f4d..7a6dbc73 100644
--- a/user.cpp
+++ b/user.cpp
@@ -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);
diff --git a/user.h b/user.h
index d19fa8f4..f76f9e0a 100644
--- a/user.h
+++ b/user.h
@@ -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,