Handle incoming invites

fixes #128
This commit is contained in:
Konstantinos Sideris 2017-12-19 22:36:12 +02:00
parent 101bf47443
commit f11044b5eb
12 changed files with 352 additions and 93 deletions

View File

@ -19,6 +19,7 @@
#include <QDir>
#include <lmdb++.h>
#include <mtx/responses.hpp>
class RoomState;
@ -33,10 +34,15 @@ public:
QString nextBatchToken() const;
QMap<QString, RoomState> states();
using Invites = std::map<std::string, mtx::responses::InvitedRoom>;
Invites invites();
void setInvites(const Invites &invites);
void deleteData();
void unmount() { isMounted_ = false; };
void removeRoom(const QString &roomid);
void removeInvite(const QString &roomid);
void setup();
bool isFormatValid();
@ -49,6 +55,7 @@ private:
lmdb::env env_;
lmdb::dbi stateDb_;
lmdb::dbi roomDb_;
lmdb::dbi invitesDb_;
bool isMounted_;

View File

@ -78,6 +78,7 @@ private slots:
void logout();
void addRoom(const QString &room_id);
void removeRoom(const QString &room_id);
void removeInvite(const QString &room_id);
private:
using UserID = QString;

View File

@ -21,6 +21,8 @@
#include <QSharedPointer>
#include <QWidget>
#include <mtx/responses.hpp>
#include "RoomState.h"
class Menu;
@ -52,12 +54,17 @@ class RoomInfoListItem : public QWidget
Q_PROPERTY(QColor highlightedSubtitleColor READ highlightedSubtitleColor WRITE
setHighlightedSubtitleColor)
Q_PROPERTY(QColor btnColor READ btnColor WRITE setBtnColor)
Q_PROPERTY(QColor btnTextColor READ btnTextColor WRITE setBtnTextColor)
public:
RoomInfoListItem(QSharedPointer<RoomSettings> settings,
RoomState state,
QString room_id,
QWidget *parent = 0);
RoomInfoListItem(QString room_id, mtx::responses::InvitedRoom room, QWidget *parent = 0);
~RoomInfoListItem();
void updateUnreadMessageCount(int count);
@ -71,35 +78,36 @@ public:
void setAvatar(const QImage &avatar_image);
void setDescriptionMessage(const DescInfo &info);
inline QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; }
inline QColor hoverBackgroundColor() const { return hoverBackgroundColor_; }
inline QColor backgroundColor() const { return backgroundColor_; }
QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; }
QColor hoverBackgroundColor() const { return hoverBackgroundColor_; }
QColor backgroundColor() const { return backgroundColor_; }
inline QColor highlightedTitleColor() const { return highlightedTitleColor_; }
inline QColor highlightedSubtitleColor() const { return highlightedSubtitleColor_; }
QColor highlightedTitleColor() const { return highlightedTitleColor_; }
QColor highlightedSubtitleColor() const { return highlightedSubtitleColor_; }
inline QColor titleColor() const { return titleColor_; }
inline QColor subtitleColor() const { return subtitleColor_; }
QColor titleColor() const { return titleColor_; }
QColor subtitleColor() const { return subtitleColor_; }
QColor btnColor() const { return btnColor_; }
QColor btnTextColor() const { return btnTextColor_; }
inline void setHighlightedBackgroundColor(QColor &color)
{
highlightedBackgroundColor_ = color;
}
inline void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; }
inline void setBackgroundColor(QColor &color) { backgroundColor_ = color; }
void setHighlightedBackgroundColor(QColor &color) { highlightedBackgroundColor_ = color; }
void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; }
void setBackgroundColor(QColor &color) { backgroundColor_ = color; }
inline void setHighlightedTitleColor(QColor &color) { highlightedTitleColor_ = color; }
inline void setHighlightedSubtitleColor(QColor &color)
{
highlightedSubtitleColor_ = color;
}
void setHighlightedTitleColor(QColor &color) { highlightedTitleColor_ = color; }
void setHighlightedSubtitleColor(QColor &color) { highlightedSubtitleColor_ = color; }
inline void setTitleColor(QColor &color) { titleColor_ = color; }
inline void setSubtitleColor(QColor &color) { subtitleColor_ = color; }
void setTitleColor(QColor &color) { titleColor_ = color; }
void setSubtitleColor(QColor &color) { subtitleColor_ = color; }
void setBtnColor(QColor &color) { btnColor_ = color; }
void setBtnTextColor(QColor &color) { btnTextColor_ = color; }
signals:
void clicked(const QString &room_id);
void leaveRoom(const QString &room_id);
void acceptInvite(const QString &room_id);
void declineInvite(const QString &room_id);
public slots:
void setPressedState(bool state);
@ -111,15 +119,33 @@ protected:
void contextMenuEvent(QContextMenuEvent *event) override;
private:
QString notificationText();
void init(QWidget *parent);
QString roomName()
{
if (roomType_ == RoomType::Joined)
return state_.getName();
const int Padding = 7;
const int IconSize = 48;
return roomName_;
}
QString notificationText();
RippleOverlay *ripple_overlay_;
enum class RoomType
{
Joined,
Invited,
};
RoomType roomType_ = RoomType::Joined;
// State information for the joined rooms.
RoomState state_;
// State information for the invited rooms.
mtx::responses::InvitedRoom invitedRoom_;
QString roomId_;
QString roomName_;
@ -135,7 +161,6 @@ private:
bool isPressed_ = false;
int maxHeight_;
int unreadMsgCount_ = 0;
QColor highlightedBackgroundColor_;
@ -147,4 +172,10 @@ private:
QColor titleColor_;
QColor subtitleColor_;
QColor btnColor_;
QColor btnTextColor_;
QRectF acceptBtnRegion_;
QRectF declineBtnRegion_;
};

