summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--include/ChatPage.h24
-rw-r--r--include/RoomState.h6
-rw-r--r--src/Cache.cc2
-rw-r--r--src/ChatPage.cc297
-rw-r--r--src/RoomList.cc6
-rw-r--r--src/RoomState.cc3
-rw-r--r--src/Sync.cc4
-rw-r--r--src/TimelineView.cc2
-rw-r--r--src/TimelineViewManager.cc4
-rw-r--r--src/events/PowerLevelsEventContent.cc8
-rw-r--r--src/ui/LoadingIndicator.cc2
-rw-r--r--src/ui/RippleOverlay.cc2
12 files changed, 242 insertions, 118 deletions
diff --git a/include/ChatPage.h b/include/ChatPage.h
index d3790f78..416f7870 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -23,6 +23,10 @@
 #include <QTimer>
 #include <QWidget>
 
+#include "MemberEventContent.h"
+#include "MessageEvent.h"
+#include "StateEvent.h"
+
 class Cache;
 class MatrixClient;
 class OverlayModal;
@@ -38,6 +42,8 @@ class TimelineViewManager;
 class TopRoomBar;
 class TypingDisplay;
 class UserInfoWidget;
+class JoinedRoom;
+class LeftRoom;
 
 constexpr int CONSENSUS_TIMEOUT    = 1000;
 constexpr int SHOW_CONTENT_TIMEOUT = 3000;
@@ -76,8 +82,24 @@ private slots:
         void removeRoom(const QString &room_id);
 
 private:
+        using UserID      = QString;
+        using RoomStates  = QMap<UserID, RoomState>;
+        using JoinedRooms = QMap<UserID, JoinedRoom>;
+        using LeftRooms   = QMap<UserID, LeftRoom>;
+        using Membership  = matrix::events::StateEvent<matrix::events::MemberEventContent>;
+        using Memberships = QMap<UserID, Membership>;
+
+        void removeLeftRooms(const LeftRooms &rooms);
+        void updateJoinedRooms(const JoinedRooms &rooms);
+
+        Memberships getMemberships(const QJsonArray &events) const;
+        RoomStates generateMembershipDifference(const JoinedRooms &rooms,
+                                                const RoomStates &states) const;
+
         void updateTypingUsers(const QString &roomid, const QList<QString> &user_ids);
-        void updateDisplayNames(const RoomState &state);
+        void updateUserMetadata(const QJsonArray &events);
+        void updateUserDisplayName(const Membership &event);
+        void updateUserAvatarUrl(const Membership &event);
         void loadStateFromCache();
         void deleteConfigs();
         void resetUI();
