aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.valgrind.qmc-example.supp12
-rw-r--r--connection.cpp115
-rw-r--r--connection.h31
-rw-r--r--examples/qmc-example.cpp31
4 files changed, 109 insertions, 80 deletions
diff --git a/.valgrind.qmc-example.supp b/.valgrind.qmc-example.supp
index a02f34ff..cb4e1e74 100644
--- a/.valgrind.qmc-example.supp
+++ b/.valgrind.qmc-example.supp
@@ -157,3 +157,15 @@
fun:_Znam
obj:/opt/qt56/lib/libQt5Network.so.5.6.3
}
+
+{
+ malloc_from_libcrypto
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:malloc
+ fun:CRYPTO_malloc
+ ...
+ obj:/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
+ ...
+ obj:/opt/qt56/lib/libQt5Network.so.5.6.3
+}
diff --git a/connection.cpp b/connection.cpp
index df9fd35f..db8bb085 100644
--- a/connection.cpp
+++ b/connection.cpp
@@ -80,7 +80,7 @@ class Connection::Private
void connectWithToken(const QString& user, const QString& accessToken,
const QString& deviceId);
- void applyDirectChatUpdates(const DirectChatsMap& newMap);
+ void broadcastDirectChatUpdates();
};
Connection::Connection(const QUrl& server, QObject* parent)
@@ -436,38 +436,45 @@ CreateRoomJob* Connection::createRoom(RoomVisibility visibility,
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;
- }
+ doInDirectChat(userId, [this] (Room* r) { emit directChatAvailable(r); });
+}
- 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)
+void Connection::doInDirectChat(const QString& userId,
+ std::function<void (Room*)> operation)
+{
+ // There can be more than one DC; find the first valid, and delete invalid
+ // (left/forgotten) ones along the way.
+ for (auto roomId: d->directChats.values(user(userId)))
{
- 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}));
- });
+ if (auto r = room(roomId, JoinState::Join))
+ {
+ Q_ASSERT(r->id() == roomId);
+ qCDebug(MAIN) << "Requested direct chat with" << userId
+ << "is already available as" << r->id();
+ operation(r);
+ return;
+ }
+ if (auto ir = invitation(roomId))
+ {
+ Q_ASSERT(ir->id() == roomId);
+ auto j = joinRoom(ir->id());
+ connect(j, &BaseJob::success, this, [this,roomId,userId,operation] {
+ qCDebug(MAIN) << "Joined the already invited direct chat with"
+ << userId << "as" << roomId;
+ operation(room(roomId, JoinState::Join));
+ });
+ }
+ qCWarning(MAIN) << "Direct chat with" << userId << "known as room"
+ << roomId << "is not valid, discarding it";
+ removeFromDirectChats(roomId);
}
+
+ auto j = createDirectChat(userId);
+ connect(j, &BaseJob::success, this, [this,j,userId,operation] {
+ qCDebug(MAIN) << "Direct chat with" << userId
+ << "has been created as" << j->roomId();
+ operation(room(j->roomId(), JoinState::Join));
+ });
}
CreateRoomJob* Connection::createDirectChat(const QString& userId,
@@ -655,16 +662,11 @@ QJsonObject toJson(const DirectChatsMap& directChats)
return json;
}
-void Connection::Private::applyDirectChatUpdates(const DirectChatsMap& newMap)
+void Connection::Private::broadcastDirectChatUpdates()
{
- 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();
- }
- });
+ q->callApi<SetAccountDataJob>(userId, QStringLiteral("m.direct"),
+ toJson(directChats));
+ emit q->directChatsListChanged();
}
void Connection::addToDirectChats(const Room* room, const User* user)
@@ -672,29 +674,32 @@ 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);
+ d->directChats.insert(user, room->id());
+ d->broadcastDirectChatUpdates();
}
-void Connection::removeFromDirectChats(const Room* room, const User* user)
+void Connection::removeFromDirectChats(const QString& roomId, const User* user)
{
- Q_ASSERT(room != nullptr);
- if ((user != nullptr && !d->directChats.contains(user, room->id())) ||
- d->directChats.key(room->id()) == nullptr)
+ Q_ASSERT(!roomId.isEmpty());
+ if ((user != nullptr && !d->directChats.contains(user, roomId)) ||
+ d->directChats.key(roomId) == 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);
+ if (user != nullptr)
+ 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();
}
-bool Connection::isDirectChat(const Room* room) const
+bool Connection::isDirectChat(const QString& roomId) const
{
- return d->directChats.key(room->id()) != nullptr;
+ return d->directChats.key(roomId) != nullptr;
}
QMap<QString, User*> Connection::users() const
diff --git a/connection.h b/connection.h
index 4497e200..7c11c32d 100644
--- a/connection.h
+++ b/connection.h
@@ -89,18 +89,30 @@ 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 */
+ /** Mark the room as a direct chat with the user
+ * This function marks \p room as a direct chat with \p user.
+ * Emits the signal synchronously, without waiting to complete
+ * synchronisation with the server.
+ *
+ * \sa directChatsListChanged
+ */
void addToDirectChats(const Room* room, const User* user);
/** Unmark the room from direct chats
- * This function removes the room from direct chats either for
+ * This function removes the room id from direct chats either for
* a specific \p user or for all users if \p user in nullptr.
+ * The room id is used to allow removal of, e.g., ids of forgotten
+ * rooms; a Room object need not exist. Emits the signal
+ * immediately, without waiting to complete synchronisation with
+ * the server.
+ *
+ * \sa directChatsListChanged
*/
- void removeFromDirectChats(const Room* room,
+ void removeFromDirectChats(const QString& roomId,
const User* user = nullptr);
- /** Check whether the room is a direct chat */
- bool isDirectChat(const Room* room) const;
+ /** Check whether the room id corresponds to a direct chat */
+ bool isDirectChat(const QString& roomId) const;
QMap<QString, User*> users() const;
@@ -249,6 +261,15 @@ namespace QMatrixClient
*/
Q_INVOKABLE void requestDirectChat(const QString& userId);
+ /** Run an operation in a direct chat with the user
+ * This method may return synchronously or asynchoronously depending
+ * on whether a direct chat room with the respective person exists
+ * already. Instead of emitting a signal it executes the passed
+ * function object with the direct chat room as its parameter.
+ */
+ Q_INVOKABLE void doInDirectChat(const QString& userId,
+ std::function<void(Room*)> operation);
+
/** 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
diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp
index 5ea91856..23a1bff1 100644
--- a/examples/qmc-example.cpp
+++ b/examples/qmc-example.cpp
@@ -126,7 +126,8 @@ void QMCTest::addAndRemoveTag()
if (targetRoom->tags().contains(TestTag))
targetRoom->removeTag(TestTag);
- QObject::connect(targetRoom, &Room::tagsChanged, targetRoom, [=] {
+ // Connect first because the signal is emitted synchronously.
+ connect(targetRoom, &Room::tagsChanged, targetRoom, [=] {
cout << "Room " << targetRoom->id().toStdString()
<< ", tag(s) changed:" << endl
<< " " << targetRoom->tagNames().join(", ").toStdString() << endl;
@@ -138,7 +139,6 @@ void QMCTest::addAndRemoveTag()
QObject::disconnect(targetRoom, &Room::tagsChanged, nullptr, nullptr);
}
});
- // The reverse order because tagsChanged is emitted synchronously.
cout << "Adding a tag" << endl;
targetRoom->addTag(TestTag);
}
@@ -211,40 +211,31 @@ void QMCTest::checkRedactionOutcome(QString evtIdToRedact,
void QMCTest::markDirectChat()
{
- if (c->isDirectChat(targetRoom))
+ if (c->isDirectChat(targetRoom->id()))
{
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 first because the signal is emitted synchronously.
connect(c.data(), &Connection::directChatsListChanged,
this, &QMCTest::checkDirectChatOutcome);
+ cout << "Marking the room as a direct chat" << endl;
+ c->addToDirectChats(targetRoom, c->user());
}
void QMCTest::checkDirectChatOutcome()
{
- if (!c->isDirectChat(targetRoom))
+ disconnect(c.data(), &Connection::directChatsListChanged, nullptr, nullptr);
+ if (!c->isDirectChat(targetRoom->id()))
{
- cout << "Room not (yet?) added to direct chats, waiting" << endl;
+ QMC_CHECK("Direct chat test", false);
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);
- });
+ c->removeFromDirectChats(targetRoom->id(), c->user());
+ QMC_CHECK("Direct chat test", !c->isDirectChat(targetRoom->id()));
}
void QMCTest::finalize()