diff --git a/.gitignore b/.gitignore
index 96ace50f..e7df9077 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,9 @@ ui_*.h
*.autosave
+# VSCode
+.vscode/*
+
#QtCtreator Qml
*.qmlproject.user
*.qmlproject.user.*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b0a5c610..29c28527 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -186,6 +186,9 @@ set(SRC_FILES
src/AvatarProvider.cc
src/Cache.cc
src/ChatPage.cc
+ src/CommunitiesListItem.cc
+ src/CommunitiesList.cc
+ src/Community.cc
src/Deserializable.cc
src/InviteeItem.cc
src/InputValidator.cc
@@ -265,6 +268,9 @@ qt5_wrap_cpp(MOC_HEADERS
include/AvatarProvider.h
include/ChatPage.h
+ include/CommunitiesListItem.h
+ include/CommunitiesList.h
+ include/Community.h
include/LoginPage.h
include/MainWindow.h
include/InviteeItem.h
diff --git a/include/ChatPage.h b/include/ChatPage.h
index 584424c0..754ee0f4 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -24,6 +24,8 @@
#include <QTimer>
#include <QWidget>
+#include "CommunitiesList.h"
+#include "Community.h"
#include <mtx.hpp>
class Cache;
@@ -80,6 +82,7 @@ private slots:
void showUnreadMessageNotification(int count);
void updateTopBarAvatar(const QString &roomid, const QPixmap &img);
void updateOwnProfileInfo(const QUrl &avatar_url, const QString &display_name);
+ void updateOwnCommunitiesInfo(const QList<QString> &own_communities);
void setOwnAvatar(const QPixmap &img);
void initialSyncCompleted(const mtx::responses::Sync &response);
void syncCompleted(const mtx::responses::Sync &response);
@@ -126,13 +129,21 @@ private:
QHBoxLayout *topLayout_;
Splitter *splitter;
- QFrame *sideBar_;
+ QWidget *sideBar_;
+ QWidget *communitiesSideBar_;
+ QVBoxLayout *communitiesSideBarLayout_;
QVBoxLayout *sideBarLayout_;
+ QVBoxLayout *sideBarTopLayout_;
+ QVBoxLayout *sideBarMainLayout_;
+ QWidget *sideBarTopWidget_;
+ QVBoxLayout *sideBarTopWidgetLayout_;
QFrame *content_;
QVBoxLayout *contentLayout_;
+ CommunitiesList *communitiesList_;
RoomList *room_list_;
+
TimelineViewManager *view_manager_;
SideBarActions *sidebarActions_;
@@ -145,13 +156,18 @@ private:
QTimer *consensusTimer_;
QString current_room_;
+ QString current_community_;
+
QMap<QString, QPixmap> room_avatars_;
+ QMap<QString, QPixmap> community_avatars_;
UserInfoWidget *user_info_widget_;
QMap<QString, RoomState> state_manager_;
QMap<QString, QSharedPointer<RoomSettings>> settingsManager_;
+ QMap<QString, QSharedPointer<Community>> communityManager_;
+
// Keeps track of the users currently typing on each room.
QMap<QString, QList<QString>> typingUsers_;
QTimer *typingRefresher_;
diff --git a/include/CommunitiesList.h b/include/CommunitiesList.h
new file mode 100644
index 00000000..53715363
--- /dev/null
+++ b/include/CommunitiesList.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <QScrollArea>
+#include <QSharedPointer>
+#include <QVBoxLayout>
+#include <QWidget>
+
+#include "CommunitiesListItem.h"
+#include "Community.h"
+#include "MatrixClient.h"
+#include "ui/Theme.h"
+
+class CommunitiesList : public QWidget
+{
+ Q_OBJECT
+
+public:
+ CommunitiesList(QSharedPointer<MatrixClient> client, QWidget *parent = nullptr);
+ ~CommunitiesList();
+
+ void setCommunities(const QMap<QString, QSharedPointer<Community>> &communities);
+ void clear();
+
+ void addCommunity(QSharedPointer<Community> community, const QString &community_id);
+ void removeCommunity(const QString &community_id);
+signals:
+ void communityChanged(const QString &community_id);
+
+public slots:
+ void updateCommunityAvatar(const QString &community_id, const QPixmap &img);
+ void highlightSelectedCommunity(const QString &community_id);
+
+private:
+ QVBoxLayout *topLayout_;
+ QVBoxLayout *contentsLayout_;
+ QWidget *scrollAreaContents_;
+ QScrollArea *scrollArea_;
+
+ QMap<QString, QSharedPointer<CommunitiesListItem>> communities_;
+
+ QSharedPointer<MatrixClient> client_;
+};
diff --git a/include/CommunitiesListItem.h b/include/CommunitiesListItem.h
new file mode 100644
index 00000000..099b4fa2
--- /dev/null
+++ b/include/CommunitiesListItem.h
@@ -0,0 +1,98 @@
+#pragma once
+
+#include <QDebug>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QSharedPointer>
+#include <QWidget>
+
+#include "Community.h"
+#include "Menu.h"
+#include "ui/Theme.h"
+
+class CommunitiesListItem : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QColor highlightedBackgroundColor READ highlightedBackgroundColor WRITE
+ setHighlightedBackgroundColor)
+ Q_PROPERTY(
+ QColor hoverBackgroundColor READ hoverBackgroundColor WRITE setHoverBackgroundColor)
+ Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
+
+public:
+ CommunitiesListItem(QSharedPointer<Community> community,
+ QString community_id,
+ QWidget *parent = nullptr);
+
+ ~CommunitiesListItem();
+
+ void setCommunity(QSharedPointer<Community> community);
+
+ inline bool isPressed() const;
+ inline void setAvatar(const QImage &avatar_image);
+
+ QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; }
+ QColor hoverBackgroundColor() const { return hoverBackgroundColor_; }
+ QColor backgroundColor() const { return backgroundColor_; }
+
+ void setHighlightedBackgroundColor(QColor &color) { highlightedBackgroundColor_ = color; }
+ void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; }
+ void setBackgroundColor(QColor &color) { backgroundColor_ = color; }
+
+ QColor highlightedBackgroundColor_;
+ QColor hoverBackgroundColor_;
+ QColor backgroundColor_;
+
+signals:
+ void clicked(const QString &community_id);
+
+public slots:
+ void setPressedState(bool state);
+
+protected:
+ void mousePressEvent(QMouseEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+ void contextMenuEvent(QContextMenuEvent *event) override;
+
+private:
+ const int IconSize = 55;
+
+ QSharedPointer<Community> community_;
+ QString communityId_;
+ QString communityName_;
+ QString communityShortDescription;
+
+ QPixmap communityAvatar_;
+
+ Menu *menu_;
+ bool isPressed_ = false;
+};
+
+inline bool
+CommunitiesListItem::isPressed() const
+{
+ return isPressed_;
+}
+
+inline void
+CommunitiesListItem::setAvatar(const QImage &avatar_image)
+{
+ communityAvatar_ = QPixmap::fromImage(
+ avatar_image.scaled(IconSize, IconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ update();
+}
+
+class WorldCommunityListItem : public CommunitiesListItem
+{
+ Q_OBJECT
+public:
+ WorldCommunityListItem(QWidget *parent = nullptr);
+ ~WorldCommunityListItem();
+
+protected:
+ void mousePressEvent(QMouseEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ const int IconSize = 55;
+};
diff --git a/include/Community.h b/include/Community.h
new file mode 100644
index 00000000..0d70dee1
--- /dev/null
+++ b/include/Community.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include <QJsonObject>
+#include <QObject>
+#include <QString>
+#include <QUrl>
+
+class Community : public QObject
+{
+ Q_OBJECT
+
+public:
+ void parseProfile(const QJsonObject &profile);
+ void parseRooms(const QJsonObject &rooms);
+
+ inline QUrl getAvatar() const;
+ inline QString getName() const;
+ inline QString getShortDescription() const;
+ inline QString getLongDescription() const;
+ inline const QList<QString> getRoomList() const;
+
+signals:
+ void roomsChanged(QList<QString> &rooms);
+
+private:
+ QUrl avatar_;
+ QString name_;
+ QString short_description_;
+ QString long_description_;
+
+ QList<QString> rooms_;
+};
+
+inline QUrl
+Community::getAvatar() const
+{
+ return avatar_;
+}
+
+inline QString
+Community::getName() const
+{
+ return name_;
+}
+
+inline QString
+Community::getShortDescription() const
+{
+ return short_description_;
+}
+
+inline QString
+Community::getLongDescription() const
+{
+ return long_description_;
+}
+
+inline const QList<QString>
+Community::getRoomList() const
+{
+ return rooms_;
+}
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index 2627f578..8936003f 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -48,6 +48,9 @@ public:
void versions() noexcept;
void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
void fetchUserAvatar(const QString &userId, const QUrl &avatarUrl);
+ void fetchCommunityAvatar(const QString &communityId, const QUrl &avatarUrl);
+ void fetchCommunityProfile(const QString &communityId);
+ void fetchCommunityRooms(const QString &communityId);
void fetchOwnAvatar(const QUrl &avatar_url);
void downloadImage(const QString &event_id, const QUrl &url);
void downloadFile(const QString &event_id, const QUrl &url);
@@ -71,6 +74,7 @@ public:
public slots:
void getOwnProfile() noexcept;
+ void getOwnCommunities() noexcept;
void logout() noexcept;
void setServer(const QString &server)
@@ -103,12 +107,16 @@ signals:
const QString &url,
const QByteArray &data);
void userAvatarRetrieved(const QString &userId, const QImage &img);
+ void communityAvatarRetrieved(const QString &communityId, const QPixmap &img);
+ void communityProfileRetrieved(const QString &communityId, const QJsonObject &profile);
+ void communityRoomsRetrieved(const QString &communityId, const QJsonObject &rooms);
void ownAvatarRetrieved(const QPixmap &img);
void imageDownloaded(const QString &event_id, const QPixmap &img);
void fileDownloaded(const QString &event_id, const QByteArray &data);
// Returned profile data for the user's account.
void getOwnProfileResponse(const QUrl &avatar_url, const QString &display_name);
+ void getOwnCommunitiesResponse(const QList<QString> &own_communities);
void initialSyncCompleted(const mtx::responses::Sync &response);
void initialSyncFailed(const QString &msg);
void syncCompleted(const mtx::responses::Sync &response);
diff --git a/include/RoomInfoListItem.h b/include/RoomInfoListItem.h
index 799e95bb..5cfea783 100644
--- a/include/RoomInfoListItem.h
+++ b/include/RoomInfoListItem.h
@@ -73,9 +73,10 @@ public:
void clearUnreadMessageCount();
void setState(const RoomState &state);
- bool isPressed() const { return isPressed_; }
- RoomState state() const { return state_; }
- int unreadMessageCount() const { return unreadMsgCount_; }
+ QString roomId();
+ bool isPressed() const { return isPressed_; };
+ RoomState state() const { return state_; };
+ int unreadMessageCount() const { return unreadMsgCount_; };
void setAvatar(const QImage &avatar_image);
void setDescriptionMessage(const DescInfo &info);
@@ -182,3 +183,9 @@ private:
QRectF acceptBtnRegion_;
QRectF declineBtnRegion_;
};
+
+inline QString
+RoomInfoListItem::roomId()
+{
+ return roomId_;
+}
diff --git a/include/RoomList.h b/include/RoomList.h
index 6b2151a2..d10cf5db 100644
--- a/include/RoomList.h
+++ b/include/RoomList.h
@@ -64,6 +64,8 @@ public:
const QString &room_id);
void addInvitedRoom(const QString &room_id, const mtx::responses::InvitedRoom &room);
void removeRoom(const QString &room_id, bool reset);
+ void setFilterRooms(bool filterRooms);
+ void setRoomFilter(QList<QString> room_ids);
signals:
void roomChanged(const QString &room_id);
@@ -105,6 +107,10 @@ private:
QSharedPointer<dialogs::LeaveRoom> leaveRoomDialog_;
QMap<QString, QSharedPointer<RoomInfoListItem>> rooms_;
+ QString selectedRoom_;
+
+ bool filterRooms_ = false;
+ QList<QString> roomFilter_ = QList<QString>(); // which rooms to include in the room list
QSharedPointer<MatrixClient> client_;
QSharedPointer<Cache> cache_;
diff --git a/include/ui/Theme.h b/include/ui/Theme.h
index c6c39553..c2e4ab59 100644
--- a/include/ui/Theme.h
+++ b/include/ui/Theme.h
@@ -13,8 +13,9 @@ enum class AvatarType
};
namespace sidebar {
-static const int SmallSize = 60;
-static const int NormalSize = 300;
+static const int SmallSize = 60;
+static const int NormalSize = 300;
+static const int CommunitiesSidebarSize = 64;
}
// Default font size.
const int FontSize = 16;
diff --git a/resources/icons/ui/world.png b/resources/icons/ui/world.png
new file mode 100644
index 00000000..d687d141
--- /dev/null
+++ b/resources/icons/ui/world.png
Binary files differdiff --git a/resources/icons/ui/world.svg b/resources/icons/ui/world.svg
new file mode 100644
index 00000000..c3acf162
--- /dev/null
+++ b/resources/icons/ui/world.svg
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg3304"
+ sodipodi:version="0.32"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="world.svg"
+ version="1.1"
+ inkscape:export-filename="/home/max/Program/nheko/resources/icons/world.png"
+ inkscape:export-xdpi="256"
+ inkscape:export-ydpi="256">
+ <defs
+ id="defs3306" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7.9375"
+ inkscape:cx="11.531663"
+ inkscape:cy="15.491127"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:grid-points="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1280"
+ inkscape:window-height="704"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata3309">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 12.998425,32.054724 c 6.286614,-2.35748 15.716536,-2.35748 22.00315,0"
+ id="path5512"
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 11.402021,24 H 36.597979"
+ id="path5516"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 20.856693,11.074078 c -3.929134,8.210961 -3.929134,18.426709 0,26.284977"
+ id="path5520"
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path6250"
+ d="m 12.998425,15.945276 c 6.286614,2.35748 15.716536,2.35748 22.00315,0"
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path9156"
+ d="m 27.143307,11.074078 c 3.929134,8.210961 3.929134,18.426709 0,26.284977"
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:none;fill-opacity:0.67634858;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path1767"
+ cx="24"
+ cy="24"
+ r="13.609846" />
+ </g>
+</svg>
diff --git a/resources/res.qrc b/resources/res.qrc
index 83415e9b..a5461718 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -22,6 +22,7 @@
<file>icons/ui/paper-clip-outline@2x.png</file>
<file>icons/ui/angle-pointing-to-left.png</file>
<file>icons/ui/angle-pointing-to-left@2x.png</file>
+ <file>icons/ui/world.png</file>
<file>icons/ui/angle-arrow-down.png</file>
<file>icons/ui/angle-arrow-down@2x.png</file>
<file>icons/ui/arrow-pointing-down.png</file>
diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss
index a78fb612..26425590 100644
--- a/resources/styles/nheko-dark.qss
+++ b/resources/styles/nheko-dark.qss
@@ -17,6 +17,10 @@ RoomList > * {
background-color: #383c4a;
}
+CommunitiesList,
+CommunitiesList > * {
+ background-color: #383c4a;
+}
FlatButton {
qproperty-foregroundColor: #caccd1;
qproperty-backgroundColor: #333;
@@ -54,6 +58,12 @@ RoomInfoListItem {
qproperty-btnTextColor: white;
}
+CommunitiesListItem {
+ qproperty-highlightedBackgroundColor: #5294e2;
+ qproperty-hoverBackgroundColor: #39679e;
+ qproperty-backgroundColor: #383c4a;
+}
+
LoadingIndicator {
qproperty-color: #caccd1;
}
diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss
index ce86e212..c135c12a 100644
--- a/resources/styles/nheko.qss
+++ b/resources/styles/nheko.qss
@@ -17,6 +17,11 @@ RoomList > * {
background-color: white;
}
+CommunitiesList,
+CommunitiesList > * {
+ background-color: white;
+}
+
FlatButton {
qproperty-foregroundColor: #333;
}
@@ -52,6 +57,12 @@ RoomInfoListItem {
qproperty-btnTextColor: #333;
}
+CommunitiesListItem {
+ qproperty-highlightedBackgroundColor: #38A3D8;
+ qproperty-hoverBackgroundColor: rgba(200, 200, 200, 128);
+ qproperty-backgroundColor: white;
+}
+
#ChatPageLoadSpinner {
qproperty-color: #acc7dc;
}
diff --git a/resources/styles/system.qss b/resources/styles/system.qss
index afb2ad26..42aba09d 100644
--- a/resources/styles/system.qss
+++ b/resources/styles/system.qss
@@ -60,6 +60,12 @@ RoomInfoListItem {
qproperty-btnTextColor: palette(text);
}
+CommunitiesListItem {
+ qproperty-highlightedBackgroundColor: palette(highlight);
+ qproperty-hoverBackgroundColor: palette(mid);
+ qproperty-backgroundColor: palette(window);
+}
+
LoadingIndicator {
qproperty-color: palette(highlight);
}
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 3958e2c2..3a78e1cc 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -60,6 +60,17 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
topLayout_->setSpacing(0);
topLayout_->setMargin(0);
+ communitiesSideBar_ = new QWidget(this);
+ communitiesSideBar_->setFixedWidth(ui::sidebar::CommunitiesSidebarSize);
+ communitiesSideBarLayout_ = new QVBoxLayout(communitiesSideBar_);
+ communitiesSideBarLayout_->setSpacing(0);
+ communitiesSideBarLayout_->setMargin(0);
+
+ communitiesList_ = new CommunitiesList(client, this);
+ communitiesSideBarLayout_->addWidget(communitiesList_);
+ // communitiesSideBarLayout_->addStretch(1);
+ topLayout_->addWidget(communitiesSideBar_);
+
auto splitter = new Splitter(this);
splitter->setHandleWidth(0);
@@ -72,7 +83,18 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
sideBarLayout_->setSpacing(0);
sideBarLayout_->setMargin(0);
- sidebarActions_ = new SideBarActions(this);
+ sideBarTopLayout_ = new QVBoxLayout();
+ sideBarTopLayout_->setSpacing(0);
+ sideBarTopLayout_->setMargin(0);
+ sideBarMainLayout_ = new QVBoxLayout();
+ sideBarMainLayout_->setSpacing(0);
+ sideBarMainLayout_->setMargin(0);
+
+ sideBarLayout_->addLayout(sideBarTopLayout_);
+ sideBarLayout_->addLayout(sideBarMainLayout_);
+
+ sideBarTopWidget_ = new QWidget(sideBar_);
+ sidebarActions_ = new SideBarActions(this);
connect(
sidebarActions_, &SideBarActions::showSettings, this, &ChatPage::showUserSettingsPage);
connect(
@@ -87,6 +109,10 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
sideBarLayout_->addWidget(room_list_);
sideBarLayout_->addWidget(sidebarActions_);
+ sideBarTopWidgetLayout_ = new QVBoxLayout(sideBarTopWidget_);
+ sideBarTopWidgetLayout_->setSpacing(0);
+ sideBarTopWidgetLayout_->setMargin(0);
+
// Content
content_ = new QFrame(this);
content_->setObjectName("mainContent");
@@ -274,6 +300,32 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
&MatrixClient::getOwnProfileResponse,
this,
&ChatPage::updateOwnProfileInfo);
+ connect(client_.data(),
+ SIGNAL(getOwnCommunitiesResponse(QList<QString>)),
+ this,
+ SLOT(updateOwnCommunitiesInfo(QList<QString>)));
+ connect(client_.data(),
+ &MatrixClient::communityProfileRetrieved,
+ this,
+ [=](QString communityId, QJsonObject profile) {
+ communityManager_[communityId]->parseProfile(profile);
+ });
+ connect(client_.data(),
+ &MatrixClient::communityRoomsRetrieved,
+ this,
+ [=](QString communityId, QJsonObject rooms) {
+ communityManager_[communityId]->parseRooms(rooms);
+
+ if (communityId == current_community_) {
+ if (communityId == "world") {
+ room_list_->setFilterRooms(false);
+ } else {
+ room_list_->setRoomFilter(
+ communityManager_[communityId]->getRoomList());
+ }
+ }
+ });
+
connect(client_.data(), &MatrixClient::ownAvatarRetrieved, this, &ChatPage::setOwnAvatar);
connect(client_.data(), &MatrixClient::joinedRoom, this, [=](const QString &room_id) {
emit showNotification("You joined the room.");
@@ -304,6 +356,19 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
}
});
+ connect(communitiesList_,
+ &CommunitiesList::communityChanged,
+ this,
+ [=](const QString &communityId) {
+ current_community_ = communityId;
+ if (communityId == "world") {
+ room_list_->setFilterRooms(false);
+ } else {
+ room_list_->setRoomFilter(
+ communityManager_[communityId]->getRoomList());
+ }
+ });
+
AvatarProvider::init(client);
instance_ = this;
@@ -359,6 +424,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
client_->setServer(homeserver);
client_->setAccessToken(token);
client_->getOwnProfile();
+ client_->getOwnCommunities();
cache_ = QSharedPointer<Cache>(new Cache(userid));
room_list_->setCache(cache_);
@@ -501,6 +567,18 @@ ChatPage::updateOwnProfileInfo(const QUrl &avatar_url, const QString &display_na
}
void
+ChatPage::updateOwnCommunitiesInfo(const QList<QString> &own_communities)
+{
+ for (int i = 0; i < own_communities.size(); i++) {
+ QSharedPointer<Community> community = QSharedPointer<Community>(new Community());
+
+ communityManager_[own_communities[i]] = community;
+ }
+
+ communitiesList_->setCommunities(communityManager_);
+}
+
+void
ChatPage::changeTopRoomInfo(const QString &room_id)
{
if (!state_manager_.contains(room_id))
diff --git a/src/CommunitiesList.cc b/src/CommunitiesList.cc
new file mode 100644
index 00000000..c40155e5
--- /dev/null
+++ b/src/CommunitiesList.cc
@@ -0,0 +1,150 @@
+#include "CommunitiesList.h"
+
+#include <QLabel>
+
+CommunitiesList::CommunitiesList(QSharedPointer<MatrixClient> client, QWidget *parent)
+ : QWidget(parent)
+ , client_(client)
+{
+ QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
+ sizePolicy.setHorizontalStretch(0);
+ sizePolicy.setVerticalStretch(1);
+ setSizePolicy(sizePolicy);
+
+ setStyleSheet("border-style: none;");
+
+ topLayout_ = new QVBoxLayout(this);
+ topLayout_->setSpacing(0);
+ topLayout_->setMargin(0);
+
+ setFixedWidth(ui::sidebar::CommunitiesSidebarSize);
+
+ scrollArea_ = new QScrollArea(this);
+ scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ scrollArea_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ scrollArea_->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
+ scrollArea_->setWidgetResizable(true);
+ scrollArea_->setAlignment(Qt::AlignLeading | Qt::AlignTop | Qt::AlignVCenter);
+
+ scrollAreaContents_ = new QWidget();
+
+ contentsLayout_ = new QVBoxLayout(scrollAreaContents_);
+ contentsLayout_->setSpacing(0);
+ contentsLayout_->setMargin(0);
+
+ WorldCommunityListItem *world_list_item = new WorldCommunityListItem();
+ contentsLayout_->addWidget(world_list_item);
+ communities_.insert("world", QSharedPointer<CommunitiesListItem>(world_list_item));
+ connect(world_list_item,
+ &WorldCommunityListItem::clicked,
+ this,
+ &CommunitiesList::highlightSelectedCommunity);
+ contentsLayout_->addStretch(1);
+
+ scrollArea_->setWidget(scrollAreaContents_);
+ topLayout_->addWidget(scrollArea_);
+
+ connect(client_.data(),
+ &MatrixClient::communityProfileRetrieved,
+ this,
+ [=](QString communityId, QJsonObject profile) {
+ client_->fetchCommunityAvatar(communityId,
+ QUrl(profile["avatar_url"].toString()));
+ });
+ connect(client_.data(),
+ SIGNAL(communityAvatarRetrieved(const QString &, const QPixmap &)),
+ this,
+ SLOT(updateCommunityAvatar(const QString &, const QPixmap &)));
+}
+
+CommunitiesList::~CommunitiesList() {}
+
+void
+CommunitiesList::setCommunities(const QMap<QString, QSharedPointer<Community>> &communities)
+{
+ communities_.clear();
+
+ // TODO: still not sure how to handle the "world" special-case
+ WorldCommunityListItem *world_list_item = new WorldCommunityListItem();
+ communities_.insert("world", QSharedPointer<CommunitiesListItem>(world_list_item));
+ connect(world_list_item,
+ &WorldCommunityListItem::clicked,
+ this,
+ &CommunitiesList::highlightSelectedCommunity);
+ contentsLayout_->insertWidget(0, world_list_item);
+
+ for (auto it = communities.constBegin(); it != communities.constEnd(); it++) {
+ const auto community_id = it.key();
+ const auto community = it.value();
+
+ addCommunity(community, community_id);
+
+ client_->fetchCommunityProfile(community_id);
+ client_->fetchCommunityRooms(community_id);
+ }
+
+ world_list_item->setPressedState(true);
+ emit communityChanged("world");
+}
+
+void
+CommunitiesList::clear()
+{
+ communities_.clear();
+}
+
+void
+CommunitiesList::addCommunity(QSharedPointer<Community> community, const QString &community_id)
+{
+ CommunitiesListItem *list_item =
+ new CommunitiesListItem(community, community_id, scrollArea_);
+
+ communities_.insert(community_id, QSharedPointer<CommunitiesListItem>(list_item));
+
+ client_->fetchCommunityAvatar(community_id, community->getAvatar());
+
+ contentsLayout_->insertWidget(contentsLayout_->count() - 1, list_item);
+
+ connect(list_item,
+ &CommunitiesListItem::clicked,
+ this,
+ &CommunitiesList::highlightSelectedCommunity);
+}
+
+void
+CommunitiesList::removeCommunity(const QString &community_id)
+{
+ communities_.remove(community_id);
+}
+
+void
+CommunitiesList::updateCommunityAvatar(const QString &community_id, const QPixmap &img)
+{
+ if (!communities_.contains(community_id)) {
+ qWarning() << "Avatar update on nonexistent community" << community_id;
+ return;
+ }
+
+ communities_.value(community_id)->setAvatar(img.toImage());
+}
+
+void
+CommunitiesList::highlightSelectedCommunity(const QString &community_id)
+{
+ emit communityChanged(community_id);
+
+ if (!communities_.contains(community_id)) {
+ qDebug() << "CommunitiesList: clicked unknown community";
+ return;
+ }
+
+ for (auto it = communities_.constBegin(); it != communities_.constEnd(); it++) {
+ if (it.key() != community_id) {
+ it.value()->setPressedState(false);
+ } else {
+ it.value()->setPressedState(true);
+ scrollArea_->ensureWidgetVisible(
+ qobject_cast<QWidget *>(it.value().data()));
+ }
+ }
+}
diff --git a/src/CommunitiesListItem.cc b/src/CommunitiesListItem.cc
new file mode 100644
index 00000000..a7789df7
--- /dev/null
+++ b/src/CommunitiesListItem.cc
@@ -0,0 +1,200 @@
+#include "CommunitiesListItem.h"
+
+CommunitiesListItem::CommunitiesListItem(QSharedPointer<Community> community,
+ QString community_id,
+ QWidget *parent)
+ : QWidget(parent)
+ , community_(community)
+ , communityId_(community_id)
+{
+ // menu_ = new Menu(this);
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ setFixedHeight(ui::sidebar::CommunitiesSidebarSize);
+ setFixedWidth(ui::sidebar::CommunitiesSidebarSize);
+}
+
+CommunitiesListItem::~CommunitiesListItem() {}
+
+void
+CommunitiesListItem::setCommunity(QSharedPointer<Community> community)
+{
+ community_ = community;
+}
+
+void
+CommunitiesListItem::setPressedState(bool state)
+{
+ if (isPressed_ != state) {
+ isPressed_ = state;
+ update();
+ }
+}
+
+void
+CommunitiesListItem::mousePressEvent(QMouseEvent *event)
+{
+ if (event->buttons() == Qt::RightButton) {
+ QWidget::mousePressEvent(event);
+ return;
+ }
+
+ emit clicked(communityId_);
+
+ setPressedState(true);
+}
+
+void
+CommunitiesListItem::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+
+ QPainter p(this);
+ p.setRenderHint(QPainter::TextAntialiasing);
+ p.setRenderHint(QPainter::SmoothPixmapTransform);
+ p.setRenderHint(QPainter::Antialiasing);
+
+ if (isPressed_)
+ p.fillRect(rect(), highlightedBackgroundColor_);
+ else if (underMouse())
+ p.fillRect(rect(), hoverBackgroundColor_);
+ else
+ p.fillRect(rect(), backgroundColor_);
+
+ QFont font;
+ font.setPixelSize(conf::fontSize);
+
+ p.setPen(QColor("#333"));
+
+ QRect avatarRegion((width() - IconSize) / 2, (height() - IconSize) / 2, IconSize, IconSize);
+
+ font.setBold(false);
+ p.setPen(Qt::NoPen);
+
+ // We using the first letter of room's name.
+ if (communityAvatar_.isNull()) {
+ QBrush brush;
+ brush.setStyle(Qt::SolidPattern);
+ brush.setColor("#eee");
+
+ p.setPen(Qt::NoPen);
+ p.setBrush(brush);
+
+ p.drawEllipse(avatarRegion.center(), IconSize / 2, IconSize / 2);
+
+ font.setPixelSize(conf::roomlist::fonts::bubble);
+ p.setFont(font);
+ p.setPen(QColor("#000"));
+ p.setBrush(Qt::NoBrush);
+ p.drawText(
+ avatarRegion.translated(0, -1), Qt::AlignCenter, QChar(community_->getName()[0]));
+ } else {
+ p.save();
+
+ QPainterPath path;
+ path.addEllipse(
+ (width() - IconSize) / 2, (height() - IconSize) / 2, IconSize, IconSize);
+ p.setClipPath(path);
+
+ p.drawPixmap(avatarRegion, communityAvatar_);
+ p.restore();
+ }
+
+ // TODO: Discord-style community ping counts?
+ /*if (unreadMsgCount_ > 0) {
+ QColor textColor("white");
+ QColor backgroundColor("#38A3D8");
+
+ QBrush brush;
+ brush.setStyle(Qt::SolidPattern);
+ brush.setColor(backgroundColor);
+
+ if (isPressed_)
+ brush.setColor(textColor);
+
+ QFont unreadCountFont;
+ unreadCountFont.setPixelSize(conf::roomlist::fonts::badge);
+ unreadCountFont.setBold(true);
+
+ p.setBrush(brush);
+ p.setPen(Qt::NoPen);
+ p.setFont(unreadCountFont);
+
+ int diameter = 20;
+
+ QRectF r(
+ width() - diameter - 5, height() - diameter - 5, diameter, diameter);
+
+ p.setPen(Qt::NoPen);
+ p.drawEllipse(r);
+
+ p.setPen(QPen(textColor));
+
+ if (isPressed_)
+ p.setPen(QPen(backgroundColor));
+
+ p.setBrush(Qt::NoBrush);
+ p.drawText(
+ r.translated(0, -0.5), Qt::AlignCenter, QString::number(unreadMsgCount_));
+ }*/
+}
+
+void
+CommunitiesListItem::contextMenuEvent(QContextMenuEvent *event)
+{
+ Q_UNUSED(event);
+
+ // menu_->popup(event->globalPos());
+}
+
+WorldCommunityListItem::WorldCommunityListItem(QWidget *parent)
+ : CommunitiesListItem(QSharedPointer<Community>(), "", parent)
+{}
+
+WorldCommunityListItem::~WorldCommunityListItem() {}
+
+void
+WorldCommunityListItem::mousePressEvent(QMouseEvent *event)
+{
+ if (event->buttons() == Qt::RightButton) {
+ QWidget::mousePressEvent(event);
+ return;
+ }
+
+ emit CommunitiesListItem::clicked("world");
+
+ setPressedState(true);
+}
+
+void
+WorldCommunityListItem::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+
+ static QPixmap worldIcon(":/icons/icons/ui/world.png");
+
+ QPainter p(this);
+ p.setRenderHint(QPainter::SmoothPixmapTransform);
+ p.setRenderHint(QPainter::Antialiasing);
+
+ if (isPressed())
+ p.fillRect(rect(), highlightedBackgroundColor_);
+ else if (underMouse())
+ p.fillRect(rect(), hoverBackgroundColor_);
+ else
+ p.fillRect(rect(), backgroundColor_);
+
+ QBrush brush;
+ brush.setStyle(Qt::SolidPattern);
+ brush.setColor("#FFFFFF");
+
+ p.setPen(Qt::NoPen);
+ p.setBrush(brush);
+
+ QRect avatarRegion((width() - IconSize) / 2, (height() - IconSize) / 2, IconSize, IconSize);
+ p.drawEllipse(avatarRegion.center(), IconSize / 2, IconSize / 2);
+ QPainterPath path;
+ path.addEllipse((width() - IconSize) / 2, (height() - IconSize) / 2, IconSize, IconSize);
+ p.setClipPath(path);
+
+ p.drawPixmap(avatarRegion, worldIcon);
+}
diff --git a/src/Community.cc b/src/Community.cc
new file mode 100644
index 00000000..df425e88
--- /dev/null
+++ b/src/Community.cc
@@ -0,0 +1,44 @@
+#include "include/Community.h"
+
+#include <QJsonArray>
+#include <QJsonValue>
+
+void
+Community::parseProfile(const QJsonObject &profile)
+{
+ if (profile["name"].type() == QJsonValue::Type::String) {
+ name_ = profile["name"].toString();
+ } else {
+ name_ = "Unnamed Community"; // TODO: what is correct here?
+ }
+
+ if (profile["avatar_url"].type() == QJsonValue::Type::String) {
+ avatar_ = QUrl(profile["avatar_url"].toString());
+ } else {
+ avatar_ = QUrl();
+ }
+
+ if (profile["short_description"].type() == QJsonValue::Type::String) {
+ short_description_ = profile["short_description"].toString();
+ } else {
+ short_description_ = "";
+ }
+
+ if (profile["long_description"].type() == QJsonValue::Type::String) {
+ long_description_ = profile["long_description"].toString();
+ } else {
+ long_description_ = "";
+ }
+}
+
+void
+Community::parseRooms(const QJsonObject &rooms)
+{
+ rooms_.clear();
+
+ for (auto i = 0; i < rooms["chunk"].toArray().size(); i++) {
+ rooms_.append(rooms["chunk"].toArray()[i].toObject()["room_id"].toString());
+ }
+
+ emit roomsChanged(rooms_);
+}
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index 1b2e020d..72467385 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -112,7 +112,6 @@ MatrixClient::login(const QString &username, const QString &password) noexcept
}
});
}
-
void
MatrixClient::logout() noexcept
{
@@ -445,6 +444,46 @@ MatrixClient::getOwnProfile() noexcept
}
void
+MatrixClient::getOwnCommunities() noexcept
+{
+ QUrlQuery query;
+ query.addQueryItem("access_token", token_);
+
+ QUrl endpoint(server_);
+ endpoint.setPath(clientApiUrl_ + "/joined_groups");
+ endpoint.setQuery(query);
+
+ QNetworkRequest request(QString(endpoint.toEncoded()));
+
+ QNetworkReply *reply = get(request);
+ connect(reply, &QNetworkReply::finished, this, [this, reply]() {
+ reply->deleteLater();
+
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (status >= 400) {
+ qWarning() << reply->errorString();
+ return;
+ }
+
+ auto data = reply->readAll();
+ auto json = QJsonDocument::fromJson(data).object();
+
+ try {
+ QList<QString> response;
+ for (auto it = json["groups"].toArray().constBegin();
+ it != json["groups"].toArray().constEnd();
+ it++) {
+ response.append(it->toString());
+ }
+ emit getOwnCommunitiesResponse(response);
+ } catch (DeserializationException &e) {
+ qWarning() << "Own communities:" << e.what();
+ }
+ });
+}
+
+void
MatrixClient::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url)
{
QList<QString> url_parts = avatar_url.toString().split("mxc://");
@@ -491,6 +530,113 @@ MatrixClient::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url)
}
void
+MatrixClient::fetchCommunityAvatar(const QString &communityId, const QUrl &avatar_url)
+{
+ QList<QString> url_parts = avatar_url.toString().split("mxc://");
+
+ if (url_parts.size() != 2) {
+ qDebug() << "Invalid format for community avatar " << avatar_url.toString();
+ return;
+ }
+
+ QUrlQuery query;
+ query.addQueryItem("width", "512");
+ query.addQueryItem("height", "512");
+ query.addQueryItem("method", "crop");
+
+ QString media_url =
+ QString("%1/_matrix/media/r0/thumbnail/%2").arg(getHomeServer().toString(), url_parts[1]);
+
+ QUrl endpoint(media_url);
+ endpoint.setQuery(query);
+
+ QNetworkRequest avatar_request(endpoint);
+
+ QNetworkReply *reply = get(avatar_request);
+ connect(reply, &QNetworkReply::finished, this, [this, reply, communityId]() {
+ reply->deleteLater();
+
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (status == 0 || status >= 400) {
+ qWarning() << reply->errorString();
+ return;
+ }
+
+ auto img = reply->readAll();
+
+ if (img.size() == 0)
+ return;
+
+ QPixmap pixmap;
+ pixmap.loadFromData(img);
+
+ emit communityAvatarRetrieved(communityId, pixmap);
+ });
+}
+
+void
+MatrixClient::fetchCommunityProfile(const QString &communityId)
+{
+ QUrlQuery query;
+ query.addQueryItem("access_token", token_);
+
+ QUrl endpoint(server_);
+ endpoint.setPath(clientApiUrl_ + "/groups/" + communityId + "/profile");
+ endpoint.setQuery(query);
+
+ QNetworkRequest request(QString(endpoint.toEncoded()));
+
+ QNetworkReply *reply = get(request);
+
+ connect(reply, &QNetworkReply::finished, this, [this, reply, communityId]() {
+ reply->deleteLater();
+
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (status >= 400) {
+ qWarning() << reply->errorString();
+ return;
+ }
+
+ auto data = reply->readAll();
+ const auto json = QJsonDocument::fromJson(data).object();
+
+ emit communityProfileRetrieved(communityId, json);
+ });
+}
+
+void
+MatrixClient::fetchCommunityRooms(const QString &communityId)
+{
+ QUrlQuery query;
+ query.addQueryItem("access_token", token_);
+
+ QUrl endpoint(server_);
+ endpoint.setPath(clientApiUrl_ + "/groups/" + communityId + "/rooms");
+ endpoint.setQuery(query);
+
+ QNetworkRequest request(QString(endpoint.toEncoded()));
+
+ QNetworkReply *reply = get(request);
+ connect(reply, &QNetworkReply::finished, this, [this, reply, communityId]() {
+ reply->deleteLater();
+
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (status >= 400) {
+ qWarning() << reply->errorString();
+ return;
+ }
+
+ auto data = reply->readAll();
+ const auto json = QJsonDocument::fromJson(data).object();
+
+ emit communityRoomsRetrieved(communityId, json);
+ });
+}
+
+void
MatrixClient::fetchUserAvatar(const QString &userId, const QUrl &avatarUrl)
{
QList<QString> url_parts = avatarUrl.toString().split("mxc://");
diff --git a/src/RoomInfoListItem.cc b/src/RoomInfoListItem.cc
index 551895d6..f8989948 100644
--- a/src/RoomInfoListItem.cc
+++ b/src/RoomInfoListItem.cc
@@ -315,10 +315,7 @@ RoomInfoListItem::clearUnreadMessageCount()
void
RoomInfoListItem::setPressedState(bool state)
{
- if (!isPressed_ && state) {
- isPressed_ = state;
- update();
- } else if (isPressed_ && !state) {
+ if (isPressed_ != state) {
isPressed_ = state;
update();
}
diff --git a/src/RoomList.cc b/src/RoomList.cc
index 0274cefe..30be6cf6 100644
--- a/src/RoomList.cc
+++ b/src/RoomList.cc
@@ -47,7 +47,7 @@ RoomList::RoomList(QSharedPointer<MatrixClient> client,
scrollArea_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea_->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
scrollArea_->setWidgetResizable(true);
- scrollArea_->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter);
+ scrollArea_->setAlignment(Qt::AlignLeading | Qt::AlignTop | Qt::AlignVCenter);
scrollAreaContents_ = new QWidget(this);
@@ -181,6 +181,8 @@ RoomList::setInitialRooms(const QMap<QString, QSharedPointer<RoomSettings>> &set
if (rooms_.isEmpty())
return;
+ setFilterRooms(filterRooms_);
+
auto first_room = rooms_.first();
first_room->setPressedState(true);
@@ -271,6 +273,8 @@ RoomList::highlightSelectedRoom(const QString &room_id)
qobject_cast<QWidget *>(it.value().data()));
}
}
+
+ selectedRoom_ = room_id;
}
void
@@ -374,6 +378,46 @@ RoomList::closeLeaveRoomDialog(bool leaving, const QString &room_id)
}
void
+RoomList::setFilterRooms(bool filterRooms)
+{
+ filterRooms_ = filterRooms;
+
+ for (int i = 0; i < contentsLayout_->count(); i++) {
+ // If roomFilter_ contains the room for the current RoomInfoListItem,
+ // show the list item, otherwise hide it
+ RoomInfoListItem *listitem =
+ (RoomInfoListItem *)contentsLayout_->itemAt(i)->widget();
+
+ if (listitem != nullptr) {
+ if (!filterRooms) {
+ contentsLayout_->itemAt(i)->widget()->show();
+ } else if (roomFilter_.contains(listitem->roomId())) {
+ contentsLayout_->itemAt(i)->widget()->show();
+ } else {
+ contentsLayout_->itemAt(i)->widget()->hide();
+ }
+ }
+ }
+
+ if (filterRooms_ && !roomFilter_.contains(selectedRoom_)) {
+ RoomInfoListItem *firstVisibleRoom = nullptr;
+ for (int i = 0; i < contentsLayout_->count(); i++) {
+ QWidget *item = contentsLayout_->itemAt(i)->widget();
+ if (item != nullptr && item->isVisible()) {
+ firstVisibleRoom = (RoomInfoListItem *)item;
+ break;
+ }
+ }
+ if (firstVisibleRoom != nullptr) {
+ highlightSelectedRoom(firstVisibleRoom->roomId());
+ }
+ } else {
+ scrollArea_->ensureWidgetVisible(
+ qobject_cast<QWidget *>(rooms_.value(selectedRoom_).data()));
+ }
+}
+
+void
RoomList::paintEvent(QPaintEvent *)
{
QStyleOption opt;
@@ -394,6 +438,13 @@ RoomList::syncInvites(const std::map<std::string, mtx::responses::InvitedRoom> &
}
void
+RoomList::setRoomFilter(QList<QString> room_ids)
+{
+ roomFilter_ = room_ids;
+ setFilterRooms(true);
+}
+
+void
RoomList::addInvitedRoom(const QString &room_id, const mtx::responses::InvitedRoom &room)
{
auto room_item = new RoomInfoListItem(room_id, room, scrollArea_);
|