summary refs log tree commit diff
path: root/src/ChatPage.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/ChatPage.cc')
-rw-r--r--src/ChatPage.cc297
1 files changed, 196 insertions, 101 deletions
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() {}