aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/qmc-example.cpp19
-rw-r--r--lib/connection.cpp81
-rw-r--r--lib/connection.h11
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();