mirror of https://github.com/Nheko-Reborn/nheko
There are probably a few things wrong with this, but I'm going to call it good enough for an initial commitpull/655/head
parent
d955444dc1
commit
4dd994ae00
@ -0,0 +1,118 @@ |
|||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors |
||||||
|
// |
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||||
|
|
||||||
|
import QtQuick 2.15 |
||||||
|
import QtQuick.Controls 2.15 |
||||||
|
import QtQuick.Layouts 1.15 |
||||||
|
import im.nheko 1.0 |
||||||
|
|
||||||
|
ApplicationWindow { |
||||||
|
id: readReceiptsRoot |
||||||
|
|
||||||
|
property ReadReceiptsModel readReceipts |
||||||
|
|
||||||
|
x: MainWindow.x + (MainWindow.width / 2) - (width / 2) |
||||||
|
y: MainWindow.y + (MainWindow.height / 2) - (height / 2) |
||||||
|
height: 380 |
||||||
|
width: 340 |
||||||
|
minimumHeight: 380 |
||||||
|
minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium |
||||||
|
palette: Nheko.colors |
||||||
|
color: Nheko.colors.window |
||||||
|
|
||||||
|
ColumnLayout { |
||||||
|
anchors.fill: parent |
||||||
|
anchors.margins: Nheko.paddingMedium |
||||||
|
spacing: Nheko.paddingMedium |
||||||
|
|
||||||
|
Label { |
||||||
|
id: headerTitle |
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignCenter |
||||||
|
text: qsTr("Read receipts") |
||||||
|
font.pointSize: fontMetrics.font.pointSize * 1.5 |
||||||
|
} |
||||||
|
|
||||||
|
ScrollView { |
||||||
|
palette: Nheko.colors |
||||||
|
padding: Nheko.paddingMedium |
||||||
|
ScrollBar.horizontal.visible: false |
||||||
|
Layout.fillHeight: true |
||||||
|
Layout.minimumHeight: 200 |
||||||
|
Layout.fillWidth: true |
||||||
|
|
||||||
|
ListView { |
||||||
|
id: readReceiptsList |
||||||
|
|
||||||
|
clip: true |
||||||
|
spacing: Nheko.paddingMedium |
||||||
|
boundsBehavior: Flickable.StopAtBounds |
||||||
|
model: readReceipts |
||||||
|
|
||||||
|
delegate: RowLayout { |
||||||
|
spacing: Nheko.paddingMedium |
||||||
|
|
||||||
|
Avatar { |
||||||
|
width: Nheko.avatarSize |
||||||
|
height: Nheko.avatarSize |
||||||
|
userid: model.mxid |
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/") |
||||||
|
displayName: model.displayName |
||||||
|
onClicked: Rooms.currentRoom.openUserProfile(model.mxid) |
||||||
|
ToolTip.visible: avatarHover.hovered |
||||||
|
ToolTip.text: model.mxid |
||||||
|
|
||||||
|
HoverHandler { |
||||||
|
id: avatarHover |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
ColumnLayout { |
||||||
|
spacing: Nheko.paddingSmall |
||||||
|
|
||||||
|
Label { |
||||||
|
text: model.displayName |
||||||
|
color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window) |
||||||
|
font.pointSize: fontMetrics.font.pointSize |
||||||
|
ToolTip.visible: displayNameHover.hovered |
||||||
|
ToolTip.text: model.mxid |
||||||
|
|
||||||
|
TapHandler { |
||||||
|
onSingleTapped: chat.model.openUserProfile(userId) |
||||||
|
} |
||||||
|
|
||||||
|
CursorShape { |
||||||
|
anchors.fill: parent |
||||||
|
cursorShape: Qt.PointingHandCursor |
||||||
|
} |
||||||
|
|
||||||
|
HoverHandler { |
||||||
|
id: displayNameHover |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Label { |
||||||
|
text: model.timestamp |
||||||
|
color: Nheko.colors.buttonText |
||||||
|
font.pointSize: fontMetrics.font.pointSize * 0.9 |
||||||
|
} |
||||||
|
|
||||||
|
Item { |
||||||
|
Layout.fillHeight: true |
||||||
|
Layout.fillWidth: true |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "ReadReceiptsModel.h" |
||||||
|
|
||||||
|
#include <QLocale> |
||||||
|
|
||||||
|
#include "Cache.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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ReadReceiptsModel::~ReadReceiptsModel() |
||||||
|
{ |
||||||
|
for (const auto &item : readReceipts_) |
||||||
|
item->deleteLater(); |
||||||
|
} |
||||||
|
|
||||||
|
QHash<int, QByteArray> |
||||||
|
ReadReceiptsModel::roleNames() const |
||||||
|
{ |
||||||
|
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()]->mxid(); |
||||||
|
case DisplayName: |
||||||
|
return readReceipts_[index.row()]->displayName(); |
||||||
|
case AvatarUrl: |
||||||
|
return readReceipts_[index.row()]->avatarUrl(); |
||||||
|
case Timestamp: |
||||||
|
// the uint64_t to QVariant conversion was ambiguous, so...
|
||||||
|
return readReceipts_[index.row()]->timestamp(); |
||||||
|
default: |
||||||
|
return {}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ReadReceiptsModel::addUsers( |
||||||
|
const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users) |
||||||
|
{ |
||||||
|
std::multimap<uint64_t, std::string, std::greater<uint64_t>> unshown; |
||||||
|
for (const auto &user : users) { |
||||||
|
if (users_.find(user.first) == users_.end()) |
||||||
|
unshown.emplace(user); |
||||||
|
} |
||||||
|
|
||||||
|
beginInsertRows( |
||||||
|
QModelIndex{}, readReceipts_.length(), readReceipts_.length() + unshown.size() - 1); |
||||||
|
|
||||||
|
for (const auto &user : unshown) |
||||||
|
readReceipts_.push_back( |
||||||
|
new ReadReceipt{QString::fromStdString(user.second), room_id_, user.first, this}); |
||||||
|
|
||||||
|
users_.merge(unshown); |
||||||
|
|
||||||
|
endInsertRows(); |
||||||
|
} |
||||||
|
|
||||||
|
ReadReceipt::ReadReceipt(QString mxid, QString room_id, uint64_t timestamp, QObject *parent) |
||||||
|
: QObject{parent} |
||||||
|
, mxid_{mxid} |
||||||
|
, room_id_{room_id} |
||||||
|
, displayName_{cache::displayName(room_id_, mxid_)} |
||||||
|
, avatarUrl_{cache::avatarUrl(room_id_, mxid_)} |
||||||
|
, timestamp_{timestamp} |
||||||
|
{} |
||||||
|
|
||||||
|
QString |
||||||
|
ReadReceipt::timestamp() const |
||||||
|
{ |
||||||
|
return dateFormat(QDateTime::fromMSecsSinceEpoch(timestamp_)); |
||||||
|
} |
||||||
|
|
||||||
|
QString |
||||||
|
ReadReceipt::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); |
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef READRECEIPTSMODEL_H |
||||||
|
#define READRECEIPTSMODEL_H |
||||||
|
|
||||||
|
#include <QAbstractListModel> |
||||||
|
#include <QObject> |
||||||
|
#include <QString> |
||||||
|
|
||||||
|
class ReadReceipt : public QObject |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
|
||||||
|
Q_PROPERTY(QString mxid READ mxid CONSTANT) |
||||||
|
Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameChanged) |
||||||
|
Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged) |
||||||
|
Q_PROPERTY(QString timestamp READ timestamp CONSTANT) |
||||||
|
|
||||||
|
public: |
||||||
|
explicit ReadReceipt(QString mxid, |
||||||
|
QString room_id, |
||||||
|
uint64_t timestamp, |
||||||
|
QObject *parent = nullptr); |
||||||
|
|
||||||
|
QString mxid() const { return mxid_; } |
||||||
|
QString displayName() const { return displayName_; } |
||||||
|
QString avatarUrl() const { return avatarUrl_; } |
||||||
|
QString timestamp() const; |
||||||
|
|
||||||
|
signals: |
||||||
|
void displayNameChanged(); |
||||||
|
void avatarUrlChanged(); |
||||||
|
|
||||||
|
private: |
||||||
|
QString dateFormat(const QDateTime &then) const; |
||||||
|
|
||||||
|
QString mxid_; |
||||||
|
QString room_id_; |
||||||
|
QString displayName_; |
||||||
|
QString avatarUrl_; |
||||||
|
uint64_t timestamp_; |
||||||
|
}; |
||||||
|
|
||||||
|
class ReadReceiptsModel : public QAbstractListModel |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
|
||||||
|
Q_PROPERTY(QString eventId READ eventId CONSTANT) |
||||||
|
Q_PROPERTY(QString roomId READ roomId CONSTANT) |
||||||
|
|
||||||
|
public: |
||||||
|
enum Roles |
||||||
|
{ |
||||||
|
Mxid, |
||||||
|
DisplayName, |
||||||
|
AvatarUrl, |
||||||
|
Timestamp, |
||||||
|
}; |
||||||
|
|
||||||
|
explicit ReadReceiptsModel(QString event_id, QString room_id, QObject *parent = nullptr); |
||||||
|
~ReadReceiptsModel() override; |
||||||
|
|
||||||
|
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); |
||||||
|
|
||||||
|
private: |
||||||
|
QString event_id_; |
||||||
|
QString room_id_; |
||||||
|
QVector<ReadReceipt *> readReceipts_; |
||||||
|
std::multimap<uint64_t, std::string, std::greater<uint64_t>> users_; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // READRECEIPTSMODEL_H
|
@ -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); |
|
||||||
} |
|
@ -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
|
|
Loading…
Reference in new issue