Some basic room list

This commit is contained in:
Nicolas Werner 2021-05-19 19:34:10 +02:00
parent 567fe81ad7
commit 10fd2752f9
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
9 changed files with 439 additions and 164 deletions

View file

@ -272,6 +272,7 @@ set(SRC_FILES
src/timeline/TimelineModel.cpp
src/timeline/DelegateChooser.cpp
src/timeline/Permissions.cpp
src/timeline/RoomlistModel.cpp
# UI components
src/ui/Avatar.cpp
@ -497,6 +498,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/timeline/TimelineModel.h
src/timeline/DelegateChooser.h
src/timeline/Permissions.h
src/timeline/RoomlistModel.h
# UI components
src/ui/Avatar.h

View file

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.9
import QtQuick.Controls 2.13
import im.nheko 1.0
Label {
id: root
property alias fullText: metrics.text
property alias elideWidth: metrics.elideWidth
color: Nheko.colors.text
text: metrics.elidedText
maximumLineCount: 1
elide: Text.ElideRight
textFormat: Text.PlainText
TextMetrics {
id: metrics
font.pointSize: root.font.pointSize
elide: Text.ElideRight
}
}

View file

@ -21,7 +21,7 @@ Popup {
modal: true
palette: Nheko.colors
parent: Overlay.overlay
width: implicitWidth >= (timelineView.width * 0.8) ? implicitWidth : (timelineView.width * 0.8)
width: implicitWidth >= (timelineRoot.width * 0.8) ? implicitWidth : (timelineRoot.width * 0.8)
height: implicitHeight + completerPopup.height + padding * 2
leftPadding: 10
rightPadding: 10

View file

@ -8,6 +8,132 @@ import QtQuick.Layouts 1.3
import im.nheko 1.0
Page {
ListView {
anchors.left: parent.left
anchors.right: parent.right
height: parent.height
model: Rooms
ScrollHelper {
flickable: parent
anchors.fill: parent
enabled: !Settings.mobileMode
}
delegate: Rectangle {
color: Nheko.colors.window
height: fontMetrics.lineSpacing * 2.5 + Nheko.paddingMedium * 2
width: ListView.view.width
RowLayout {
//id: userInfoGrid
spacing: Nheko.paddingMedium
anchors.fill: parent
anchors.margins: Nheko.paddingMedium
Avatar {
//userid: Nheko.currentUser.userid
id: avatar
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: fontMetrics.lineSpacing * 2.5
Layout.preferredHeight: fontMetrics.lineSpacing * 2.5
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
displayName: model.roomName
}
ColumnLayout {
id: textContent
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
Layout.minimumWidth: 100
width: parent.width - avatar.width
Layout.preferredWidth: parent.width - avatar.width
spacing: 0
RowLayout {
Layout.fillWidth: true
spacing: 0
ElidedLabel {
Layout.alignment: Qt.AlignBottom
color: Nheko.colors.text
elideWidth: textContent.width - timestamp.width - Nheko.paddingMedium
fullText: model.roomName + ": " + model.notificationCount
}
Item {
Layout.fillWidth: true
}
Label {
id: timestamp
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
font.pixelSize: fontMetrics.font.pixelSize * 0.9
color: Nheko.colors.buttonText
text: "14:32"
}
}
RowLayout {
Layout.fillWidth: true
spacing: 0
ElidedLabel {
color: Nheko.colors.buttonText
font.weight: Font.Thin
font.pixelSize: fontMetrics.font.pixelSize * 0.9
elideWidth: textContent.width - notificationBubble.width
fullText: model.lastMessage
}
Item {
Layout.fillWidth: true
}
Rectangle {
id: notificationBubble
Layout.alignment: Qt.AlignRight
height: fontMetrics.font.pixelSize * 1.3
width: height
radius: height / 2
color: Nheko.colors.highlight
Label {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
fontSizeMode: Text.Fit
color: Nheko.colors.highlightedText
text: model.notificationCount
}
}
}
}
}
Rectangle {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
height: parent.height - Nheko.paddingSmall * 2
width: 3
color: Nheko.colors.highlight
visible: model.hasUnreadMessages
}
}
}
background: Rectangle {
color: Nheko.theme.sidebarBackground
@ -34,8 +160,8 @@ Page {
id: avatar
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: Nheko.avatarSize
Layout.preferredHeight: Nheko.avatarSize
Layout.preferredWidth: fontMetrics.lineSpacing * 2
Layout.preferredHeight: fontMetrics.lineSpacing * 2
url: Nheko.currentUser.avatarUrl.replace("mxc://", "image://MxcImage/")
displayName: Nheko.currentUser.displayName
userid: Nheko.currentUser.userid
@ -46,50 +172,25 @@ Page {
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
Layout.minimumWidth: 100
width: parent.width - avatar.width - logoutButton.width
Layout.preferredWidth: parent.width - avatar.width - logoutButton.width
width: parent.width - avatar.width - logoutButton.width - Nheko.paddingMedium * 2
Layout.preferredWidth: parent.width - avatar.width - logoutButton.width - Nheko.paddingMedium * 2
spacing: 0
Label {
ElidedLabel {
Layout.alignment: Qt.AlignBottom
color: Nheko.colors.text
font.pointSize: fontMetrics.font.pointSize * 1.1
font.weight: Font.DemiBold
text: userNameText.elidedText
maximumLineCount: 1
elide: Text.ElideRight
textFormat: Text.PlainText
TextMetrics {
id: userNameText
font.pointSize: fontMetrics.font.pointSize * 1.1
elide: Text.ElideRight
elideWidth: col.width
text: Nheko.currentUser.displayName
}
fullText: Nheko.currentUser.displayName
elideWidth: col.width
}
Label {
ElidedLabel {
Layout.alignment: Qt.AlignTop
color: Nheko.colors.buttonText
font.weight: Font.Thin
text: userIdText.elidedText
maximumLineCount: 1
textFormat: Text.PlainText
font.pointSize: fontMetrics.font.pointSize * 0.9
TextMetrics {
id: userIdText
font.pointSize: fontMetrics.font.pointSize * 0.9
elide: Text.ElideRight
elideWidth: col.width
text: Nheko.currentUser.userid
}
elideWidth: col.width
fullText: Nheko.currentUser.userid
}
}

View file

@ -131,6 +131,7 @@
<file>qml/Completer.qml</file>
<file>qml/EncryptionIndicator.qml</file>
<file>qml/ImageButton.qml</file>
<file>qml/ElidedLabel.qml</file>
<file>qml/MatrixText.qml</file>
<file>qml/MatrixTextField.qml</file>
<file>qml/ToggleButton.qml</file>

View file

@ -0,0 +1,146 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "RoomlistModel.h"
#include "ChatPage.h"
#include "MatrixClient.h"
#include "MxcImageProvider.h"
#include "TimelineModel.h"
#include "TimelineViewManager.h"
#include "UserSettingsPage.h"
RoomlistModel::RoomlistModel(TimelineViewManager *parent)
: manager(parent)
{
connect(ChatPage::instance(), &ChatPage::decryptSidebarChanged, this, [this]() {
auto decrypt = ChatPage::instance()->userSettings()->decryptSidebar();
QHash<QString, QSharedPointer<TimelineModel>>::iterator i;
for (i = models.begin(); i != models.end(); ++i) {
auto ptr = i.value();
if (!ptr.isNull()) {
ptr->setDecryptDescription(decrypt);
ptr->updateLastMessage();
}
}
});
}
QHash<int, QByteArray>
RoomlistModel::roleNames() const
{
return {
{AvatarUrl, "avatarUrl"},
{RoomName, "roomName"},
{LastMessage, "lastMessage"},
{HasUnreadMessages, "hasUnreadMessages"},
{NotificationCount, "notificationCount"},
};
}
QVariant
RoomlistModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= 0 && static_cast<size_t>(index.row()) < roomids.size()) {
auto room = models.value(roomids.at(index.row()));
switch (role) {
case Roles::AvatarUrl:
return room->roomAvatarUrl();
case Roles::RoomName:
return room->roomName();
case Roles::LastMessage:
return QString("Nico: Hahaha, this is funny!");
case Roles::HasUnreadMessages:
return true;
case Roles::NotificationCount:
return 5;
default:
return {};
}
} else {
return {};
}
}
void
RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification)
{
if (!models.contains(room_id)) {
QSharedPointer<TimelineModel> newRoom(new TimelineModel(manager, room_id));
newRoom->setDecryptDescription(
ChatPage::instance()->userSettings()->decryptSidebar());
connect(newRoom.data(),
&TimelineModel::newEncryptedImage,
manager->imageProvider(),
&MxcImageProvider::addEncryptionInfo);
connect(newRoom.data(),
&TimelineModel::forwardToRoom,
manager,
&TimelineViewManager::forwardMessageToRoom);
if (!suppressInsertNotification)
beginInsertRows(QModelIndex(), (int)roomids.size(), (int)roomids.size());
models.insert(room_id, std::move(newRoom));
roomids.push_back(room_id);
if (!suppressInsertNotification)
endInsertRows();
}
}
void
RoomlistModel::sync(const mtx::responses::Rooms &rooms)
{
for (const auto &[room_id, room] : rooms.join) {
// addRoom will only add the room, if it doesn't exist
addRoom(QString::fromStdString(room_id));
const auto &room_model = models.value(QString::fromStdString(room_id));
room_model->syncState(room.state);
room_model->addEvents(room.timeline);
connect(room_model.data(),
&TimelineModel::newCallEvent,
manager->callManager(),
&CallManager::syncEvent,
Qt::UniqueConnection);
if (ChatPage::instance()->userSettings()->typingNotifications()) {
for (const auto &ev : room.ephemeral.events) {
if (auto t = std::get_if<
mtx::events::EphemeralEvent<mtx::events::ephemeral::Typing>>(
&ev)) {
std::vector<QString> typing;
typing.reserve(t->content.user_ids.size());
for (const auto &user : t->content.user_ids) {
if (user != http::client()->user_id().to_string())
typing.push_back(
QString::fromStdString(user));
}
room_model->updateTypingUsers(typing);
}
}
}
}
}
void
RoomlistModel::initializeRooms(const std::vector<QString> &roomIds_)
{
beginResetModel();
models.clear();
roomids.clear();
roomids = roomIds_;
for (const auto &id : roomIds_)
addRoom(id, true);
endResetModel();
}
void
RoomlistModel::clear()
{
beginResetModel();
models.clear();
roomids.clear();
endResetModel();
}

View file

@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QAbstractListModel>
#include <QHash>
#include <QSharedPointer>
#include <QString>
#include <mtx/responses/sync.hpp>
class TimelineModel;
class TimelineViewManager;
class RoomlistModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles
{
AvatarUrl = Qt::UserRole,
RoomName,
LastMessage,
HasUnreadMessages,
NotificationCount,
};
RoomlistModel(TimelineViewManager *parent = nullptr);
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
(void)parent;
return (int)roomids.size();
}
QVariant data(const QModelIndex &index, int role) const override;
QSharedPointer<TimelineModel> getRoomById(QString id) const
{
if (models.contains(id))
return models.value(id);
else
return {};
}
public slots:
void initializeRooms(const std::vector<QString> &roomids);
void sync(const mtx::responses::Rooms &rooms);
void clear();
private:
void addRoom(const QString &room_id, bool suppressInsertNotification = false);
TimelineViewManager *manager = nullptr;
std::vector<QString> roomids;
QHash<QString, QSharedPointer<TimelineModel>> models;
};

View file

@ -86,21 +86,6 @@ removeReplyFallback(mtx::events::Event<T> &e)
}
}
void
TimelineViewManager::updateEncryptedDescriptions()
{
auto decrypt = ChatPage::instance()->userSettings()->decryptSidebar();
QHash<QString, QSharedPointer<TimelineModel>>::iterator i;
for (i = models.begin(); i != models.end(); ++i) {
auto ptr = i.value();
if (!ptr.isNull()) {
ptr->setDecryptDescription(decrypt);
ptr->updateLastMessage();
}
}
}
void
TimelineViewManager::updateColorPalette()
{
@ -148,6 +133,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
, colorImgProvider(new ColorImageProvider())
, blurhashProvider(new BlurhashProvider())
, callManager_(callManager)
, rooms(new RoomlistModel(this))
{
qRegisterMetaType<mtx::events::msg::KeyVerificationAccept>();
qRegisterMetaType<mtx::events::msg::KeyVerificationCancel>();
@ -205,6 +191,12 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
return ptr;
});
qmlRegisterSingletonType<RoomlistModel>(
"im.nheko", 1, 0, "Rooms", [](QQmlEngine *, QJSEngine *) -> QObject * {
auto ptr = self->rooms;
QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
return ptr;
});
qmlRegisterSingletonType<UserSettings>(
"im.nheko", 1, 0, "Settings", [](QQmlEngine *, QJSEngine *) -> QObject * {
auto ptr = ChatPage::instance()->userSettings().data();
@ -260,10 +252,6 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
view->setSource(QUrl("qrc:///qml/Root.qml"));
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
connect(parent,
&ChatPage::decryptSidebarChanged,
this,
&TimelineViewManager::updateEncryptedDescriptions);
connect(
dynamic_cast<ChatPage *>(parent),
&ChatPage::receivedRoomDeviceVerificationRequest,
@ -334,64 +322,13 @@ TimelineViewManager::setVideoCallItem()
}
void
TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
TimelineViewManager::sync(const mtx::responses::Rooms &rooms_)
{
for (const auto &[room_id, room] : rooms.join) {
// addRoom will only add the room, if it doesn't exist
addRoom(QString::fromStdString(room_id));
const auto &room_model = models.value(QString::fromStdString(room_id));
if (!isInitialSync_)
connect(room_model.data(),
&TimelineModel::newCallEvent,
callManager_,
&CallManager::syncEvent);
room_model->syncState(room.state);
room_model->addEvents(room.timeline);
if (!isInitialSync_)
disconnect(room_model.data(),
&TimelineModel::newCallEvent,
callManager_,
&CallManager::syncEvent);
this->rooms->sync(rooms_);
if (ChatPage::instance()->userSettings()->typingNotifications()) {
for (const auto &ev : room.ephemeral.events) {
if (auto t = std::get_if<
mtx::events::EphemeralEvent<mtx::events::ephemeral::Typing>>(
&ev)) {
std::vector<QString> typing;
typing.reserve(t->content.user_ids.size());
for (const auto &user : t->content.user_ids) {
if (user != http::client()->user_id().to_string())
typing.push_back(
QString::fromStdString(user));
}
room_model->updateTypingUsers(typing);
}
}
}
}
this->isInitialSync_ = false;
emit initialSyncChanged(false);
}
void
TimelineViewManager::addRoom(const QString &room_id)
{
if (!models.contains(room_id)) {
QSharedPointer<TimelineModel> newRoom(new TimelineModel(this, room_id));
newRoom->setDecryptDescription(
ChatPage::instance()->userSettings()->decryptSidebar());
connect(newRoom.data(),
&TimelineModel::newEncryptedImage,
imgProvider,
&MxcImageProvider::addEncryptionInfo);
connect(newRoom.data(),
&TimelineModel::forwardToRoom,
this,
&TimelineViewManager::forwardMessageToRoom);
models.insert(room_id, std::move(newRoom));
if (isInitialSync_) {
this->isInitialSync_ = false;
emit initialSyncChanged(false);
}
}
@ -400,9 +337,8 @@ TimelineViewManager::setHistoryView(const QString &room_id)
{
nhlog::ui()->info("Trying to activate room {}", room_id.toStdString());
auto room = models.find(room_id);
if (room != models.end()) {
timeline_ = room.value().data();
if (auto room = rooms->getRoomById(room_id)) {
timeline_ = room.get();
emit activeTimelineChanged(timeline_);
container->setFocus();
nhlog::ui()->info("Activated room {}", room_id.toStdString());
@ -418,10 +354,9 @@ TimelineViewManager::highlightRoom(const QString &room_id)
void
TimelineViewManager::showEvent(const QString &room_id, const QString &event_id)
{
auto room = models.find(room_id);
if (room != models.end()) {
if (timeline_ != room.value().data()) {
timeline_ = room.value().data();
if (auto room = rooms->getRoomById(room_id)) {
if (timeline_ != room) {
timeline_ = room.get();
emit activeTimelineChanged(timeline_);
container->setFocus();
nhlog::ui()->info("Activated room {}", room_id.toStdString());
@ -505,17 +440,21 @@ TimelineViewManager::verifyUser(QString userid)
if (std::find(room_members.begin(),
room_members.end(),
(userid).toStdString()) != room_members.end()) {
auto model = models.value(QString::fromStdString(room_id));
auto flow = DeviceVerificationFlow::InitiateUserVerification(
this, model.data(), userid);
connect(model.data(),
&TimelineModel::updateFlowEventId,
this,
[this, flow](std::string eventId) {
dvList[QString::fromStdString(eventId)] = flow;
});
emit newDeviceVerificationRequest(flow.data());
return;
if (auto model =
rooms->getRoomById(QString::fromStdString(room_id))) {
auto flow =
DeviceVerificationFlow::InitiateUserVerification(
this, model.data(), userid);
connect(model.data(),
&TimelineModel::updateFlowEventId,
this,
[this, flow](std::string eventId) {
dvList[QString::fromStdString(eventId)] =
flow;
});
emit newDeviceVerificationRequest(flow.data());
return;
}
}
}
}
@ -548,26 +487,23 @@ void
TimelineViewManager::updateReadReceipts(const QString &room_id,
const std::vector<QString> &event_ids)
{
auto room = models.find(room_id);
if (room != models.end()) {
room.value()->markEventsAsRead(event_ids);
if (auto room = rooms->getRoomById(room_id)) {
room->markEventsAsRead(event_ids);
}
}
void
TimelineViewManager::receivedSessionKey(const std::string &room_id, const std::string &session_id)
{
auto room = models.find(QString::fromStdString(room_id));
if (room != models.end()) {
room.value()->receivedSessionKey(session_id);
if (auto room = rooms->getRoomById(QString::fromStdString(room_id))) {
room->receivedSessionKey(session_id);
}
}
void
TimelineViewManager::initWithMessages(const std::vector<QString> &roomIds)
{
for (const auto &roomId : roomIds)
addRoom(roomId);
rooms->initializeRooms(roomIds);
}
void
@ -575,10 +511,9 @@ TimelineViewManager::queueReply(const QString &roomid,
const QString &repliedToEvent,
const QString &replyBody)
{
auto room = models.find(roomid);
if (room != models.end()) {
room.value()->setReply(repliedToEvent);
room.value()->input()->message(replyBody);
if (auto room = rooms->getRoomById(roomid)) {
room->setReply(repliedToEvent);
room->input()->message(replyBody);
}
}
@ -620,29 +555,32 @@ void
TimelineViewManager::queueCallMessage(const QString &roomid,
const mtx::events::msg::CallInvite &callInvite)
{
models.value(roomid)->sendMessageEvent(callInvite, mtx::events::EventType::CallInvite);
if (auto room = rooms->getRoomById(roomid))
room->sendMessageEvent(callInvite, mtx::events::EventType::CallInvite);
}
void
TimelineViewManager::queueCallMessage(const QString &roomid,
const mtx::events::msg::CallCandidates &callCandidates)
{
models.value(roomid)->sendMessageEvent(callCandidates,
mtx::events::EventType::CallCandidates);
if (auto room = rooms->getRoomById(roomid))
room->sendMessageEvent(callCandidates, mtx::events::EventType::CallCandidates);
}
void
TimelineViewManager::queueCallMessage(const QString &roomid,
const mtx::events::msg::CallAnswer &callAnswer)
{
models.value(roomid)->sendMessageEvent(callAnswer, mtx::events::EventType::CallAnswer);
if (auto room = rooms->getRoomById(roomid))
room->sendMessageEvent(callAnswer, mtx::events::EventType::CallAnswer);
}
void
TimelineViewManager::queueCallMessage(const QString &roomid,
const mtx::events::msg::CallHangUp &callHangUp)
{
models.value(roomid)->sendMessageEvent(callHangUp, mtx::events::EventType::CallHangUp);
if (auto room = rooms->getRoomById(roomid))
room->sendMessageEvent(callHangUp, mtx::events::EventType::CallHangUp);
}
void
@ -693,7 +631,7 @@ void
TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEvents *e,
QString roomId)
{
auto room = models.find(roomId);
auto room = rooms->getRoomById(roomId);
auto content = mtx::accessors::url(*e);
std::optional<mtx::crypto::EncryptedFile> encryptionInfo = mtx::accessors::file(*e);
@ -736,12 +674,15 @@ TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEven
ev.content.url = url;
}
auto room = models.find(roomId);
removeReplyFallback(ev);
ev.content.relations.relations.clear();
room.value()->sendMessageEvent(
ev.content,
mtx::events::EventType::RoomMessage);
if (auto room = rooms->getRoomById(roomId)) {
removeReplyFallback(ev);
ev.content.relations.relations
.clear();
room->sendMessageEvent(
ev.content,
mtx::events::EventType::
RoomMessage);
}
}
},
*e);
@ -759,8 +700,7 @@ TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEven
mtx::events::EventType::RoomMessage) {
e.content.relations.relations.clear();
removeReplyFallback(e);
room.value()->sendMessageEvent(e.content,
mtx::events::EventType::RoomMessage);
room->sendMessageEvent(e.content, mtx::events::EventType::RoomMessage);
}
},
*e);

View file

@ -22,6 +22,7 @@
#include "WebRTCSession.h"
#include "emoji/EmojiModel.h"
#include "emoji/Provider.h"
#include "timeline/RoomlistModel.h"
class MxcImageProvider;
class BlurhashProvider;
@ -48,13 +49,15 @@ public:
QWidget *getWidget() const { return container; }
void sync(const mtx::responses::Rooms &rooms);
void addRoom(const QString &room_id);
MxcImageProvider *imageProvider() { return imgProvider; }
CallManager *callManager() { return callManager_; }
void clearAll()
{
timeline_ = nullptr;
emit activeTimelineChanged(nullptr);
models.clear();
rooms->clear();
}
Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; }
@ -109,11 +112,7 @@ public slots:
void focusTimeline();
TimelineModel *getHistoryView(const QString &room_id)
{
auto room = models.find(room_id);
if (room != models.end())
return room.value().data();
else
return nullptr;
return rooms->getRoomById(room_id).get();
}
void updateColorPalette();
@ -126,7 +125,6 @@ public slots:
void queueCallMessage(const QString &roomid, const mtx::events::msg::CallAnswer &);
void queueCallMessage(const QString &roomid, const mtx::events::msg::CallHangUp &);
void updateEncryptedDescriptions();
void setVideoCallItem();
void enableBackButton()
@ -163,7 +161,6 @@ private:
ColorImageProvider *colorImgProvider;
BlurhashProvider *blurhashProvider;
QHash<QString, QSharedPointer<TimelineModel>> models;
TimelineModel *timeline_ = nullptr;
CallManager *callManager_ = nullptr;
@ -171,6 +168,8 @@ private:
bool isNarrowView_ = false;
bool isWindowFocused_ = false;
RoomlistModel *rooms = nullptr;
QHash<QString, QColor> userColors;
QHash<QString, QSharedPointer<DeviceVerificationFlow>> dvList;