Lower the complexity of the group filtering algorithm (#380)

The old algorithm during filtering will check every room
if it's part of the group. O(N*G)

The room ids for a group are now stored in a map for faster lookup
so the search can be completed in a single pass. O(N)
This commit is contained in:
Konstantinos Sideris 2018-07-21 13:35:36 +03:00
parent 9d718fccf4
commit 54c7eb374a
6 changed files with 52 additions and 44 deletions

View File

@ -514,9 +514,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
current_community_ = groupId; current_community_ = groupId;
if (groupId == "world") if (groupId == "world")
room_list_->setFilterRooms(false); room_list_->removeFilter();
else else
room_list_->setRoomFilter(communitiesList_->roomList(groupId)); room_list_->applyFilter(communitiesList_->roomList(groupId));
}); });
connect(&notificationsManager, connect(&notificationsManager,

View File

@ -1,5 +1,5 @@
#include "CommunitiesList.h"
#include "Cache.h" #include "Cache.h"
#include "CommunitiesList.h"
#include "Logging.h" #include "Logging.h"
#include "MatrixClient.h" #include "MatrixClient.h"
@ -83,7 +83,7 @@ CommunitiesList::addCommunity(const std::string &group_id)
connect(this, connect(this,
&CommunitiesList::groupRoomsRetrieved, &CommunitiesList::groupRoomsRetrieved,
this, this,
[this](const QString &id, const std::vector<QString> &rooms) { [this](const QString &id, const std::map<QString, bool> &rooms) {
if (communities_.find(id) == communities_.end()) if (communities_.find(id) == communities_.end())
return; return;
@ -109,9 +109,9 @@ CommunitiesList::addCommunity(const std::string &group_id)
return; return;
} }
std::vector<QString> room_ids; std::map<QString, bool> room_ids;
for (const auto &room : res.at("chunk")) for (const auto &room : res.at("chunk"))
room_ids.push_back(QString::fromStdString(room.at("room_id"))); room_ids.emplace(QString::fromStdString(room.at("room_id")), true);
emit groupRoomsRetrieved(id, room_ids); emit groupRoomsRetrieved(id, room_ids);
}); });
@ -185,7 +185,7 @@ CommunitiesList::fetchCommunityAvatar(const QString &id, const QString &avatarUr
}); });
} }
std::vector<QString> std::map<QString, bool>
CommunitiesList::roomList(const QString &id) const CommunitiesList::roomList(const QString &id) const
{ {
if (communityExists(id)) if (communityExists(id))

View File

@ -18,13 +18,13 @@ public:
void addCommunity(const std::string &id); void addCommunity(const std::string &id);
void removeCommunity(const QString &id) { communities_.erase(id); }; void removeCommunity(const QString &id) { communities_.erase(id); };
std::vector<QString> roomList(const QString &id) const; std::map<QString, bool> roomList(const QString &id) const;
signals: signals:
void communityChanged(const QString &id); void communityChanged(const QString &id);
void avatarRetrieved(const QString &id, const QPixmap &img); void avatarRetrieved(const QString &id, const QPixmap &img);
void groupProfileRetrieved(const QString &group_id, const mtx::responses::GroupProfile &); void groupProfileRetrieved(const QString &group_id, const mtx::responses::GroupProfile &);
void groupRoomsRetrieved(const QString &group_id, const std::vector<QString> &res); void groupRoomsRetrieved(const QString &group_id, const std::map<QString, bool> &res);
public slots: public slots:
void updateCommunityAvatar(const QString &id, const QPixmap &img); void updateCommunityAvatar(const QString &id, const QPixmap &img);

View File

@ -32,8 +32,8 @@ public:
bool isPressed() const { return isPressed_; } bool isPressed() const { return isPressed_; }
void setAvatar(const QImage &img); void setAvatar(const QImage &img);
void setRooms(std::vector<QString> room_ids) { room_ids_ = std::move(room_ids); } void setRooms(std::map<QString, bool> room_ids) { room_ids_ = std::move(room_ids); }
std::vector<QString> rooms() const { return room_ids_; } std::map<QString, bool> rooms() const { return room_ids_; }
QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; } QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; }
QColor hoverBackgroundColor() const { return hoverBackgroundColor_; } QColor hoverBackgroundColor() const { return hoverBackgroundColor_; }
@ -69,7 +69,7 @@ private:
QString resolveName() const; QString resolveName() const;
std::vector<QString> room_ids_; std::map<QString, bool> room_ids_;
QString name_; QString name_;
QString groupId_; QString groupId_;

View File

