aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/connection.cpp121
-rw-r--r--lib/connection.h62
-rw-r--r--lib/user.cpp25
-rw-r--r--lib/user.h6
4 files changed, 182 insertions, 32 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index 339be5b9..c4d09d3e 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -19,11 +19,9 @@
#include "connection.h"
#include "connectiondata.h"
#include "user.h"
-#include "events/event.h"
#include "events/directchatevent.h"
#include "room.h"
#include "settings.h"
-#include "converters.h"
#include "csapi/login.h"
#include "csapi/logout.h"
#include "csapi/receipts.h"
@@ -90,7 +88,7 @@ class Connection::Private
QVector<Room*> firstTimeRooms;
QMap<QString, User*> userMap;
DirectChatsMap directChats;
- QHash<QString, AccountDataMap> accountData;
+ std::unordered_map<QString, EventPtr> accountData;
QString userId;
SyncJob* syncJob = nullptr;
@@ -103,6 +101,30 @@ class Connection::Private
const QString& deviceId);
void broadcastDirectChatUpdates(const DirectChatsMap& additions,
const DirectChatsMap& removals);
+
+ template <typename EventT>
+ EventT* unpackAccountData() const
+ {
+ const auto& eventIt = accountData.find(EventT::matrixTypeId());
+ return eventIt == accountData.end()
+ ? nullptr : weakPtrCast<EventT>(eventIt->second);
+ }
+
+ void packAndSendAccountData(EventPtr&& event)
+ {
+ const auto eventType = event->matrixType();
+ q->callApi<SetAccountDataJob>(userId, eventType,
+ event->contentJson());
+ accountData[eventType] = std::move(event);
+ emit q->accountDataChanged(eventType);
+ }
+
+ template <typename EventT, typename ContentT>
+ void packAndSendAccountData(ContentT&& content)
+ {
+ packAndSendAccountData(
+ makeEvent<EventT>(std::forward<ContentT>(content)));
+ }
};
Connection::Connection(const QUrl& server, QObject* parent)
@@ -325,7 +347,7 @@ void Connection::onSyncSuccess(SyncData &&data) {
}
for (auto&& accountEvent: data.takeAccountData())
{
- if (accountEvent->type() == EventType::DirectChat)
+ if (is<DirectChatEvent>(*accountEvent))
{
const auto usersToDCs = ptrCast<DirectChatEvent>(move(accountEvent))
->usersToDirectChats();
@@ -355,9 +377,21 @@ void Connection::onSyncSuccess(SyncData &&data) {
continue;
}
- d->accountData[accountEvent->matrixType()] =
- fromJson<AccountDataMap>(accountEvent->contentJson());
- emit accountDataChanged(accountEvent->matrixType());
+ if (is<IgnoredUsersEvent>(*accountEvent))
+ qCDebug(MAIN) << "Users ignored by" << d->userId << "updated:"
+ << QStringList::fromSet(ignoredUsers()).join(',');
+
+ auto& currentData = d->accountData[accountEvent->matrixType()];
+ // A polymorphic event-specific comparison might be a bit more
+ // efficient; maaybe do it another day
+ if (!currentData ||
+ currentData->contentJson() != accountEvent->contentJson())
+ {
+ currentData = std::move(accountEvent);
+ qCDebug(MAIN) << "Updated account data of type"
+ << currentData->matrixType();
+ emit accountDataChanged(currentData->matrixType());
+ }
}
}
@@ -705,12 +739,30 @@ QHash< QPair<QString, bool>, Room* > Connection::roomMap() const
bool Connection::hasAccountData(const QString& type) const
{
- return d->accountData.contains(type);
+ return d->accountData.find(type) != d->accountData.cend();
+}
+
+const EventPtr& Connection::accountData(const QString& type) const
+{
+ static EventPtr NoEventPtr {};
+ auto it = d->accountData.find(type);
+ return it == d->accountData.end() ? NoEventPtr : it->second;
+}
+
+QJsonObject Connection::accountDataJson(const QString& type) const
+{
+ const auto& eventPtr = accountData(type);
+ return eventPtr ? eventPtr->contentJson() : QJsonObject();
+}
+
+void Connection::setAccountData(EventPtr&& event)
+{
+ d->packAndSendAccountData(std::move(event));
}
-Connection::AccountDataMap Connection::accountData(const QString& type) const
+void Connection::setAccountData(const QString& type, const QJsonObject& content)
{
- return d->accountData.value(type);
+ d->packAndSendAccountData(loadEvent<Event>(type, content));
}
QHash<QString, QVector<Room*>> Connection::tagsToRooms() const
@@ -815,6 +867,42 @@ QList<const User*> Connection::directChatUsers(const Room* room) const
return d->directChats.keys(room->id());
}
+bool Connection::isIgnored(const User* user) const
+{
+ return ignoredUsers().contains(user->id());
+}
+
+Connection::IgnoredUsersList Connection::ignoredUsers() const
+{
+ const auto* event = d->unpackAccountData<IgnoredUsersEvent>();
+ return event ? event->ignored_users() : IgnoredUsersList();
+}
+
+void Connection::addToIgnoredUsers(const User* user)
+{
+ Q_ASSERT(user != nullptr);
+
+ auto ignoreList = ignoredUsers();
+ if (!ignoreList.contains(user->id()))
+ {
+ ignoreList.insert(user->id());
+ d->packAndSendAccountData<IgnoredUsersEvent>(ignoreList);
+ emit ignoredUsersListChanged({{ user->id() }}, {});
+ }
+}
+
+void Connection::removeFromIgnoredUsers(const User* user)
+{
+ Q_ASSERT(user != nullptr);
+
+ auto ignoreList = ignoredUsers();
+ if (ignoreList.remove(user->id()) != 0)
+ {
+ d->packAndSendAccountData<IgnoredUsersEvent>(ignoreList);
+ emit ignoredUsersListChanged({}, {{ user->id() }});
+ }
+}
+
QMap<QString, User*> Connection::users() const
{
return d->userMap;
@@ -952,17 +1040,12 @@ void Connection::saveState(const QUrl &toFile) const
}
{
QJsonArray accountDataEvents {
- QJsonObject {
- { QStringLiteral("type"), QStringLiteral("m.direct") },
- { QStringLiteral("content"), toJson(d->directChats) }
- }
+ basicEventJson(QStringLiteral("m.direct"), toJson(d->directChats))
};
+ for (const auto &e : d->accountData)
+ accountDataEvents.append(
+ basicEventJson(e.first, e.second->contentJson()));
- for (auto it = d->accountData.begin(); it != d->accountData.end(); ++it)
- accountDataEvents.append(QJsonObject {
- {"type", it.key()},
- {"content", QMatrixClient::toJson(it.value())}
- });
rootObj.insert("account_data",
QJsonObject {{ QStringLiteral("events"), accountDataEvents }});
}
diff --git a/lib/connection.h b/lib/connection.h
index a1662449..b2bfdce3 100644
--- a/lib/connection.h
+++ b/lib/connection.h
@@ -20,6 +20,7 @@
#include "csapi/create_room.h"
#include "joinstate.h"
+#include "events/accountdataevents.h"
#include <QtCore/QObject>
#include <QtCore/QUrl>
@@ -32,7 +33,6 @@ namespace QMatrixClient
{
class Room;
class User;
- class RoomEvent;
class ConnectionData;
class SyncJob;
@@ -46,7 +46,6 @@ namespace QMatrixClient
class GetContentJob;
class DownloadFileJob;
class SendToDeviceJob;
- class Event;
/** Enumeration with flags defining the network job running policy
* So far only background/foreground flags are available.
@@ -74,10 +73,7 @@ namespace QMatrixClient
std::function<User*(Connection*, const QString&)>;
using DirectChatsMap = QMultiHash<const User*, QString>;
-
- using AccountDataMap = std::conditional_t<
- QT_VERSION >= QT_VERSION_CHECK(5, 5, 0),
- QVariantHash, QVariantMap>;
+ using IgnoredUsersList = IgnoredUsersEvent::content_type;
using UsersToDevicesToEvents =
std::unordered_map<QString,
@@ -101,11 +97,37 @@ namespace QMatrixClient
bool hasAccountData(const QString& type) const;
/** Get a generic account data event of the given type
- * This returns a generic hashmap for any account data event
+ * This returns an account data event of the given type
+ * stored on the server. Direct chats map cannot be retrieved
+ * using this method _yet_; use directChats() instead.
+ */
+ const EventPtr& accountData(const QString& type) const;
+
+ /** Get a generic account data event of the given type
+ * This returns an account data event of the given type
* stored on the server. Direct chats map cannot be retrieved
* using this method _yet_; use directChats() instead.
*/
- AccountDataMap accountData(const QString& type) const;
+ template <typename EventT>
+ const typename EventT::content_type accountData() const
+ {
+ if (const auto& eventPtr = accountData(EventT::matrixTypeId()))
+ return eventPtr->content();
+ return {};
+ }
+
+ /** Get account data as a JSON object
+ * This returns the content part of the account data event
+ * of the given type. Direct chats map cannot be retrieved using
+ * this method _yet_; use directChats() instead.
+ */
+ Q_INVOKABLE QJsonObject accountDataJson(const QString& type) const;
+
+ /** Set a generic account data event of the given type */
+ void setAccountData(EventPtr&& event);
+
+ Q_INVOKABLE void setAccountData(const QString& type,
+ const QJsonObject& content);
/** Get all Invited and Joined rooms grouped by tag
* \return a hashmap from tag name to a vector of room pointers,
@@ -154,6 +176,27 @@ namespace QMatrixClient
*/
QList<const User*> directChatUsers(const Room* room) const;
+ /** Check whether a particular user is in the ignore list */
+ bool isIgnored(const User* user) const;
+
+ /** Get the whole list of ignored users */
+ IgnoredUsersList ignoredUsers() const;
+
+ /** Add the user to the ignore list
+ * The change signal is emitted synchronously, without waiting
+ * to complete synchronisation with the server.
+ *
+ * \sa ignoredUsersListChanged
+ */
+ void addToIgnoredUsers(const User* user);
+
+ /** Remove the user from the ignore list
+ * Similar to adding, the change signal is emitted synchronously.
+ *
+ * \sa ignoredUsersListChanged
+ */
+ void removeFromIgnoredUsers(const User* user);
+
/** Get the full list of users known to this account */
QMap<QString, User*> users() const;
@@ -516,6 +559,9 @@ namespace QMatrixClient
void directChatsListChanged(DirectChatsMap additions,
DirectChatsMap removals);
+ void ignoredUsersListChanged(IgnoredUsersList additions,
+ IgnoredUsersList removals);
+
void cacheStateChanged();
protected:
diff --git a/lib/user.cpp b/lib/user.cpp
index 025d669c..f6611309 100644
--- a/lib/user.cpp
+++ b/lib/user.cpp
@@ -199,6 +199,12 @@ User::User(QString userId, Connection* connection)
setObjectName(userId);
}
+Connection* User::connection() const
+{
+ Q_ASSERT(d->connection);
+ return d->connection;
+}
+
User::~User() = default;
QString User::id() const
@@ -260,7 +266,7 @@ void User::updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl,
void User::rename(const QString& newName)
{
- auto job = d->connection->callApi<SetDisplayNameJob>(id(), newName);
+ auto job = connection()->callApi<SetDisplayNameJob>(id(), newName);
connect(job, &BaseJob::success, this, [=] { updateName(newName); });
}
@@ -283,20 +289,29 @@ void User::rename(const QString& newName, const Room* r)
bool User::setAvatar(const QString& fileName)
{
- return avatarObject().upload(d->connection, fileName,
+ return avatarObject().upload(connection(), fileName,
std::bind(&Private::setAvatarOnServer, d.data(), _1, this));
}
bool User::setAvatar(QIODevice* source)
{
- return avatarObject().upload(d->connection, source,
+ return avatarObject().upload(connection(), source,
std::bind(&Private::setAvatarOnServer, d.data(), _1, this));
}
void User::requestDirectChat()
{
- Q_ASSERT(d->connection);
- d->connection->requestDirectChat(d->userId);
+ connection()->requestDirectChat(d->userId);
+}
+
+void User::ignore()
+{
+ connection()->addToIgnoredUsers(this);
+}
+
+void User::unmarkIgnore()
+{
+ connection()->removeFromIgnoredUsers(this);
}
void User::Private::setAvatarOnServer(QString contentUri, User* q)
diff --git a/lib/user.h b/lib/user.h
index 1cf72155..2eb7f78a 100644
--- a/lib/user.h
+++ b/lib/user.h
@@ -43,6 +43,8 @@ namespace QMatrixClient
User(QString userId, Connection* connection);
~User() override;
+ Connection* connection() const;
+
/** Get unique stable user id
* User id is generated by the server and is not changed ever.
*/
@@ -119,6 +121,10 @@ namespace QMatrixClient
* Connection::directChatAvailable()
*/
void requestDirectChat();
+ /** Add the user to the ignore list */
+ void ignore();
+ /** Remove the user from the ignore list */
+ void unmarkIgnore();
signals:
void nameAboutToChange(QString newName, QString oldName,