diff --git a/include/RoomState.h b/include/RoomState.h
index 57955e56..db1cdc68 100644
--- a/include/RoomState.h
+++ b/include/RoomState.h
@@ -41,6 +41,9 @@ namespace events = matrix::events;
 class RoomState
 {
 public:
+        RoomState();
+        RoomState(const QJsonArray &events);
+
         // Calculate room data that are not immediatly accessible. Like room name and
         // avatar.
         //
@@ -71,7 +74,8 @@ public:
         events::StateEvent<events::TopicEventContent> topic;
 
         // Contains the m.room.member events for all the joined users.
-        QMap<QString, events::StateEvent<events::MemberEventContent>> memberships;
+        using UserID = QString;
+        QMap<UserID, events::StateEvent<events::MemberEventContent>> memberships;
 
 private:
         QUrl avatar_;
diff --git a/src/Cache.cc b/src/Cache.cc
index de2c7944..99267343 100644
--- a/src/Cache.cc
+++ b/src/Cache.cc
@@ -107,7 +107,7 @@ Cache::setState(const QString &nextBatchToken, const QMap<QString, RoomState> &s
 
                 setNextBatchToken(txn, nextBatchToken);
 
-                for (auto it = states.constBegin(); it != states.constEnd(); it++)
+                for (auto it = states.constBegin(); it != states.constEnd(); ++it)
                         insertRoomState(txn, it.key(), it.value());
 
                 txn.commit();
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 150f6007..a756a0f9 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -32,7 +32,6 @@
 #include "RoomState.h"
 #include "SideBarActions.h"
 #include "Splitter.h"
-#include "StateEvent.h"
 #include "Sync.h"
 #include "TextInputWidget.h"
 #include "Theme.h"
@@ -361,95 +360,19 @@ ChatPage::syncFailed(const QString &msg)
         QTimer::singleShot(SYNC_RETRY_TIMEOUT, this, [=]() { client_->sync(); });
 }
 
-// TODO: Should be moved in another class that manages this global list.
-void
-ChatPage::updateDisplayNames(const RoomState &state)
-{
-        for (const auto member : state.memberships) {
-                auto displayName = member.content().displayName();
-
-                if (!displayName.isEmpty())
-                        TimelineViewManager::DISPLAY_NAMES.insert(member.stateKey(), displayName);
-        }
-}
-
 void
 ChatPage::syncCompleted(const SyncResponse &response)
 {
-        auto joined = response.rooms().join();
-
-        for (auto it = joined.constBegin(); it != joined.constEnd(); it++) {
-                updateTypingUsers(it.key(), it.value().typingUserIDs());
-
-                RoomState room_state;
-
-                // Merge the new updates for rooms that we are tracking.
-                if (state_manager_.contains(it.key())) {
-                        room_state = state_manager_[it.key()];
-                }
-
-                room_state.updateFromEvents(it.value().state().events());
-                room_state.updateFromEvents(it.value().timeline().events());
-
-                updateDisplayNames(room_state);
+        updateJoinedRooms(response.rooms().join());
+        removeLeftRooms(response.rooms().leave());
 
-                if (state_manager_.contains(it.key())) {
-                        // TODO: Use pointers instead of copying.
-                        auto oldState = state_manager_[it.key()];
-                        oldState.update(room_state);
-                        state_manager_.insert(it.key(), oldState);
-                } else {
-                        RoomState room_state;
-
-                        // Build the current state from the timeline and state events.
-                        room_state.updateFromEvents(it.value().state().events());
-                        room_state.updateFromEvents(it.value().timeline().events());
-
-                        // Remove redundant memberships.
-                        room_state.removeLeaveMemberships();
-
-                        // Resolve room name and avatar. e.g in case of one-to-one chats.
-                        room_state.resolveName();
-                        room_state.resolveAvatar();
-
-                        updateDisplayNames(room_state);
-
-                        state_manager_.insert(it.key(), room_state);
-                        settingsManager_.insert(
-                          it.key(), QSharedPointer<RoomSettings>(new RoomSettings(it.key())));
-
-                        for (const auto membership : room_state.memberships) {
-                                auto uid = membership.sender();
-                                auto url = membership.content().avatarUrl();
-
-                                if (!url.toString().isEmpty())
-                                        AvatarProvider::setAvatarUrl(uid, url);
-                        }
-
-                        view_manager_->addRoom(it.value(), it.key());
-                }
-
-                if (it.key() == current_room_)
-                        changeTopRoomInfo(it.key());
-
-                QApplication::processEvents();
-        }
-
-        auto leave = response.rooms().leave();
-
-        for (auto it = leave.constBegin(); it != leave.constEnd(); it++) {
-                if (state_manager_.contains(it.key())) {
-                        removeRoom(it.key());
-                }
-        }
-
-        QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), state_manager_);
-
-        client_->setNextBatchToken(response.nextBatch());
+        auto stateDiff = generateMembershipDifference(response.rooms().join(), state_manager_);
+        QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), stateDiff);
 
         room_list_->sync(state_manager_);
         view_manager_->sync(response.rooms());
 
+        client_->setNextBatchToken(response.nextBatch());
         client_->sync();
 }
 