View File

@ -24,6 +24,8 @@
#include <QVBoxLayout>
#include <QWidget>
#include <mtx.hpp>
#include "dialogs/LeaveRoom.h"
class LeaveRoomDialog;
@ -47,17 +49,21 @@ public:
const QMap<QString, RoomState> &states);
void sync(const QMap<QString, RoomState> &states,
QMap<QString, QSharedPointer<RoomSettings>> &settings);
void syncInvites(const std::map<std::string, mtx::responses::InvitedRoom> &rooms);
void clear();
void addRoom(const QMap<QString, QSharedPointer<RoomSettings>> &settings,
const RoomState &state,
const QString &room_id);
void addInvitedRoom(const QString &room_id, const mtx::responses::InvitedRoom &room);
void removeRoom(const QString &room_id, bool reset);
signals:
void roomChanged(const QString &room_id);
void totalUnreadMessageCountUpdated(int count);
void acceptInvite(const QString &room_id);
void declineInvite(const QString &room_id);
public slots:
void updateRoomAvatar(const QString &roomid, const QPixmap &img);

@ -1 +1 @@
Subproject commit ff5a9c887af1a8cffd13876abcf4c079a8525bd3
Subproject commit 582c6956c464e80f738c57ded239c3fb613f1c24

View File

@ -49,6 +49,9 @@ RoomInfoListItem {
qproperty-highlightedTitleColor: #e4e5e8;
qproperty-highlightedSubtitleColor: #e4e5e8;
qproperty-btnColor: #414A59;
qproperty-btnTextColor: white;
}
LoadingIndicator {

View File

@ -47,6 +47,9 @@ RoomInfoListItem {
qproperty-highlightedTitleColor: white;
qproperty-highlightedSubtitleColor: white;
qproperty-btnColor: #ccc;
qproperty-btnTextColor: #333;
}
#ChatPageLoadSpinner {

View File

@ -55,6 +55,9 @@ RoomInfoListItem {
qproperty-highlightedTitleColor: palette(light);
qproperty-highlightedSubtitleColor: palette(light);
qproperty-btnColor: palette(light);
qproperty-btnTextColor: palette(text);
}
LoadingIndicator {

View File

@ -33,6 +33,7 @@ Cache::Cache(const QString &userId)
: env_{nullptr}
, stateDb_{0}
, roomDb_{0}
, invitesDb_{0}
, isMounted_{false}
, userId_{userId}
{}
@ -86,9 +87,10 @@ Cache::setup()
env_.open(statePath.toStdString().c_str());
}
auto txn = lmdb::txn::begin(env_);
stateDb_ = lmdb::dbi::open(txn, "state", MDB_CREATE);
roomDb_ = lmdb::dbi::open(txn, "rooms", MDB_CREATE);
auto txn = lmdb::txn::begin(env_);
stateDb_ = lmdb::dbi::open(txn, "state", MDB_CREATE);
roomDb_ = lmdb::dbi::open(txn, "rooms", MDB_CREATE);
invitesDb_ = lmdb::dbi::open(txn, "invites", MDB_CREATE);
txn.commit();
@ -174,6 +176,20 @@ Cache::removeRoom(const QString &roomid)
txn.commit();
}
void
Cache::removeInvite(const QString &room_id)
{
if (!isMounted_)
return;
auto txn = lmdb::txn::begin(env_, nullptr, 0);
lmdb::dbi_del(
txn, invitesDb_, lmdb::val(room_id.toUtf8(), room_id.toUtf8().size()), nullptr);
txn.commit();
}
QMap<QString, RoomState>
Cache::states()
{
@ -310,3 +326,76 @@ Cache::setCurrentFormat()
txn.commit();
}
std::map<std::string, mtx::responses::InvitedRoom>
Cache::invites()
{
std::map<std::string, mtx::responses::InvitedRoom> invites;
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
auto cursor = lmdb::cursor::open(txn, invitesDb_);
std::string room_id;
std::string payload;
mtx::responses::InvitedRoom state;
while (cursor.get(room_id, payload, MDB_NEXT)) {
state = nlohmann::json::parse(payload);
invites.emplace(room_id, state);
}
if (invites.size() > 0)
qDebug() << "Retrieved" << invites.size() << "invites";
cursor.close();
txn.commit();
return invites;
}
void
Cache::setInvites(const std::map<std::string, mtx::responses::InvitedRoom> &invites)
{
if (!isMounted_)
return;
using Aliases = mtx::events::StrippedEvent<mtx::events::state::Aliases>;
using Avatar = mtx::events::StrippedEvent<mtx::events::state::Avatar>;
using Member = mtx::events::StrippedEvent<mtx::events::state::Member>;
using Name = mtx::events::StrippedEvent<mtx::events::state::Name>;
using Topic = mtx::events::StrippedEvent<mtx::events::state::Topic>;
try {
auto txn = lmdb::txn::begin(env_);
for (auto it = invites.cbegin(); it != invites.cend(); ++it) {
nlohmann::json j;
for (const auto &e : it->second.invite_state) {
if (mpark::holds_alternative<Name>(e)) {
j["invite_state"]["events"].push_back(mpark::get<Name>(e));
} else if (mpark::holds_alternative<Topic>(e)) {
j["invite_state"]["events"].push_back(mpark::get<Topic>(e));
} else if (mpark::holds_alternative<Avatar>(e)) {
j["invite_state"]["events"].push_back(
mpark::get<Avatar>(e));
} else if (mpark::holds_alternative<Aliases>(e)) {
j["invite_state"]["events"].push_back(
mpark::get<Aliases>(e));
} else if (mpark::holds_alternative<Member>(e)) {
j["invite_state"]["events"].push_back(
mpark::get<Member>(e));
}
}
lmdb::dbi_put(txn, invitesDb_, lmdb::val(it->first), lmdb::val(j.dump()));
}
txn.commit();
} catch (const lmdb::error &e) {
qCritical() << "setInvitedRooms: " << e.what();
unmount();
}
}

