summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorDeepBlueV7.X <nicolas.werner@hotmail.de>2021-07-30 22:08:51 +0000
committerGitHub <noreply@github.com>2021-07-30 22:08:51 +0000
commit5b5a89b52217b672bf31c26ccd6e2a3b6f15a942 (patch)
tree0f076e6b6ee36e6fd8258bbcf677a779b6b794c3 /src
parentFix crash when we don't have keys for other device when receiving an olm mess... (diff)
parentUse correct date format (diff)
downloadnheko-5b5a89b52217b672bf31c26ccd6e2a3b6f15a942.tar.xz
Merge pull request #655 from LorenDB/qml-all-the-things
QML all the things, part 2: Read receipts dialog
Diffstat (limited to 'src')
-rw-r--r--src/ChatPage.cpp1
-rw-r--r--src/MainWindow.cpp22
-rw-r--r--src/MainWindow.h1
-rw-r--r--src/MemberList.cpp11
-rw-r--r--src/MemberList.h3
-rw-r--r--src/ReadReceiptsModel.cpp131
-rw-r--r--src/ReadReceiptsModel.h73
-rw-r--r--src/dialogs/ReadReceipts.cpp179
-rw-r--r--src/dialogs/ReadReceipts.h61
-rw-r--r--src/timeline/TimelineModel.cpp5
-rw-r--r--src/timeline/TimelineModel.h4
-rw-r--r--src/timeline/TimelineViewManager.cpp7
-rw-r--r--src/ui/Avatar.cpp168
-rw-r--r--src/ui/Avatar.h48
14 files changed, 219 insertions, 495 deletions
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index a76756ae..42e3bc7b 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -31,7 +31,6 @@
 
 #include "notifications/Manager.h"
 
-#include "dialogs/ReadReceipts.h"
 #include "timeline/TimelineViewManager.h"
 
 #include "blurhash.hpp"
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index c0486d01..8bc90f29 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -36,7 +36,6 @@
 #include "dialogs/JoinRoom.h"
 #include "dialogs/LeaveRoom.h"
 #include "dialogs/Logout.h"
-#include "dialogs/ReadReceipts.h"
 
 MainWindow *MainWindow::instance_ = nullptr;
 
@@ -398,27 +397,6 @@ MainWindow::openLogoutDialog()
         showDialog(dialog);
 }
 
-void
-MainWindow::openReadReceiptsDialog(const QString &event_id)
-{
-        auto dialog = new dialogs::ReadReceipts(this);
-
-        const auto room_id = chat_page_->currentRoom();
-
-        try {
-                dialog->addUsers(cache::readReceipts(event_id, room_id));
-        } catch (const lmdb::error &) {
-                nhlog::db()->warn("failed to retrieve read receipts for {} {}",
-                                  event_id.toStdString(),
-                                  chat_page_->currentRoom().toStdString());
-                dialog->deleteLater();
-
-                return;
-        }
-
-        showDialog(dialog);
-}
-
 bool
 MainWindow::hasActiveDialogs() const
 {
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 6d62545c..d423af9f 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -65,7 +65,6 @@ public:
           std::function<void(const mtx::requests::CreateRoom &request)> callback);
         void openJoinRoomDialog(std::function<void(const QString &room_id)> callback);
         void openLogoutDialog();