@@ -458,7 +381,7 @@ ChatPage::initialSyncCompleted(const SyncResponse &response)
 {
         auto joined = response.rooms().join();
 
-        for (auto it = joined.constBegin(); it != joined.constEnd(); it++) {
+        for (auto it = joined.constBegin(); it != joined.constEnd(); ++it) {
                 RoomState room_state;
 
                 // Build the current state from the timeline and state events.
@@ -472,25 +395,18 @@ ChatPage::initialSyncCompleted(const SyncResponse &response)
                 room_state.resolveName();
                 room_state.resolveAvatar();
 
-                updateDisplayNames(room_state);
-
                 state_manager_.insert(it.key(), room_state);
                 settingsManager_.insert(it.key(),
                                         QSharedPointer<RoomSettings>(new RoomSettings(it.key())));
 
                 for (const auto membership : room_state.memberships) {
-                        auto uid = membership.sender();
-                        auto url = membership.content().avatarUrl();
-
-                        if (!url.toString().isEmpty())
-                                AvatarProvider::setAvatarUrl(uid, url);
+                        updateUserDisplayName(membership);
+                        updateUserAvatarUrl(membership);
                 }
 
                 QApplication::processEvents();
         }
 
-        client_->setNextBatchToken(response.nextBatch());
-
         QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), state_manager_);
 
         // Populate timelines with messages.
@@ -499,6 +415,7 @@ ChatPage::initialSyncCompleted(const SyncResponse &response)
         // Initialize room list.
         room_list_->setInitialRooms(settingsManager_, state_manager_);
 
+        client_->setNextBatchToken(response.nextBatch());
         client_->sync();
 
         emit contentLoaded();
@@ -571,7 +488,7 @@ ChatPage::loadStateFromCache()
         // Fetch all the joined room's state.
         auto rooms = cache_->states();
 
-        for (auto it = rooms.constBegin(); it != rooms.constEnd(); it++) {
+        for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) {
                 RoomState room_state = it.value();
 
                 // Clean up and prepare state for use.
@@ -579,9 +496,6 @@ ChatPage::loadStateFromCache()
                 room_state.resolveName();
                 room_state.resolveAvatar();
 
-                // Update the global list with user's display names.
-                updateDisplayNames(room_state);
-
                 // Save the current room state.
                 state_manager_.insert(it.key(), room_state);
 
@@ -591,11 +505,8 @@ ChatPage::loadStateFromCache()
 
                 // Resolve user avatars.
                 for (const auto membership : room_state.memberships) {
-                        auto uid = membership.sender();
-                        auto url = membership.content().avatarUrl();
-
-                        if (!url.toString().isEmpty())
-                                AvatarProvider::setAvatarUrl(uid, url);
+                        updateUserDisplayName(membership);
+                        updateUserAvatarUrl(membership);
                 }
         }
 
@@ -700,4 +611,188 @@ ChatPage::updateTypingUsers(const QString &roomid, const QList<QString> &user_id
         typingUsers_.insert(roomid, users);
 }
 