View File

@ -134,6 +134,9 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
connect(
room_list_, &RoomList::roomChanged, view_manager_, &TimelineViewManager::setHistoryView);
connect(room_list_, &RoomList::acceptInvite, client_.data(), &MatrixClient::joinRoom);
connect(room_list_, &RoomList::declineInvite, client_.data(), &MatrixClient::leaveRoom);
connect(view_manager_,
&TimelineViewManager::clearRoomMessageCount,
room_list_,
@ -266,8 +269,9 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
this,
&ChatPage::updateOwnProfileInfo);
connect(client_.data(), &MatrixClient::ownAvatarRetrieved, this, &ChatPage::setOwnAvatar);
connect(client_.data(), &MatrixClient::joinedRoom, this, [=]() {
connect(client_.data(), &MatrixClient::joinedRoom, this, [=](const QString &room_id) {
emit showNotification("You joined the room.");
removeInvite(room_id);
});
connect(client_.data(), &MatrixClient::invitedUser, this, [=](QString, QString user) {
emit showNotification(QString("Invited user %1").arg(user));
@ -400,8 +404,11 @@ ChatPage::syncCompleted(const mtx::responses::Sync &response)
auto stateDiff = generateMembershipDifference(response.rooms.join, state_manager_);
QtConcurrent::run(cache_.data(), &Cache::setState, nextBatchToken, stateDiff);
QtConcurrent::run(cache_.data(), &Cache::setInvites, response.rooms.invite);
room_list_->sync(state_manager_, settingsManager_);
room_list_->syncInvites(response.rooms.invite);
view_manager_->sync(response.rooms);
client_->setNextBatchToken(nextBatchToken);
@ -445,12 +452,14 @@ ChatPage::initialSyncCompleted(const mtx::responses::Sync &response)
&Cache::setState,
QString::fromStdString(response.next_batch),
state_manager_);
QtConcurrent::run(cache_.data(), &Cache::setInvites, response.rooms.invite);
// Populate timelines with messages.
view_manager_->initialize(response.rooms);
// Initialize room list.
room_list_->setInitialRooms(settingsManager_, state_manager_);
room_list_->syncInvites(response.rooms.invite);
client_->setNextBatchToken(QString::fromStdString(response.next_batch));
client_->sync();
@ -552,6 +561,7 @@ ChatPage::loadStateFromCache()
// Initialize room list from the restored state and settings.
room_list_->setInitialRooms(settingsManager_, state_manager_);
room_list_->syncInvites(cache_->invites());
// Check periodically if the timelines have been loaded.
consensusTimer_->start(CONSENSUS_TIMEOUT);
@ -629,6 +639,7 @@ ChatPage::removeRoom(const QString &room_id)
settingsManager_.remove(room_id);
try {
cache_->removeRoom(room_id);
cache_->removeInvite(room_id);
} catch (const lmdb::error &e) {
qCritical() << "The cache couldn't be updated: " << e.what();
// TODO: Notify the user.
@ -638,6 +649,21 @@ ChatPage::removeRoom(const QString &room_id)
room_list_->removeRoom(room_id, room_id == current_room_);
}
void
ChatPage::removeInvite(const QString &room_id)
{
try {
cache_->removeInvite(room_id);
} catch (const lmdb::error &e) {
qCritical() << "The cache couldn't be updated: " << e.what();
// TODO: Notify the user.
cache_->unmount();
cache_->deleteData();
}
room_list_->removeRoom(room_id, room_id == current_room_);
}
void
ChatPage::updateTypingUsers(const QString &roomid, const std::vector<std::string> &user_ids)
{

View File

@ -15,9 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QDebug>
#include <QMouseEvent>
#include <QPainter>
#include <variant.hpp>
#include "Config.h"
#include "Menu.h"
#include "Ripple.h"
@ -26,6 +29,44 @@
#include "RoomSettings.h"
#include "Theme.h"
constexpr int Padding = 7;
constexpr int IconSize = 48;
constexpr int MaxHeight = IconSize + 2 * Padding;
constexpr int InviteBtnX = IconSize + 2 * Padding;
constexpr int InviteBtnY = IconSize / 2 + Padding;
void
RoomInfoListItem::init(QWidget *parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setMouseTracking(true);
setAttribute(Qt::WA_Hover);
setFixedHeight(MaxHeight);
QPainterPath path;
path.addRect(0, 0, parent->width(), height());
ripple_overlay_ = new RippleOverlay(this);
ripple_overlay_->setClipPath(path);
ripple_overlay_->setClipping(true);
}
RoomInfoListItem::RoomInfoListItem(QString room_id,
mtx::responses::InvitedRoom room,
QWidget *parent)
: QWidget(parent)
, roomType_{RoomType::Invited}
, invitedRoom_{std::move(room)}
, roomId_{std::move(room_id)}
{
init(parent);
roomAvatar_ = QString::fromStdString(invitedRoom_.avatar());
roomName_ = QString::fromStdString(invitedRoom_.name());
}
RoomInfoListItem::RoomInfoListItem(QSharedPointer<RoomSettings> settings,
RoomState state,
QString room_id,
@ -35,21 +76,9 @@ RoomInfoListItem::RoomInfoListItem(QSharedPointer<RoomSettings> settings,
, roomId_(room_id)
, roomSettings_{settings}
, isPressed_(false)
, maxHeight_(IconSize + 2 * Padding)
, unreadMsgCount_(0)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setMouseTracking(true);
setAttribute(Qt::WA_Hover);
setFixedHeight(maxHeight_);
QPainterPath path;
path.addRect(0, 0, parent->width(), height());
ripple_overlay_ = new RippleOverlay(this);
ripple_overlay_->setClipPath(path);
ripple_overlay_->setClipping(true);
init(parent);
menu_ = new Menu(this);
@ -99,82 +128,100 @@ RoomInfoListItem::paintEvent(QPaintEvent *event)
font.setPixelSize(conf::fontSize);
QFontMetrics metrics(font);
QPen titlePen(titleColor_);
QPen subtitlePen(subtitleColor_);
if (isPressed_) {
p.fillRect(rect(), highlightedBackgroundColor_);
titlePen.setColor(highlightedTitleColor_);
subtitlePen.setColor(highlightedSubtitleColor_);
} else if (underMouse()) {
p.fillRect(rect(), hoverBackgroundColor_);
} else {
p.fillRect(rect(), backgroundColor_);
}
// p.setPen(QColor("#333"));
QRect avatarRegion(Padding, Padding, IconSize, IconSize);
// Description line with the default font.
int bottom_y = maxHeight_ - Padding - Padding / 3 - metrics.ascent() / 2;
int bottom_y = MaxHeight - Padding - Padding / 3 - metrics.ascent() / 2;
if (width() > ui::sidebar::SmallSize) {
if (isPressed_) {
QPen pen(highlightedTitleColor_);
p.setPen(pen);
} else {
QPen pen(titleColor_);
p.setPen(pen);
}
font.setPixelSize(conf::roomlist::fonts::heading);
p.setFont(font);
p.setPen(titlePen);
// Name line.
QFontMetrics fontNameMetrics(font);
int top_y = 2 * Padding + fontNameMetrics.ascent() / 2;
auto name = metrics.elidedText(
state_.getName(), Qt::ElideRight, (width() - IconSize - 2 * Padding) * 0.8);
roomName(), Qt::ElideRight, (width() - IconSize - 2 * Padding) * 0.8);
p.drawText(QPoint(2 * Padding + IconSize, top_y), name);
if (isPressed_) {
QPen pen(highlightedSubtitleColor_);
p.setPen(pen);
} else {
QPen pen(subtitleColor_);
p.setPen(pen);
}
if (roomType_ == RoomType::Joined) {
font.setPixelSize(conf::fontSize);
p.setFont(font);
p.setPen(subtitlePen);
font.setPixelSize(conf::fontSize);
p.setFont(font);
auto msgStampWidth = QFontMetrics(font).width(lastMsgInfo_.timestamp) + 5;
auto msgStampWidth = QFontMetrics(font).width(lastMsgInfo_.timestamp) + 5;
// The limit is the space between the end of the avatar and the start of the
// timestamp.
int usernameLimit =
std::max(0, width() - 3 * Padding - msgStampWidth - IconSize - 20);
auto userName =
metrics.elidedText(lastMsgInfo_.username, Qt::ElideRight, usernameLimit);
// The limit is the space between the end of the avatar and the start of the
// timestamp.
int usernameLimit =
std::max(0, width() - 3 * Padding - msgStampWidth - IconSize - 20);
auto userName =
metrics.elidedText(lastMsgInfo_.username, Qt::ElideRight, usernameLimit);
font.setBold(true);
p.setFont(font);
p.drawText(QPoint(2 * Padding + IconSize, bottom_y), userName);
int nameWidth = QFontMetrics(font).width(userName);
font.setBold(false);
p.setFont(font);
// The limit is the space between the end of the username and the start of
// the timestamp.
int descriptionLimit =
std::max(0, width() - 3 * Padding - msgStampWidth - IconSize - nameWidth - 5);
auto description =
metrics.elidedText(lastMsgInfo_.body, Qt::ElideRight, descriptionLimit);
p.drawText(QPoint(2 * Padding + IconSize + nameWidth, bottom_y), description);
// We either show the bubble or the last message timestamp.
if (unreadMsgCount_ == 0) {
font.setBold(true);
p.drawText(QPoint(width() - Padding - msgStampWidth, bottom_y),
lastMsgInfo_.timestamp);
p.setFont(font);
p.drawText(QPoint(2 * Padding + IconSize, bottom_y), userName);
int nameWidth = QFontMetrics(font).width(userName);
font.setBold(false);
p.setFont(font);
// The limit is the space between the end of the username and the start of
// the timestamp.
int descriptionLimit = std::max(
0, width() - 3 * Padding - msgStampWidth - IconSize - nameWidth - 5);
auto description =
metrics.elidedText(lastMsgInfo_.body, Qt::ElideRight, descriptionLimit);
p.drawText(QPoint(2 * Padding + IconSize + nameWidth, bottom_y),
description);
// We either show the bubble or the last message timestamp.
if (unreadMsgCount_ == 0) {
font.setBold(true);
p.drawText(QPoint(width() - Padding - msgStampWidth, bottom_y),
lastMsgInfo_.timestamp);
}
} else {
int btnWidth = (width() - IconSize - 6 * Padding) / 2;
acceptBtnRegion_ = QRectF(InviteBtnX, InviteBtnY, btnWidth, 20);
declineBtnRegion_ =
QRectF(InviteBtnX + btnWidth + 2 * Padding, InviteBtnY, btnWidth, 20);
QPainterPath acceptPath;
acceptPath.addRoundedRect(acceptBtnRegion_, 10, 10);
p.setPen(Qt::NoPen);
p.fillPath(acceptPath, btnColor_);
p.drawPath(acceptPath);
QPainterPath declinePath;
declinePath.addRoundedRect(declineBtnRegion_, 10, 10);
p.setPen(Qt::NoPen);
p.fillPath(declinePath, btnColor_);
p.drawPath(declinePath);
p.setPen(QPen(btnTextColor_));
p.setFont(font);
p.drawText(acceptBtnRegion_, Qt::AlignCenter, tr("Accept"));
p.drawText(declineBtnRegion_, Qt::AlignCenter, tr("Decline"));
}
}
@ -196,8 +243,7 @@ RoomInfoListItem::paintEvent(QPaintEvent *event)
p.setFont(font);
p.setPen(QColor("#333"));
p.setBrush(Qt::NoBrush);
p.drawText(
avatarRegion.translated(0, -1), Qt::AlignCenter, QChar(state_.getName()[0]));
p.drawText(avatarRegion.translated(0, -1), Qt::AlignCenter, QChar(roomName()[0]));
} else {
p.save();
@ -289,6 +335,9 @@ RoomInfoListItem::contextMenuEvent(QContextMenuEvent *event)
{
Q_UNUSED(event);
if (roomType_ == RoomType::Invited)
return;
toggleNotifications_->setText(notificationText());
menu_->popup(event->globalPos());
}
@ -301,6 +350,18 @@ RoomInfoListItem::mousePressEvent(QMouseEvent *event)
return;
}
if (roomType_ == RoomType::Invited) {
const auto point = event->pos();
if (acceptBtnRegion_.contains(point))
emit acceptInvite(roomId_);
if (declineBtnRegion_.contains(point))
emit declineInvite(roomId_);
return;
}
emit clicked(roomId_);
setPressedState(true);

View File

@ -287,3 +287,32 @@ RoomList::paintEvent(QPaintEvent *)
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
void
RoomList::syncInvites(const std::map<std::string, mtx::responses::InvitedRoom> &rooms)
{
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
const auto room_id = QString::fromStdString(it->first);
if (!rooms_.contains(room_id))
addInvitedRoom(room_id, it->second);
}
}
void
RoomList::addInvitedRoom(const QString &room_id, const mtx::responses::InvitedRoom &room)
{
auto room_item = new RoomInfoListItem(room_id, room, scrollArea_);
connect(room_item, &RoomInfoListItem::acceptInvite, this, &RoomList::acceptInvite);
connect(room_item, &RoomInfoListItem::declineInvite, this, &RoomList::declineInvite);
rooms_.insert(room_id, QSharedPointer<RoomInfoListItem>(room_item));
auto avatarUrl = QString::fromStdString(room.avatar());
if (!avatarUrl.isEmpty())
client_->fetchRoomAvatar(room_id, avatarUrl);
int pos = contentsLayout_->count() - 1;
contentsLayout_->insertWidget(pos, room_item);
}