-        void openReadReceiptsDialog(const QString &event_id);
 
         void hideOverlay();
         void showSolidOverlayModal(QWidget *content,
diff --git a/src/MemberList.cpp b/src/MemberList.cpp
index 0ef3b696..196647fe 100644
--- a/src/MemberList.cpp
+++ b/src/MemberList.cpp
@@ -2,16 +2,6 @@
 //
 // SPDX-License-Identifier: GPL-3.0-or-later
 
-#include <QAbstractSlider>
-#include <QLabel>
-#include <QListWidgetItem>
-#include <QPainter>
-#include <QPushButton>
-#include <QScrollBar>
-#include <QShortcut>
-#include <QStyleOption>
-#include <QVBoxLayout>
-
 #include "MemberList.h"
 
 #include "Cache.h"
@@ -20,7 +10,6 @@
 #include "Logging.h"
 #include "Utils.h"
 #include "timeline/TimelineViewManager.h"
-#include "ui/Avatar.h"
 
 MemberList::MemberList(const QString &room_id, QObject *parent)
   : QAbstractListModel{parent}
diff --git a/src/MemberList.h b/src/MemberList.h
index 9932f6a4..e6522694 100644
--- a/src/MemberList.h
+++ b/src/MemberList.h
@@ -4,9 +4,10 @@
 
 #pragma once
 
-#include "CacheStructs.h"
 #include <QAbstractListModel>
 
+#include "CacheStructs.h"
+
 class MemberList : public QAbstractListModel
 {
         Q_OBJECT
diff --git a/src/ReadReceiptsModel.cpp b/src/ReadReceiptsModel.cpp
new file mode 100644
index 00000000..25262c59
--- /dev/null
+++ b/src/ReadReceiptsModel.cpp
@@ -0,0 +1,131 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "ReadReceiptsModel.h"
+
+#include <QLocale>
+
+#include "Cache.h"
+#include "Cache_p.h"
+#include "Logging.h"
+#include "Utils.h"
+
+ReadReceiptsModel::ReadReceiptsModel(QString event_id, QString room_id, QObject *parent)
+  : QAbstractListModel{parent}
+  , event_id_{event_id}
+  , room_id_{room_id}
+{
+        try {
+                addUsers(cache::readReceipts(event_id_, room_id_));
+        } catch (const lmdb::error &) {
+                nhlog::db()->warn("failed to retrieve read receipts for {} {}",
+                                  event_id_.toStdString(),
+                                  room_id_.toStdString());
+
+                return;
+        }
+
+        connect(cache::client(), &Cache::newReadReceipts, this, &ReadReceiptsModel::update);
+}
+
+void
+ReadReceiptsModel::update()
+{
+        try {
+                addUsers(cache::readReceipts(event_id_, room_id_));
+        } catch (const lmdb::error &) {
+                nhlog::db()->warn("failed to retrieve read receipts for {} {}",
+                                  event_id_.toStdString(),
+                                  room_id_.toStdString());
+
+                return;
+        }
+}
+
+QHash<int, QByteArray>
+ReadReceiptsModel::roleNames() const
+{
+        // Note: RawTimestamp is purposely not included here
+        return {
+          {Mxid, "mxid"},
+          {DisplayName, "displayName"},
+          {AvatarUrl, "avatarUrl"},
+          {Timestamp, "timestamp"},
+        };
+}
+
+QVariant
+ReadReceiptsModel::data(const QModelIndex &index, int role) const
+{
+        if (!index.isValid() || index.row() >= (int)readReceipts_.size() || index.row() < 0)
+                return {};
+
+        switch (role) {
+        case Mxid:
+                return readReceipts_[index.row()].first;
+        case DisplayName:
+                return cache::displayName(room_id_, readReceipts_[index.row()].first);
+        case AvatarUrl:
+                return cache::avatarUrl(room_id_, readReceipts_[index.row()].first);
+        case Timestamp:
+                return dateFormat(readReceipts_[index.row()].second);
+        case RawTimestamp:
+                return readReceipts_[index.row()].second;
+        default:
+                return {};
+        }
+}
+
+void
+ReadReceiptsModel::addUsers(
+  const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users)
+{
+        auto newReceipts = users.size() - readReceipts_.size();
+
+        if (newReceipts > 0) {
+                beginInsertRows(
+                  QModelIndex{}, readReceipts_.size(), readReceipts_.size() + newReceipts - 1);
+
+                for (const auto &user : users) {
+                        QPair<QString, QDateTime> item = {
+                          QString::fromStdString(user.second),
+                          QDateTime::fromMSecsSinceEpoch(user.first)};
+                        if (!readReceipts_.contains(item))
+                                readReceipts_.push_back(item);
+                }
+
+                endInsertRows();
+        }
+}
+
+QString
+ReadReceiptsModel::dateFormat(const QDateTime &then) const
+{
+        auto now  = QDateTime::currentDateTime();
+        auto days = then.daysTo(now);
+
+        if (days == 0)
+                return QLocale::system().toString(then.time(), QLocale::ShortFormat);
+        else if (days < 2)
+                return tr("Yesterday, %1")
+                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
+        else if (days < 7)
+                //: %1 is the name of the current day, %2 is the time the read receipt was read. The
+                //: result may look like this: Monday, 7:15
+                return QString("%1, %2")
+                  .arg(then.toString("dddd"))
+                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
+
+        return QLocale::system().toString(then.time(), QLocale::ShortFormat);
+}
+
+ReadReceiptsProxy::ReadReceiptsProxy(QString event_id, QString room_id, QObject *parent)
+  : QSortFilterProxyModel{parent}
+  , model_{event_id, room_id, this}
+{
+        setSourceModel(&model_);
+        setSortRole(ReadReceiptsModel::RawTimestamp);
+        sort(0, Qt::DescendingOrder);
+        setDynamicSortFilter(true);
+}
diff --git a/src/ReadReceiptsModel.h b/src/ReadReceiptsModel.h
new file mode 100644
index 00000000..3b45716c
--- /dev/null
+++ b/src/ReadReceiptsModel.h
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef READRECEIPTSMODEL_H
+#define READRECEIPTSMODEL_H
+
+#include <QAbstractListModel>
+#include <QDateTime>
+#include <QObject>
+#include <QSortFilterProxyModel>
+#include <QString>
+
+class ReadReceiptsModel : public QAbstractListModel
+{
+        Q_OBJECT
+
+public:
+        enum Roles
+        {
+                Mxid,
+                DisplayName,
+                AvatarUrl,
+                Timestamp,
+                RawTimestamp,
+        };
+
+        explicit ReadReceiptsModel(QString event_id, QString room_id, QObject *parent = nullptr);
+
+        QString eventId() const { return event_id_; }
+        QString roomId() const { return room_id_; }
+
+        QHash<int, QByteArray> roleNames() const override;
+        int rowCount(const QModelIndex &parent) const override
+        {
+                Q_UNUSED(parent)
+                return readReceipts_.size();
+        }
+        QVariant data(const QModelIndex &index, int role) const override;
+
+public slots:
+        void addUsers(const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users);
+        void update();
+
+private:
+        QString dateFormat(const QDateTime &then) const;
+
+        QString event_id_;
+        QString room_id_;
+        QVector<QPair<QString, QDateTime>> readReceipts_;
+};
+
+class ReadReceiptsProxy : public QSortFilterProxyModel
+{
+        Q_OBJECT
+
+        Q_PROPERTY(QString eventId READ eventId CONSTANT)
+        Q_PROPERTY(QString roomId READ roomId CONSTANT)
+
+public:
+        explicit ReadReceiptsProxy(QString event_id, QString room_id, QObject *parent = nullptr);
+
+        QString eventId() const { return event_id_; }
+        QString roomId() const { return room_id_; }
+
+private:
+        QString event_id_;
+        QString room_id_;
+
+        ReadReceiptsModel model_;
+};
+
+#endif // READRECEIPTSMODEL_H
diff --git a/src/dialogs/ReadReceipts.cpp b/src/dialogs/ReadReceipts.cpp
deleted file mode 100644
index fa7132fd..00000000
--- a/src/dialogs/ReadReceipts.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QDebug>
-#include <QIcon>
-#include <QLabel>
-#include <QListWidgetItem>
-#include <QPainter>
-#include <QPushButton>
-#include <QShortcut>
-#include <QStyleOption>
-#include <QTimer>
-#include <QVBoxLayout>
-
-#include "dialogs/ReadReceipts.h"
-
-#include "AvatarProvider.h"
-#include "Cache.h"
-#include "ChatPage.h"
-#include "Config.h"
-#include "Utils.h"
-#include "ui/Avatar.h"
-
-using namespace dialogs;
-
-ReceiptItem::ReceiptItem(QWidget *parent,
-                         const QString &user_id,
-                         uint64_t timestamp,
-                         const QString &room_id)
-  : QWidget(parent)
-{
-        topLayout_ = new QHBoxLayout(this);
-        topLayout_->setMargin(0);
-
-        textLayout_ = new QVBoxLayout;
-        textLayout_->setMargin(0);
-        textLayout_->setSpacing(conf::modals::TEXT_SPACING);
-
-        QFont nameFont;
-        nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1);
-
-        auto displayName = cache::displayName(room_id, user_id);
-
-        avatar_ = new Avatar(this, 44);
-        avatar_->setLetter(utils::firstChar(displayName));
-
-        // If it's a matrix id we use the second letter.
-        if (displayName.size() > 1 && displayName.at(0) == '@')
-                avatar_->setLetter(QChar(displayName.at(1)));
-
-        userName_ = new QLabel(displayName, this);
-        userName_->setFont(nameFont);
-
-        timestamp_ = new QLabel(dateFormat(QDateTime::fromMSecsSinceEpoch(timestamp)), this);
-
-        textLayout_->addWidget(userName_);
-        textLayout_->addWidget(timestamp_);
-
-        topLayout_->addWidget(avatar_);
-        topLayout_->addLayout(textLayout_, 1);
-
-        avatar_->setImage(ChatPage::instance()->currentRoom(), user_id);
-}
-
-void
-ReceiptItem::paintEvent(QPaintEvent *)
-{
-        QStyleOption opt;
-        opt.init(this);
-        QPainter p(this);
-        style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-}
-
-QString
-ReceiptItem::dateFormat(const QDateTime &then) const
-{
-        auto now  = QDateTime::currentDateTime();
-        auto days = then.daysTo(now);
-
-        if (days == 0)
-                return tr("Today %1")
-                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
-        else if (days < 2)
-                return tr("Yesterday %1")
-                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
-        else if (days < 7)
-                return QString("%1 %2")
-                  .arg(then.toString("dddd"))
-                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
-
-        return QLocale::system().toString(then.time(), QLocale::ShortFormat);
-}
-
-ReadReceipts::ReadReceipts(QWidget *parent)
-  : QFrame(parent)
-{
-        setAutoFillBackground(true);
-        setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
-        setWindowModality(Qt::WindowModal);
-        setAttribute(Qt::WA_DeleteOnClose, true);
-
-        auto layout = new QVBoxLayout(this);
-        layout->setSpacing(conf::modals::WIDGET_SPACING);
-        layout->setMargin(conf::modals::WIDGET_MARGIN);
-
-        userList_ = new QListWidget;
-        userList_->setFrameStyle(QFrame::NoFrame);
-        userList_->setSelectionMode(QAbstractItemView::NoSelection);
-        userList_->setSpacing(conf::modals::TEXT_SPACING);
-
-        QFont largeFont;
-        largeFont.setPointSizeF(largeFont.pointSizeF() * 1.5);
-
-        setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
-        setMinimumHeight(userList_->sizeHint().height() * 2);
-        setMinimumWidth(std::max(userList_->sizeHint().width() + 4 * conf::modals::WIDGET_MARGIN,
-                                 QFontMetrics(largeFont).averageCharWidth() * 30 -
-                                   2 * conf::modals::WIDGET_MARGIN));
-
-        QFont font;
-        font.setPointSizeF(font.pointSizeF() * conf::modals::LABEL_MEDIUM_SIZE_RATIO);
-
-        topLabel_ = new QLabel(tr("Read receipts"), this);
-        topLabel_->setAlignment(Qt::AlignCenter);
-        topLabel_->setFont(font);
-
-        auto okBtn = new QPushButton(tr("Close"), this);
-
-        auto buttonLayout = new QHBoxLayout();
-        buttonLayout->setSpacing(15);
-        buttonLayout->addStretch(1);
-        buttonLayout->addWidget(okBtn);
-
-        layout->addWidget(topLabel_);
-        layout->addWidget(userList_);
-        layout->addLayout(buttonLayout);
-
-        auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
-        connect(closeShortcut, &QShortcut::activated, this, &ReadReceipts::close);
-        connect(okBtn, &QPushButton::clicked, this, &ReadReceipts::close);
-}
-
-void
-ReadReceipts::addUsers(const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &receipts)
-{
-        // We want to remove any previous items that have been set.
-        userList_->clear();
-
-        for (const auto &receipt : receipts) {
-                auto user = new ReceiptItem(this,
-                                            QString::fromStdString(receipt.second),
-                                            receipt.first,
-                                            ChatPage::instance()->currentRoom());
-                auto item = new QListWidgetItem(userList_);
-
-                item->setSizeHint(user->minimumSizeHint());
-                item->setFlags(Qt::NoItemFlags);
-                item->setTextAlignment(Qt::AlignCenter);
-
-                userList_->setItemWidget(item, user);
-        }
-}
-
-void
-ReadReceipts::paintEvent(QPaintEvent *)
-{
-        QStyleOption opt;
-        opt.init(this);
-        QPainter p(this);
-        style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-}
-
-void
-ReadReceipts::hideEvent(QHideEvent *event)
-{
-        userList_->clear();
-        QFrame::hideEvent(event);
-}
diff --git a/src/dialogs/ReadReceipts.h b/src/dialogs/ReadReceipts.h
deleted file mode 100644
index 5c6c5d2b..00000000
--- a/src/dialogs/ReadReceipts.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QDateTime>
-#include <QFrame>
-
-class Avatar;
-class QLabel;
-class QListWidget;
-class QHBoxLayout;
-class QVBoxLayout;
-
-namespace dialogs {
-
-class ReceiptItem : public QWidget
-{
-        Q_OBJECT
-
-public:
-        ReceiptItem(QWidget *parent,
-                    const QString &user_id,
-                    uint64_t timestamp,
-                    const QString &room_id);
-
-protected:
-        void paintEvent(QPaintEvent *) override;
-
-private:
-        QString dateFormat(const QDateTime &then) const;
-
-        QHBoxLayout *topLayout_;
-        QVBoxLayout *textLayout_;
-
-        Avatar *avatar_;
-
-        QLabel *userName_;
-        QLabel *timestamp_;
-};
-
-class ReadReceipts : public QFrame
-{
-        Q_OBJECT
-public:
-        explicit ReadReceipts(QWidget *parent = nullptr);
-
-public slots:
-        void addUsers(const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users);
-
-protected:
-        void paintEvent(QPaintEvent *event) override;
-        void hideEvent(QHideEvent *event) override;
-
-private:
-        QLabel *topLabel_;
-
-        QListWidget *userList_;
-};
-} // dialogs
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index ee5564a5..6ae0c4d1 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -28,6 +28,7 @@
 #include "MemberList.h"
 #include "MxcImageProvider.h"
 #include "Olm.h"
+#include "ReadReceiptsModel.h"
 #include "TimelineViewManager.h"
 #include "Utils.h"
 #include "dialogs/RawMessage.h"
@@ -1089,9 +1090,9 @@ TimelineModel::relatedInfo(QString id)
 }
 
 void