+void
+ChatPage::updateUserMetadata(const QJsonArray &events)
+{
+        events::EventType ty;
+
+        for (const auto &event : events) {
+                try {
+                        ty = events::extractEventType(event.toObject());
+                } catch (const DeserializationException &e) {
+                        qWarning() << e.what() << event;
+                        continue;
+                }
+
+                if (!events::isStateEvent(ty))
+                        continue;
+
+                try {
+                        switch (ty) {
+                        case events::EventType::RoomMember: {
+                                events::StateEvent<events::MemberEventContent> member;
+                                member.deserialize(event);
+
+                                updateUserAvatarUrl(member);
+                                updateUserDisplayName(member);
+
+                                break;
+                        }
+                        default: {
+                                continue;
+                        }
+                        }
+                } catch (const DeserializationException &e) {
+                        qWarning() << e.what() << event;
+                        continue;
+                }
+        }
+}
+
+void
+ChatPage::updateUserAvatarUrl(const events::StateEvent<events::MemberEventContent> &membership)
+{
+        auto uid = membership.sender();
+        auto url = membership.content().avatarUrl();
+
+        if (!url.toString().isEmpty())
+                AvatarProvider::setAvatarUrl(uid, url);
+}
+
+void
+ChatPage::updateUserDisplayName(const events::StateEvent<events::MemberEventContent> &membership)
+{
+        auto displayName = membership.content().displayName();
+
+        if (!displayName.isEmpty())
+                TimelineViewManager::DISPLAY_NAMES.insert(membership.stateKey(), displayName);
+}
+
+void
+ChatPage::removeLeftRooms(const QMap<QString, LeftRoom> &rooms)
+{
+        for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) {
+                if (state_manager_.contains(it.key()))
+                        removeRoom(it.key());
+        }
+}
+
+void
+ChatPage::updateJoinedRooms(const QMap<QString, JoinedRoom> &rooms)
+{
+        for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) {
+                updateTypingUsers(it.key(), it.value().typingUserIDs());
+
+                const auto newStateEvents    = it.value().state().events();
+                const auto newTimelineEvents = it.value().timeline().events();
+
+                // Merge the new updates for rooms that we are tracking.
+                if (state_manager_.contains(it.key())) {
+                        auto oldState = &state_manager_[it.key()];
+                        oldState->updateFromEvents(newStateEvents);
+                        oldState->updateFromEvents(newTimelineEvents);
+                        oldState->resolveName();
+                        oldState->resolveAvatar();
+                } else {
+                        // Build the current state from the timeline and state events.
+                        RoomState room_state;
+                        room_state.updateFromEvents(newStateEvents);
+                        room_state.updateFromEvents(newTimelineEvents);
+
+                        // Resolve room name and avatar. e.g in case of one-to-one chats.
+                        room_state.resolveName();
+                        room_state.resolveAvatar();
+
+                        state_manager_.insert(it.key(), room_state);
+
+                        // TODO Doesn't work on the sidebar.
+                        settingsManager_.insert(
+                          it.key(), QSharedPointer<RoomSettings>(new RoomSettings(it.key())));
+
+                        view_manager_->addRoom(it.value(), it.key());
+                }
+
+                updateUserMetadata(newStateEvents);
+                updateUserMetadata(newTimelineEvents);
+
+                if (it.key() == current_room_)
+                        changeTopRoomInfo(it.key());
+
+                QApplication::processEvents();
+        }
+}
+
+QMap<QString, RoomState>
+ChatPage::generateMembershipDifference(const QMap<QString, JoinedRoom> &rooms,
+                                       const QMap<QString, RoomState> &states) const
+{
+        QMap<QString, RoomState> stateDiff;
+
+        for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) {
+                if (!states.contains(it.key()))
+                        continue;
+
+                auto events = it.value().state().events();
+
+                for (auto event : it.value().timeline().events())
+                        events.append(event);
+
+                RoomState local;
+                local.aliases            = states[it.key()].aliases;
+                local.avatar             = states[it.key()].avatar;
+                local.canonical_alias    = states[it.key()].canonical_alias;
+                local.history_visibility = states[it.key()].history_visibility;
+                local.join_rules         = states[it.key()].join_rules;
+                local.name               = states[it.key()].name;
+                local.power_levels       = states[it.key()].power_levels;
+                local.topic              = states[it.key()].topic;
+                local.memberships        = getMemberships(events);
+
+                stateDiff.insert(it.key(), local);
+        }
+
+        return stateDiff;
+}
+
+using Memberships = QMap<QString, matrix::events::StateEvent<events::MemberEventContent>>;
+
+Memberships
+ChatPage::getMemberships(const QJsonArray &events) const
+{
+        Memberships memberships;
+
+        events::EventType ty;
+
+        for (const auto &event : events) {
+                try {
+                        ty = events::extractEventType(event.toObject());
+                } catch (const DeserializationException &e) {
+                        qWarning() << e.what() << event;
+                        continue;
+                }
+
+                if (!events::isStateEvent(ty))
+                        continue;
+
+                try {
+                        switch (ty) {
+                        case events::EventType::RoomMember: {
+                                events::StateEvent<events::MemberEventContent> member;
+                                member.deserialize(event);
+                                memberships.insert(member.stateKey(), member);
+                                break;
+                        }
+                        default: {
+                                continue;
+                        }
+                        }
+                } catch (const DeserializationException &e) {
+                        qWarning() << e.what() << event;
+                        continue;
+                }
+        }
+
+        return memberships;
+};
+
 ChatPage::~ChatPage() {}