@ -56,6 +56,8 @@ RoomList::RoomList(QSharedPointer<UserSettings> userSettings, QWidget *parent)
scrollArea_->setWidget(scrollAreaContents_); scrollArea_->setWidget(scrollAreaContents_);
topLayout_->addWidget(scrollArea_); topLayout_->addWidget(scrollArea_);
qRegisterMetaType<std::map<QString, bool>>();
connect(this, &RoomList::updateRoomAvatarCb, this, &RoomList::updateRoomAvatar); connect(this, &RoomList::updateRoomAvatarCb, this, &RoomList::updateRoomAvatar);
} }
@ -340,10 +342,21 @@ RoomList::closeJoinRoomDialog(bool isJoining, QString roomAlias)
} }
void void
RoomList::setFilterRooms(bool isFilteringEnabled) RoomList::removeFilter()
{ {
for (int i = 0; i < contentsLayout_->count(); i++) { for (int i = 0; i < contentsLayout_->count(); i++) {
// If roomFilter_ contains the room for the current RoomInfoListItem, auto widget =
qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(i)->widget());
if (widget)
widget->show();
}
}
void
RoomList::applyFilter(const std::map<QString, bool> &filter)
{
for (int i = 0; i < contentsLayout_->count(); i++) {
// If filter contains the room for the current RoomInfoListItem,
// show the list item, otherwise hide it // show the list item, otherwise hide it
auto listitem = auto listitem =
qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(i)->widget()); qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(i)->widget());
@ -351,28 +364,29 @@ RoomList::setFilterRooms(bool isFilteringEnabled)
if (!listitem) if (!listitem)
continue; continue;
if (!isFilteringEnabled || filterItemExists(listitem->roomId())) if (filter.find(listitem->roomId()) != filter.end())
listitem->show(); listitem->show();
else else
listitem->hide(); listitem->hide();
} }
if (isFilteringEnabled && !filterItemExists(selectedRoom_)) { // If the already selected room is part of the group, make sure it's visible.
RoomInfoListItem *firstVisibleRoom = nullptr; if (!selectedRoom_.isEmpty() && (filter.find(selectedRoom_) != filter.end()))
return;
for (int i = 0; i < contentsLayout_->count(); i++) { selectFirstVisibleRoom();
QWidget *item = contentsLayout_->itemAt(i)->widget(); }
if (item != nullptr && item->isVisible()) { void
firstVisibleRoom = qobject_cast<RoomInfoListItem *>(item); RoomList::selectFirstVisibleRoom()
break; {
} for (int i = 0; i < contentsLayout_->count(); i++) {
auto item = qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(i)->widget());
if (item && item->isVisible()) {
highlightSelectedRoom(item->roomId());
break;
} }
if (firstVisibleRoom != nullptr)
highlightSelectedRoom(firstVisibleRoom->roomId());
} else {
scrollArea_->ensureWidgetVisible(rooms_[selectedRoom_].data());
} }
} }
@ -404,13 +418,6 @@ RoomList::updateRoom(const QString &room_id, const RoomInfo &info)
room->update(); room->update();
} }
void
RoomList::setRoomFilter(std::vector<QString> room_ids)
{
roomFilter_ = room_ids;
setFilterRooms(true);
}
void void
RoomList::addInvitedRoom(const QString &room_id, const RoomInfo &info) RoomList::addInvitedRoom(const QString &room_id, const RoomInfo &info)
{ {

View File

@ -17,6 +17,7 @@
#pragma once #pragma once
#include <QMetaType>
#include <QPushButton> #include <QPushButton>
#include <QScrollArea> #include <QScrollArea>
#include <QSharedPointer> #include <QSharedPointer>
@ -33,6 +34,9 @@ class UserSettings;
struct DescInfo; struct DescInfo;
struct RoomInfo; struct RoomInfo;
using RoomIds = std::map<QString, bool>;
Q_DECLARE_METATYPE(RoomIds)
class RoomList : public QWidget class RoomList : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -49,8 +53,10 @@ public:
void addRoom(const QString &room_id, const RoomInfo &info); void addRoom(const QString &room_id, const RoomInfo &info);
void addInvitedRoom(const QString &room_id, const RoomInfo &info); void addInvitedRoom(const QString &room_id, const RoomInfo &info);
void removeRoom(const QString &room_id, bool reset); void removeRoom(const QString &room_id, bool reset);
void setFilterRooms(bool filterRooms); //! Hide rooms that are not present in the given filter.
void setRoomFilter(std::vector<QString> room_ids); void applyFilter(const std::map<QString, bool> &rooms);
//! Show all the available rooms.
void removeFilter();
void updateRoom(const QString &room_id, const RoomInfo &info); void updateRoom(const QString &room_id, const RoomInfo &info);
void cleanupInvites(const std::map<QString, bool> &invites); void cleanupInvites(const std::map<QString, bool> &invites);
@ -82,10 +88,8 @@ private:
std::pair<QString, QSharedPointer<RoomInfoListItem>> firstRoom() const; std::pair<QString, QSharedPointer<RoomInfoListItem>> firstRoom() const;
void calculateUnreadMessageCount(); void calculateUnreadMessageCount();
bool roomExists(const QString &room_id) { return rooms_.find(room_id) != rooms_.end(); } bool roomExists(const QString &room_id) { return rooms_.find(room_id) != rooms_.end(); }
bool filterItemExists(const QString &id) //! Select the first visible room in the room list.
{ void selectFirstVisibleRoom();
return std::find(roomFilter_.begin(), roomFilter_.end(), id) != roomFilter_.end();
}
QVBoxLayout *topLayout_; QVBoxLayout *topLayout_;
QVBoxLayout *contentsLayout_; QVBoxLayout *contentsLayout_;
@ -99,9 +103,6 @@ private:
std::map<QString, QSharedPointer<RoomInfoListItem>> rooms_; std::map<QString, QSharedPointer<RoomInfoListItem>> rooms_;
QString selectedRoom_; QString selectedRoom_;
//! Which rooms to include in the room list.
std::vector<QString> roomFilter_;
QSharedPointer<UserSettings> userSettings_; QSharedPointer<UserSettings> userSettings_;
bool isSortPending_ = false; bool isSortPending_ = false;