-TimelineModel::readReceiptsAction(QString id) const
+TimelineModel::showReadReceipts(QString id)
 {
-        MainWindow::instance()->openReadReceiptsDialog(id);
+        emit openReadReceiptsDialog(new ReadReceiptsProxy{id, roomId(), this});
 }
 
 void
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 0e2ce153..0d5f7109 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -20,6 +20,7 @@
 #include "InviteesModel.h"
 #include "MemberList.h"
 #include "Permissions.h"
+#include "ReadReceiptsModel.h"
 #include "ui/RoomSettings.h"
 #include "ui/UserProfile.h"
 
@@ -241,7 +242,7 @@ public:
         Q_INVOKABLE void openUserProfile(QString userid);
         Q_INVOKABLE void editAction(QString id);
         Q_INVOKABLE void replyAction(QString id);
-        Q_INVOKABLE void readReceiptsAction(QString id) const;
+        Q_INVOKABLE void showReadReceipts(QString id);
         Q_INVOKABLE void redactEvent(QString id);
         Q_INVOKABLE int idToIndex(QString id) const;
         Q_INVOKABLE QString indexToId(int index) const;
@@ -348,6 +349,7 @@ signals:
         void typingUsersChanged(std::vector<QString> users);
         void replyChanged(QString reply);
         void editChanged(QString reply);