diff --git a/src/RoomList.cc b/src/RoomList.cc
index 73e85ea8..3a95cb17 100644
--- a/src/RoomList.cc
+++ b/src/RoomList.cc
@@ -140,7 +140,7 @@ RoomList::setInitialRooms(const QMap<QString, QSharedPointer<RoomSettings>> &set
                 return;
         }
 
-        for (auto it = states.constBegin(); it != states.constEnd(); it++) {
+        for (auto it = states.constBegin(); it != states.constEnd(); ++it) {
                 auto room_id = it.key();
                 auto state   = it.value();
 
@@ -194,7 +194,7 @@ RoomList::openLeaveRoomDialog(const QString &room_id)
 void
 RoomList::sync(const QMap<QString, RoomState> &states)
 {
-        for (auto it = states.constBegin(); it != states.constEnd(); it++) {
+        for (auto it = states.constBegin(); it != states.constEnd(); ++it) {
                 auto room_id = it.key();
                 auto state   = it.value();
 
@@ -231,7 +231,7 @@ RoomList::highlightSelectedRoom(const QString &room_id)
 
         calculateUnreadMessageCount();
 
-        for (auto it = rooms_.constBegin(); it != rooms_.constEnd(); it++) {
+        for (auto it = rooms_.constBegin(); it != rooms_.constEnd(); ++it) {
                 if (it.key() != room_id) {
                         it.value()->setPressedState(false);
                 } else {
diff --git a/src/RoomState.cc b/src/RoomState.cc
index 8db9b2bc..2b8bcdba 100644
--- a/src/RoomState.cc
+++ b/src/RoomState.cc
@@ -22,6 +22,9 @@
 
 namespace events = matrix::events;
 
+RoomState::RoomState() {}
+RoomState::RoomState(const QJsonArray &events) { updateFromEvents(events); }
+
 void
 RoomState::resolveName()
 {
diff --git a/src/Sync.cc b/src/Sync.cc
index 965f7c3d..416fa0c6 100644
--- a/src/Sync.cc
+++ b/src/Sync.cc
@@ -83,7 +83,7 @@ Rooms::deserialize(const QJsonValue &data)
 
                 auto join = object.value("join").toObject();
 
-                for (auto it = join.constBegin(); it != join.constEnd(); it++) {
+                for (auto it = join.constBegin(); it != join.constEnd(); ++it) {
                         JoinedRoom tmp_room;
                         try {
                                 tmp_room.deserialize(it.value());
@@ -108,7 +108,7 @@ Rooms::deserialize(const QJsonValue &data)
                 }
                 auto leave = object.value("leave").toObject();
 
-                for (auto it = leave.constBegin(); it != leave.constEnd(); it++) {
+                for (auto it = leave.constBegin(); it != leave.constEnd(); ++it) {
                         LeftRoom tmp_room;
 
                         try {
diff --git a/src/TimelineView.cc b/src/TimelineView.cc
index ee98fe72..354a725c 100644
--- a/src/TimelineView.cc
+++ b/src/TimelineView.cc
@@ -577,7 +577,7 @@ TimelineView::isPendingMessage(const QString &eventid,
 void
 TimelineView::removePendingMessage(const QString &eventid, const QString &body)
 {
-        for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); it++) {
+        for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); ++it) {
                 int index = std::distance(pending_msgs_.begin(), it);
 
                 if (it->event_id == eventid || it->body == body) {
diff --git a/src/TimelineViewManager.cc b/src/TimelineViewManager.cc
index 44b626ed..01e7d9ab 100644
--- a/src/TimelineViewManager.cc
+++ b/src/TimelineViewManager.cc
@@ -100,7 +100,7 @@ TimelineViewManager::clearAll()
 void
 TimelineViewManager::initialize(const Rooms &rooms)
 {
-        for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); it++) {
+        for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) {
                 addRoom(it.value(), it.key());
         }
 }
@@ -148,7 +148,7 @@ TimelineViewManager::addRoom(const QString &room_id)
 void
 TimelineViewManager::sync(const Rooms &rooms)
 {
-        for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); it++) {
+        for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) {
                 auto roomid = it.key();
 
                 if (!views_.contains(roomid)) {
diff --git a/src/events/PowerLevelsEventContent.cc b/src/events/PowerLevelsEventContent.cc
index 02a6ee71..c796f81f 100644
--- a/src/events/PowerLevelsEventContent.cc
+++ b/src/events/PowerLevelsEventContent.cc
@@ -54,14 +54,14 @@ PowerLevelsEventContent::deserialize(const QJsonValue &data)
         if (object.value("users").isObject()) {
                 auto users = object.value("users").toObject();
 
-                for (auto it = users.constBegin(); it != users.constEnd(); it++)
+                for (auto it = users.constBegin(); it != users.constEnd(); ++it)
                         users_.insert(it.key(), it.value().toInt());
         }
 
         if (object.value("events").isObject()) {
                 auto events = object.value("events").toObject();
 
-                for (auto it = events.constBegin(); it != events.constEnd(); it++)
+                for (auto it = events.constBegin(); it != events.constEnd(); ++it)
                         events_.insert(it.key(), it.value().toInt());
         }
 }
@@ -83,10 +83,10 @@ PowerLevelsEventContent::serialize() const
         QJsonObject users;
         QJsonObject events;
 
-        for (auto it = users_.constBegin(); it != users_.constEnd(); it++)
+        for (auto it = users_.constBegin(); it != users_.constEnd(); ++it)
                 users.insert(it.key(), it.value());
 
-        for (auto it = events_.constBegin(); it != events_.constEnd(); it++)
+        for (auto it = events_.constBegin(); it != events_.constEnd(); ++it)
                 events.insert(it.key(), it.value());
 
         object["users"]  = users;
diff --git a/src/ui/LoadingIndicator.cc b/src/ui/LoadingIndicator.cc
index 71312d32..f64151ce 100644
--- a/src/ui/LoadingIndicator.cc
+++ b/src/ui/LoadingIndicator.cc
@@ -41,7 +41,7 @@ LoadingIndicator::paintEvent(QPaintEvent *e)
 
         int capsuleRadius = (outerRadius - innerRadius) / 2;
 
-        for (int i = 0; i < 8; i++) {
+        for (int i = 0; i < 8; ++i) {
                 QColor color = color_;
 
                 color.setAlphaF(1.0f - (i / 8.0f));
diff --git a/src/ui/RippleOverlay.cc b/src/ui/RippleOverlay.cc
index 26d432f6..a3567db2 100644
--- a/src/ui/RippleOverlay.cc
+++ b/src/ui/RippleOverlay.cc
@@ -48,7 +48,7 @@ RippleOverlay::paintEvent(QPaintEvent *event)
         if (use_clip_)
                 painter.setClipPath(clip_path_);
 
-        for (auto it = ripples_.constBegin(); it != ripples_.constEnd(); it++)
+        for (auto it = ripples_.constBegin(); it != ripples_.constEnd(); ++it)
                 paintRipple(&painter, *it);
 }