diff --git a/CMakeLists.txt b/CMakeLists.txt
index 864f42a1..e1d835f6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -106,6 +106,7 @@ set(SRC_FILES
src/dialogs/Logout.cc
src/dialogs/ReadReceipts.cc
src/dialogs/ReCaptcha.cpp
+ src/dialogs/RoomSettings.cpp
# Emoji
src/emoji/Category.cc
@@ -222,6 +223,7 @@ qt5_wrap_cpp(MOC_HEADERS
include/dialogs/Logout.h
include/dialogs/ReadReceipts.h
include/dialogs/ReCaptcha.hpp
+ include/dialogs/RoomSettings.hpp
# Emoji
include/emoji/Category.h
diff --git a/include/Cache.h b/include/Cache.h
index a92f6bc5..db5dba00 100644
--- a/include/Cache.h
+++ b/include/Cache.h
@@ -64,6 +64,8 @@ struct RoomInfo
std::string avatar_url;
//! Whether or not the room is an invite.
bool is_invite = false;
+ //! Total number of members in the room.
+ int16_t member_count = 0;
};
inline void
@@ -73,6 +75,9 @@ to_json(json &j, const RoomInfo &info)
j["topic"] = info.topic;
j["avatar_url"] = info.avatar_url;
j["is_invite"] = info.is_invite;
+
+ if (info.member_count != 0)
+ j["member_count"] = info.member_count;
}
inline void
@@ -82,6 +87,9 @@ from_json(const json &j, RoomInfo &info)
info.topic = j.at("topic");
info.avatar_url = j.at("avatar_url");
info.is_invite = j.at("is_invite");
+
+ if (j.count("member_count"))
+ info.member_count = j.at("member_count");
}
//! Basic information per member;
diff --git a/include/ChatPage.h b/include/ChatPage.h
index 831f3933..1582db06 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -80,6 +80,7 @@ public:
}
QSharedPointer<UserSettings> userSettings() { return userSettings_; }
+ QSharedPointer<Cache> cache() { return cache_; }
signals:
void contentLoaded();
diff --git a/include/MainWindow.h b/include/MainWindow.h
index d747f9b4..08d7e53e 100644
--- a/include/MainWindow.h
+++ b/include/MainWindow.h
@@ -50,6 +50,7 @@ class JoinRoom;
class LeaveRoom;
class Logout;
class ReCaptcha;
+class RoomSettings;
}
class MainWindow : public QMainWindow
@@ -68,6 +69,7 @@ public:
std::function<void(const mtx::requests::CreateRoom &request)> callback);
void openJoinRoomDialog(std::function<void(const QString &room_id)> callback);
void openLogoutDialog(std::function<void()> callback);
+ void openRoomSettings(const QString &room_id = "");
protected:
void closeEvent(QCloseEvent *event);
@@ -147,4 +149,8 @@ private:
QSharedPointer<OverlayModal> logoutModal_;
//! Logout dialog.
QSharedPointer<dialogs::Logout> logoutDialog_;
+ //! Room settings modal.
+ QSharedPointer<OverlayModal> roomSettingsModal_;
+ //! Room settings dialog.
+ QSharedPointer<dialogs::RoomSettings> roomSettingsDialog_;
};
diff --git a/include/TopRoomBar.h b/include/TopRoomBar.h
index 12fd0645..37a0b61b 100644
--- a/include/TopRoomBar.h
+++ b/include/TopRoomBar.h
@@ -21,7 +21,6 @@
#include <QIcon>
#include <QImage>
#include <QLabel>
-#include <QMenu>
#include <QPaintEvent>
#include <QSharedPointer>
#include <QVBoxLayout>
@@ -66,8 +65,9 @@ private:
QLabel *nameLabel_;
Label *topicLabel_;
- QMenu *menu_;
+ Menu *menu_;
QAction *leaveRoom_;
+ QAction *roomSettings_;
QAction *inviteUsers_;
FlatButton *settingsBtn_;
diff --git a/include/dialogs/RoomSettings.hpp b/include/dialogs/RoomSettings.hpp
new file mode 100644
index 00000000..fbbc2e02
--- /dev/null
+++ b/include/dialogs/RoomSettings.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+#include <QFrame>
+#include <QImage>
+
+#include "Cache.h"
+
+class FlatButton;
+class TextField;
+class Avatar;
+class QPixmap;
+class QLayout;
+class QLabel;
+
+template<class T>
+class QSharedPointer;
+
+class TopSection : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
+
+public:
+ TopSection(const RoomInfo &info, const QImage &img, QWidget *parent = nullptr)
+ : QWidget{parent}
+ , info_{std::move(info)}
+ {
+ textColor_ = palette().color(QPalette::Text);
+ avatar_ = QPixmap::fromImage(img.scaled(
+ AvatarSize, AvatarSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ }
+
+ QSize sizeHint() const override
+ {
+ QFont font;
+ font.setPixelSize(18);
+ return QSize(200, AvatarSize + QFontMetrics(font).ascent() + 6 * Padding);
+ }
+
+ QColor textColor() const { return textColor_; }
+ void setTextColor(QColor &color) { textColor_ = color; }
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ static constexpr int AvatarSize = 72;
+ static constexpr int Padding = 5;
+
+ RoomInfo info_;
+ QPixmap avatar_;
+ QColor textColor_;
+};
+
+namespace dialogs {
+
+class RoomSettings : public QFrame
+{
+ Q_OBJECT
+public:
+ RoomSettings(const QString &room_id,
+ QSharedPointer<Cache> cache,
+ QWidget *parent = nullptr);
+
+signals:
+ void closing();
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ static constexpr int AvatarSize = 64;
+
+ void setAvatar(const QImage &img) { avatarImg_ = img; }
+
+ QSharedPointer<Cache> cache_;
+
+ // Button section
+ FlatButton *saveBtn_;
+ FlatButton *cancelBtn_;
+
+ RoomInfo info_;
+ QString room_id_;
+ QImage avatarImg_;
+};
+
+} // dialogs
diff --git a/include/ui/Painter.h b/include/ui/Painter.h
index 9558b004..8de39651 100644
--- a/include/ui/Painter.h
+++ b/include/ui/Painter.h
@@ -103,7 +103,7 @@ public:
drawPixmap(region, pix);
}
- void drawLetterAvatar(const QChar &c,
+ void drawLetterAvatar(const QString &c,
const QColor &penColor,
const QColor &brushColor,
int w,
diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss
index 28850711..89582348 100644
--- a/resources/styles/nheko-dark.qss
+++ b/resources/styles/nheko-dark.qss
@@ -136,6 +136,7 @@ dialogs--Logout,
dialogs--ReCaptcha,
dialogs--LeaveRoom,
dialogs--CreateRoom,
+dialogs--RoomSettings,
dialogs--InviteUsers,
dialogs--ReadReceipts,
dialogs--JoinRoom,
@@ -147,6 +148,10 @@ dialogs--JoinRoom > QLineEdit {
color: #caccd1;
}
+TopSection {
+ qproperty-textColor: #caccd1;
+}
+
QListWidget,
WelcomePage,
LoginPage,
diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss
index e1cb839f..6538a780 100644
--- a/resources/styles/nheko.qss
+++ b/resources/styles/nheko.qss
@@ -138,6 +138,7 @@ dialogs--Logout,
dialogs--ReCaptcha,
dialogs--LeaveRoom,
dialogs--CreateRoom,
+dialogs--RoomSettings,
dialogs--InviteUsers,
dialogs--ReadReceipts,
dialogs--JoinRoom,
@@ -147,6 +148,10 @@ QListWidget {
color: #333;
}
+TopSection {
+ qproperty-textColor: #333;
+}
+
WelcomePage,
LoginPage,
RegisterPage {
diff --git a/src/Cache.cc b/src/Cache.cc
index 699774ce..60181afc 100644
--- a/src/Cache.cc
+++ b/src/Cache.cc
@@ -538,9 +538,10 @@ Cache::getRoomInfo(const std::vector<std::string> &rooms)
// Check if the room is joined.
if (lmdb::dbi_get(txn, roomsDb_, lmdb::val(room), data)) {
try {
- room_info.emplace(
- QString::fromStdString(room),
- json::parse(std::string(data.data(), data.size())));
+ RoomInfo tmp = json::parse(std::string(data.data(), data.size()));
+ tmp.member_count = getMembersDb(txn, room).size(txn);
+
+ room_info.emplace(QString::fromStdString(room), std::move(tmp));
} catch (const json::exception &e) {
qWarning()
<< "failed to parse room info:" << QString::fromStdString(room)
@@ -550,9 +551,12 @@ Cache::getRoomInfo(const std::vector<std::string> &rooms)
// Check if the room is an invite.
if (lmdb::dbi_get(txn, invitesDb_, lmdb::val(room), data)) {
try {
- room_info.emplace(
- QString::fromStdString(room),
- json::parse(std::string(data.data(), data.size())));
+ RoomInfo tmp =
+ json::parse(std::string(data.data(), data.size()));
+ tmp.member_count = getInviteMembersDb(txn, room).size(txn);
+
+ room_info.emplace(QString::fromStdString(room),
+ std::move(tmp));
} catch (const json::exception &e) {
qWarning() << "failed to parse room info for invite:"
<< QString::fromStdString(room)
@@ -581,7 +585,8 @@ Cache::roomInfo(bool withInvites)
// Gather info about the joined rooms.
auto roomsCursor = lmdb::cursor::open(txn, roomsDb_);
while (roomsCursor.get(room_id, room_data, MDB_NEXT)) {
- RoomInfo tmp = json::parse(std::move(room_data));
+ RoomInfo tmp = json::parse(std::move(room_data));
+ tmp.member_count = getMembersDb(txn, room_id).size(txn);
result.insert(QString::fromStdString(std::move(room_id)), std::move(tmp));
}
roomsCursor.close();
@@ -590,7 +595,8 @@ Cache::roomInfo(bool withInvites)
// Gather info about the invites.
auto invitesCursor = lmdb::cursor::open(txn, invitesDb_);
while (invitesCursor.get(room_id, room_data, MDB_NEXT)) {
- RoomInfo tmp = json::parse(room_data);
+ RoomInfo tmp = json::parse(room_data);
+ tmp.member_count = getInviteMembersDb(txn, room_id).size(txn);
result.insert(QString::fromStdString(std::move(room_id)), std::move(tmp));
}
invitesCursor.close();
diff --git a/src/CommunitiesListItem.cc b/src/CommunitiesListItem.cc
index e86216e8..0650c0cf 100644
--- a/src/CommunitiesListItem.cc
+++ b/src/CommunitiesListItem.cc
@@ -2,6 +2,7 @@
#include "Painter.h"
#include "Ripple.h"
#include "RippleOverlay.h"
+#include "Utils.h"
CommunitiesListItem::CommunitiesListItem(QSharedPointer<Community> community,
QString community_id,
@@ -74,7 +75,7 @@ CommunitiesListItem::paintEvent(QPaintEvent *)
font.setPixelSize(conf::roomlist::fonts::communityBubble);
p.setFont(font);
- p.drawLetterAvatar(community_->getName()[0],
+ p.drawLetterAvatar(utils::firstChar(community_->getName()),
avatarFgColor_,
avatarBgColor_,
width(),
diff --git a/src/MainWindow.cc b/src/MainWindow.cc
index c59ce2d9..66f956a5 100644
--- a/src/MainWindow.cc
+++ b/src/MainWindow.cc
@@ -41,6 +41,7 @@
#include "dialogs/JoinRoom.h"
#include "dialogs/LeaveRoom.h"
#include "dialogs/Logout.h"
+#include "dialogs/RoomSettings.hpp"
MainWindow *MainWindow::instance_ = nullptr;
@@ -263,6 +264,27 @@ MainWindow::hasActiveUser()
}
void
+MainWindow::openRoomSettings(const QString &room_id)
+{
+ const auto roomToSearch = room_id.isEmpty() ? chat_page_->currentRoom() : "";
+
+ qDebug() << "room settings" << roomToSearch;
+
+ roomSettingsDialog_ = QSharedPointer<dialogs::RoomSettings>(
+ new dialogs::RoomSettings(roomToSearch, chat_page_->cache(), this));
+
+ connect(roomSettingsDialog_.data(), &dialogs::RoomSettings::closing, this, [this]() {
+ roomSettingsModal_->hide();
+ });
+
+ roomSettingsModal_ =
+ QSharedPointer<OverlayModal>(new OverlayModal(this, roomSettingsDialog_.data()));
+ roomSettingsModal_->setColor(QColor(30, 30, 30, 170));
+
+ roomSettingsModal_->show();
+}
+
+void
MainWindow::openLeaveRoomDialog(const QString &room_id)
{
auto roomToLeave = room_id.isEmpty() ? chat_page_->currentRoom() : room_id;
diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc
index 3b60ab6a..beca1d51 100644
--- a/src/TopRoomBar.cc
+++ b/src/TopRoomBar.cc
@@ -95,8 +95,14 @@ TopRoomBar::TopRoomBar(QWidget *parent)
MainWindow::instance()->openLeaveRoomDialog();
});
+ roomSettings_ = new QAction(tr("Settings"), this);
+ connect(roomSettings_, &QAction::triggered, this, []() {
+ MainWindow::instance()->openRoomSettings();
+ });
+
menu_->addAction(inviteUsers_);
menu_->addAction(leaveRoom_);
+ menu_->addAction(roomSettings_);
connect(settingsBtn_, &QPushButton::clicked, this, [this]() {
auto pos = mapToGlobal(settingsBtn_->pos());
diff --git a/src/dialogs/RoomSettings.cpp b/src/dialogs/RoomSettings.cpp
new file mode 100644
index 00000000..8fcbc82f
--- /dev/null
+++ b/src/dialogs/RoomSettings.cpp
@@ -0,0 +1,147 @@
+#include "Avatar.h"
+#include "Config.h"
+#include "FlatButton.h"
+#include "Painter.h"
+#include "Utils.h"
+#include "dialogs/RoomSettings.hpp"
+
+#include <QComboBox>
+#include <QLabel>
+#include <QPainter>
+#include <QPixmap>
+#include <QSharedPointer>
+#include <QStyleOption>
+#include <QVBoxLayout>
+
+using namespace dialogs;
+
+RoomSettings::RoomSettings(const QString &room_id, QSharedPointer<Cache> cache, QWidget *parent)
+ : QFrame(parent)
+ , cache_{cache}
+ , room_id_{std::move(room_id)}
+{
+ setMaximumWidth(385);
+
+ try {
+ auto res = cache_->getRoomInfo({room_id_.toStdString()});
+ info_ = res[room_id_];
+
+ setAvatar(QImage::fromData(cache_->image(info_.avatar_url)));
+ } catch (const lmdb::error &e) {
+ qWarning() << "failed to retrieve room info from cache" << room_id;
+ }
+
+ auto layout = new QVBoxLayout(this);
+ layout->setSpacing(30);
+ layout->setMargin(20);
+
+ saveBtn_ = new FlatButton("SAVE", this);
+ saveBtn_->setFontSize(conf::btn::fontSize);
+ cancelBtn_ = new FlatButton(tr("CANCEL"), this);
+ cancelBtn_->setFontSize(conf::btn::fontSize);
+
+ auto btnLayout = new QHBoxLayout();
+ btnLayout->setSpacing(0);
+ btnLayout->setMargin(0);
+ btnLayout->addStretch(1);
+ btnLayout->addWidget(saveBtn_);
+ btnLayout->addWidget(cancelBtn_);
+
+ auto notifOptionLayout_ = new QHBoxLayout;
+ notifOptionLayout_->setMargin(5);
+ auto themeLabel_ = new QLabel(tr("Notifications"), this);
+ auto notifCombo = new QComboBox(this);
+ notifCombo->addItem("Nothing");
+ notifCombo->addItem("Mentions only");
+ notifCombo->addItem("All messages");
+ themeLabel_->setStyleSheet("font-size: 15px;");
+
+ notifOptionLayout_->addWidget(themeLabel_);
+ notifOptionLayout_->addWidget(notifCombo, 0, Qt::AlignBottom | Qt::AlignRight);
+
+ layout->addWidget(new TopSection(info_, avatarImg_, this));
+ layout->addLayout(notifOptionLayout_);
+ layout->addLayout(btnLayout);
+
+ connect(cancelBtn_, &FlatButton::clicked, this, &RoomSettings::closing);
+ connect(saveBtn_, &FlatButton::clicked, this, [this]() { emit closing(); });
+}
+
+void
+RoomSettings::paintEvent(QPaintEvent *)
+{
+ QStyleOption opt;
+ opt.init(this);
+ QPainter p(this);
+ style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+}
+
+void
+TopSection::paintEvent(QPaintEvent *)
+{
+ Painter p(this);
+ PainterHighQualityEnabler hq(p);
+
+ constexpr int textPadding = 23;
+ constexpr int textStartX = AvatarSize + 5 * Padding;
+ const int availableTextWidth = width() - textStartX;
+
+ constexpr int nameFont = 15;
+ constexpr int membersFont = 14;
+ constexpr int labelFont = 18;
+
+ QFont font;
+ font.setPixelSize(labelFont);
+ font.setWeight(70);
+
+ p.setFont(font);
+ p.setPen(textColor());
+ p.drawTextLeft(Padding, Padding, "Room settings");
+ p.translate(0, textPadding + QFontMetrics(p.font()).ascent());
+
+ p.save();
+ p.translate(textStartX, 2 * Padding);
+
+ // Draw the name.
+ font.setPixelSize(membersFont);
+ const auto members = QString("%1 members").arg(info_.member_count);
+
+ font.setPixelSize(nameFont);
+ const auto name = QFontMetrics(font).elidedText(
+ QString::fromStdString(info_.name), Qt::ElideRight, availableTextWidth - 4 * Padding);
+
+ font.setWeight(60);
+ p.setFont(font);
+ p.drawTextLeft(0, 0, name);
+
+ // Draw the number of members
+ p.translate(0, QFontMetrics(p.font()).ascent() + 2 * Padding);
+
+ font.setPixelSize(membersFont);
+ font.setWeight(50);
+ p.setFont(font);
+ p.drawTextLeft(0, 0, members);
+ p.restore();
+
+ if (avatar_.isNull()) {
+ font.setPixelSize(AvatarSize / 2);
+ font.setWeight(60);
+ p.setFont(font);
+
+ p.translate(Padding, Padding);
+ p.drawLetterAvatar(utils::firstChar(name),
+ QColor("white"),
+ QColor("black"),
+ AvatarSize + Padding,
+ AvatarSize + Padding,
+ AvatarSize);
+ } else {
+ QRect avatarRegion(Padding, Padding, AvatarSize, AvatarSize);
+
+ QPainterPath pp;
+ pp.addEllipse(avatarRegion.center(), AvatarSize, AvatarSize);
+
+ p.setClipPath(pp);
+ p.drawPixmap(avatarRegion, avatar_);
+ }
+}
|