+        void openReadReceiptsDialog(ReadReceiptsProxy *rr);
         void paginationInProgressChanged(const bool);
         void newCallEvent(const mtx::events::collections::TimelineEvents &event);
         void scrollToIndex(int index);
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index a6922be7..76bc127e 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -26,6 +26,7 @@
 #include "MainWindow.h"
 #include "MatrixClient.h"
 #include "MxcImageProvider.h"
+#include "ReadReceiptsModel.h"
 #include "RoomsModel.h"
 #include "SingleImagePackModel.h"
 #include "UserSettingsPage.h"
@@ -205,6 +206,12 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
           0,
           "InviteesModel",
           "InviteesModel needs to be instantiated on the C++ side");
+        qmlRegisterUncreatableType<ReadReceiptsProxy>(
+          "im.nheko",
+          1,
+          0,
+          "ReadReceiptsProxy",
+          "ReadReceiptsProxy needs to be instantiated on the C++ side");
 
         static auto self = this;
         qmlRegisterSingletonType<MainWindow>(
diff --git a/src/ui/Avatar.cpp b/src/ui/Avatar.cpp
deleted file mode 100644
index 154a0e2c..00000000
--- a/src/ui/Avatar.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QPainter>
-#include <QPainterPath>
-#include <QSettings>
-
-#include "AvatarProvider.h"
-#include "Utils.h"
-#include "ui/Avatar.h"
-
-Avatar::Avatar(QWidget *parent, int size)
-  : QWidget(parent)
-  , size_(size)
-{
-        type_   = ui::AvatarType::Letter;
-        letter_ = "A";
-
-        QFont _font(font());
-        _font.setPointSizeF(ui::FontSize);
-        setFont(_font);
-
-        QSizePolicy policy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
-        setSizePolicy(policy);
-}
-
-QColor
-Avatar::textColor() const
-{
-        if (!text_color_.isValid())
-                return QColor("black");
-
-        return text_color_;
-}
-
-QColor
-Avatar::backgroundColor() const
-{
-        if (!text_color_.isValid())
-                return QColor("white");
-
-        return background_color_;
-}
-
-QSize
-Avatar::sizeHint() const
-{
-        return QSize(size_ + 2, size_ + 2);
-}
-
-void
-Avatar::setTextColor(const QColor &color)
-{
-        text_color_ = color;
-}
-
-void
-Avatar::setBackgroundColor(const QColor &color)
-{
-        background_color_ = color;
-}
-
-void
-Avatar::setLetter(const QString &letter)
-{
-        letter_ = letter;
-        type_   = ui::AvatarType::Letter;
-        update();
-}
-
-void
-Avatar::setImage(const QString &avatar_url)
-{
-        avatar_url_ = avatar_url;
-        AvatarProvider::resolve(avatar_url,
-                                static_cast<int>(size_ * pixmap_.devicePixelRatio()),
-                                this,
-                                [this, requestedRatio = pixmap_.devicePixelRatio()](QPixmap pm) {
-                                        if (pm.isNull())
-                                                return;
-                                        type_   = ui::AvatarType::Image;
-                                        pixmap_ = pm;
-                                        pixmap_.setDevicePixelRatio(requestedRatio);
-                                        update();
-                                });
-}
-
-void
-Avatar::setImage(const QString &room, const QString &user)
-{
-        room_ = room;
-        user_ = user;
-        AvatarProvider::resolve(room,
-                                user,
-                                static_cast<int>(size_ * pixmap_.devicePixelRatio()),
-                                this,
-                                [this, requestedRatio = pixmap_.devicePixelRatio()](QPixmap pm) {
-                                        if (pm.isNull())
-                                                return;
-                                        type_   = ui::AvatarType::Image;
-                                        pixmap_ = pm;
-                                        pixmap_.setDevicePixelRatio(requestedRatio);
-                                        update();
-                                });
-}
-
-void
-Avatar::setDevicePixelRatio(double ratio)
-{
-        if (type_ == ui::AvatarType::Image && abs(pixmap_.devicePixelRatio() - ratio) > 0.01) {
-                pixmap_ = pixmap_.scaled(QSize(size_, size_) * ratio);
-                pixmap_.setDevicePixelRatio(ratio);
-
-                if (!avatar_url_.isEmpty())
-                        setImage(avatar_url_);
-                else
-                        setImage(room_, user_);
-        }
-}
-
-void
-Avatar::paintEvent(QPaintEvent *)
-{
-        bool rounded = QSettings().value(QStringLiteral("user/avatar_circles"), true).toBool();
-
-        QPainter painter(this);
-
-        painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform |
-                               QPainter::TextAntialiasing);
-
-        QRectF r     = rect();
-        const int hs = size_ / 2;
-
-        if (type_ != ui::AvatarType::Image) {
-                QBrush brush;
-                brush.setStyle(Qt::SolidPattern);
-                brush.setColor(backgroundColor());
-
-                painter.setPen(Qt::NoPen);
-                painter.setBrush(brush);
-                rounded ? painter.drawEllipse(r) : painter.drawRoundedRect(r, 3, 3);
-        } else if (painter.isActive()) {
-                setDevicePixelRatio(painter.device()->devicePixelRatioF());
-        }
-
-        switch (type_) {
-        case ui::AvatarType::Image: {
-                QPainterPath ppath;
-
-                rounded ? ppath.addEllipse(width() / 2 - hs, height() / 2 - hs, size_, size_)
-                        : ppath.addRoundedRect(r, 3, 3);
-
-                painter.setClipPath(ppath);
-                painter.drawPixmap(QRect(width() / 2 - hs, height() / 2 - hs, size_, size_),
-                                   pixmap_);
-                break;
-        }
-        case ui::AvatarType::Letter: {
-                painter.setPen(textColor());
-                painter.setBrush(Qt::NoBrush);
-                painter.drawText(r.translated(0, -1), Qt::AlignCenter, letter_);
-                break;
-        }
-        default:
-                break;
-        }
-}
diff --git a/src/ui/Avatar.h b/src/ui/Avatar.h
deleted file mode 100644
index bbf05be3..00000000
--- a/src/ui/Avatar.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QImage>
-#include <QPixmap>
-#include <QWidget>
-
-#include "Theme.h"
-
-class Avatar : public QWidget
-{
-        Q_OBJECT
-
-        Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
-        Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
-
-public:
-        explicit Avatar(QWidget *parent = nullptr, int size = ui::AvatarSize);
-
-        void setBackgroundColor(const QColor &color);
-        void setImage(const QString &avatar_url);
-        void setImage(const QString &room, const QString &user);
-        void setLetter(const QString &letter);
-        void setTextColor(const QColor &color);
-        void setDevicePixelRatio(double ratio);
-
-        QColor backgroundColor() const;
-        QColor textColor() const;
-
-        QSize sizeHint() const override;
-
-protected:
-        void paintEvent(QPaintEvent *event) override;
-
-private:
-        void init();
-
-        ui::AvatarType type_;
-        QString letter_;
-        QString avatar_url_, room_, user_;
-        QColor background_color_;
-        QColor text_color_;
-        QPixmap pixmap_;
-        int size_;
-};