mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 03:00:46 +03:00
Mark encrypted messages with a lock icon
This commit is contained in:
parent
95ce2ef920
commit
ccc6cd8dab
13 changed files with 200 additions and 102 deletions
|
@ -97,6 +97,7 @@ constexpr int headerLeftMargin = 15;
|
|||
|
||||
namespace fonts {
|
||||
constexpr int timestamp = 13;
|
||||
constexpr int indicator = timestamp - 2;
|
||||
constexpr int dateSeparator = conf::fontSize;
|
||||
} // namespace fonts
|
||||
} // namespace timeline
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <QDateTime>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QPainter>
|
||||
#include <QSettings>
|
||||
#include <QStyle>
|
||||
|
@ -43,6 +44,46 @@ class VideoItem;
|
|||
class FileItem;
|
||||
class Avatar;
|
||||
|
||||
enum class StatusIndicatorState
|
||||
{
|
||||
//! The encrypted message was received by the server.
|
||||
Encrypted,
|
||||
//! The plaintext message was received by the server.
|
||||
Received,
|
||||
//! The client sent the message. Not yet received.
|
||||
Sent,
|
||||
//! When the message is loaded from cache or backfill.
|
||||
Empty,
|
||||
};
|
||||
|
||||
//!
|
||||
//! Used to notify the user about the status of a message.
|
||||
//!
|
||||
class StatusIndicator : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StatusIndicator(QWidget *parent);
|
||||
void setState(StatusIndicatorState state);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
void paintIcon(QPainter &p, QIcon &icon);
|
||||
|
||||
QIcon lockIcon_;
|
||||
QIcon clockIcon_;
|
||||
QIcon checkmarkIcon_;
|
||||
|
||||
QColor iconColor_ = QColor("#999");
|
||||
|
||||
StatusIndicatorState state_ = StatusIndicatorState::Empty;
|
||||
|
||||
static constexpr int MaxWidth = 24;
|
||||
};
|
||||
|
||||
class TextLabel : public QTextBrowser
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -192,7 +233,8 @@ public:
|
|||
DescInfo descriptionMessage() const { return descriptionMsg_; }
|
||||
QString eventId() const { return event_id_; }
|
||||
void setEventId(const QString &event_id) { event_id_ = event_id; }
|
||||
void markReceived();
|
||||
void markReceived(bool isEncrypted);
|
||||
void markSent();
|
||||
bool isReceived() { return isReceived_; };
|
||||
void setRoomId(QString room_id) { room_id_ = room_id; }
|
||||
void sendReadReceipt() const;
|
||||
|
@ -228,6 +270,9 @@ private:
|
|||
void setupAvatarLayout(const QString &userName);
|
||||
void setupSimpleLayout();
|
||||
|
||||
void adjustMessageLayout();
|
||||
void adjustMessageLayoutForWidget();
|
||||
|
||||
//! Whether or not the event associated with the widget
|
||||
//! has been acknowledged by the server.
|
||||
bool isReceived_ = false;
|
||||
|
@ -247,7 +292,6 @@ private:
|
|||
QHBoxLayout *topLayout_ = nullptr;
|
||||
QHBoxLayout *messageLayout_ = nullptr;
|
||||
QVBoxLayout *mainLayout_ = nullptr;
|
||||
QVBoxLayout *headerLayout_ = nullptr;
|
||||
QHBoxLayout *widgetLayout_ = nullptr;
|
||||
|
||||
Avatar *userAvatar_;
|
||||
|
@ -255,8 +299,9 @@ private:
|
|||
QFont font_;
|
||||
QFont usernameFont_;
|
||||
|
||||
StatusIndicator *statusIndicator_;
|
||||
|
||||
QLabel *timestamp_;
|
||||
QLabel *checkmark_;
|
||||
QLabel *userName_;
|
||||
TextLabel *body_;
|
||||
};
|
||||
|
@ -285,20 +330,13 @@ TimelineItem::setupLocalWidgetLayout(Widget *widget, const QString &userid, bool
|
|||
generateBody(userid, displayName, "");
|
||||
setupAvatarLayout(displayName);
|
||||
|
||||
headerLayout_->addLayout(widgetLayout_);
|
||||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
setupSimpleLayout();
|
||||
|
||||
messageLayout_->addLayout(widgetLayout_, 1);
|
||||
}
|
||||
|
||||
messageLayout_->addWidget(checkmark_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
mainLayout_->addLayout(messageLayout_);
|
||||
adjustMessageLayoutForWidget();
|
||||
}
|
||||
|
||||
template<class Event, class Widget>
|
||||
|
@ -331,18 +369,11 @@ TimelineItem::setupWidgetLayout(Widget *widget, const Event &event, bool withSen
|
|||
generateBody(sender, displayName, "");
|
||||
setupAvatarLayout(displayName);
|
||||
|
||||
headerLayout_->addLayout(widgetLayout_);
|
||||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
setupSimpleLayout();
|
||||
|
||||
messageLayout_->addLayout(widgetLayout_, 1);
|
||||
}
|
||||
|
||||
messageLayout_->addWidget(checkmark_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
mainLayout_->addLayout(messageLayout_);
|
||||
adjustMessageLayoutForWidget();
|
||||
}
|
||||
|
|
BIN
resources/icons/ui/checkmark.png
Normal file
BIN
resources/icons/ui/checkmark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 450 B |
BIN
resources/icons/ui/checkmark@2x.png
Normal file
BIN
resources/icons/ui/checkmark@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 551 B |
BIN
resources/icons/ui/clock.png
Normal file
BIN
resources/icons/ui/clock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 779 B |
BIN
resources/icons/ui/clock@2x.png
Normal file
BIN
resources/icons/ui/clock@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
resources/icons/ui/lock.png
Normal file
BIN
resources/icons/ui/lock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 602 B |
BIN
resources/icons/ui/lock@2x.png
Normal file
BIN
resources/icons/ui/lock@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 810 B |
|
@ -1,5 +1,11 @@
|
|||
<RCC>
|
||||
<qresource prefix="/icons">
|
||||
<file>icons/ui/lock.png</file>
|
||||
<file>icons/ui/lock@2x.png</file>
|
||||
<file>icons/ui/clock.png</file>
|
||||
<file>icons/ui/clock@2x.png</file>
|
||||
<file>icons/ui/checkmark.png</file>
|
||||
<file>icons/ui/checkmark@2x.png</file>
|
||||
<file>icons/ui/cursor.png</file>
|
||||
<file>icons/ui/cursor@2x.png</file>
|
||||
<file>icons/ui/settings.png</file>
|
||||
|
|
|
@ -48,7 +48,7 @@ CommunitiesList > * {
|
|||
}
|
||||
|
||||
FlatButton {
|
||||
qproperty-foregroundColor: #14272d;
|
||||
qproperty-foregroundColor: #495057;
|
||||
}
|
||||
|
||||
FileItem {
|
||||
|
|
32
src/main.cc
32
src/main.cc
|
@ -38,6 +38,36 @@
|
|||
#include "RunGuard.h"
|
||||
#include "version.hpp"
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
#include <boost/stacktrace.hpp>
|
||||
#include <signal.h>
|
||||
|
||||
void
|
||||
stacktraceHandler(int signum)
|
||||
{
|
||||
auto dir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||
|
||||
std::signal(signum, SIG_DFL);
|
||||
boost::stacktrace::safe_dump_to(dir.toStdString() + "/backtrace.dump");
|
||||
std::raise(SIGABRT);
|
||||
}
|
||||
|
||||
void
|
||||
registerSignalHandlers()
|
||||
{
|
||||
std::signal(SIGSEGV, &stacktraceHandler);
|
||||
std::signal(SIGABRT, &stacktraceHandler);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// No implementation for systems with no stacktrace support.
|
||||
void
|
||||
registerSignalHandlers()
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
QPoint
|
||||
screenCenter(int width, int height)
|
||||
{
|
||||
|
@ -126,6 +156,8 @@ main(int argc, char *argv[])
|
|||
|
||||
createCacheDirectory();
|
||||
|
||||
registerSignalHandlers();
|
||||
|
||||
try {
|
||||
nhlog::init(QString("%1/nheko.log")
|
||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "ChatPage.h"
|
||||
#include "Config.h"
|
||||
#include "Logging.hpp"
|
||||
#include "Painter.h"
|
||||
|
||||
#include "timeline/TimelineItem.h"
|
||||
#include "timeline/widgets/AudioItem.h"
|
||||
|
@ -31,11 +32,90 @@
|
|||
#include "timeline/widgets/ImageItem.h"
|
||||
#include "timeline/widgets/VideoItem.h"
|
||||
|
||||
constexpr const static char *CHECKMARK = "✓";
|
||||
|
||||
constexpr int MSG_RIGHT_MARGIN = 7;
|
||||
constexpr int MSG_PADDING = 20;
|
||||
|
||||
StatusIndicator::StatusIndicator(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
lockIcon_.addFile(":/icons/icons/ui/lock.png");
|
||||
clockIcon_.addFile(":/icons/icons/ui/clock.png");
|
||||
checkmarkIcon_.addFile(":/icons/icons/ui/checkmark.png");
|
||||
}
|
||||
|
||||
void
|
||||
StatusIndicator::paintIcon(QPainter &p, QIcon &icon)
|
||||
{
|
||||
auto pixmap = icon.pixmap(width());
|
||||
|
||||
QPainter painter(&pixmap);
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||
painter.fillRect(pixmap.rect(), p.pen().color());
|
||||
|
||||
QIcon(pixmap).paint(&p, rect(), Qt::AlignCenter, QIcon::Normal);
|
||||
}
|
||||
|
||||
void
|
||||
StatusIndicator::paintEvent(QPaintEvent *)
|
||||
{
|
||||
if (state_ == StatusIndicatorState::Empty)
|
||||
return;
|
||||
|
||||
Painter p(this);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
p.setPen(iconColor_);
|
||||
|
||||
switch (state_) {
|
||||
case StatusIndicatorState::Sent: {
|
||||
paintIcon(p, clockIcon_);
|
||||
break;
|
||||
}
|
||||
case StatusIndicatorState::Encrypted:
|
||||
paintIcon(p, lockIcon_);
|
||||
break;
|
||||
case StatusIndicatorState::Received: {
|
||||
paintIcon(p, checkmarkIcon_);
|
||||
break;
|
||||
}
|
||||
case StatusIndicatorState::Empty:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StatusIndicator::setState(StatusIndicatorState state)
|
||||
{
|
||||
state_ = state;
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineItem::adjustMessageLayoutForWidget()
|
||||
{
|
||||
messageLayout_->addLayout(widgetLayout_, 1);
|
||||
messageLayout_->addWidget(statusIndicator_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
|
||||
messageLayout_->setAlignment(statusIndicator_, Qt::AlignTop);
|
||||
messageLayout_->setAlignment(timestamp_, Qt::AlignTop);
|
||||
|
||||
mainLayout_->addLayout(messageLayout_);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineItem::adjustMessageLayout()
|
||||
{
|
||||
messageLayout_->addWidget(body_, 1);
|
||||
messageLayout_->addWidget(statusIndicator_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
|
||||
messageLayout_->setAlignment(statusIndicator_, Qt::AlignTop);
|
||||
messageLayout_->setAlignment(timestamp_, Qt::AlignTop);
|
||||
|
||||
mainLayout_->addLayout(messageLayout_);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineItem::init()
|
||||
{
|
||||
|
@ -102,14 +182,13 @@ TimelineItem::init()
|
|||
mainLayout_->setContentsMargins(conf::timeline::headerLeftMargin, 0, 0, 0);
|
||||
mainLayout_->setSpacing(0);
|
||||
|
||||
QFont checkmarkFont;
|
||||
checkmarkFont.setPixelSize(conf::timeline::fonts::timestamp);
|
||||
QFont timestampFont;
|
||||
timestampFont.setPixelSize(conf::timeline::fonts::indicator);
|
||||
QFontMetrics tsFm(timestampFont);
|
||||
|
||||
// Setting fixed width for checkmark because systems may have a differing width for a
|
||||
// space and the Unicode checkmark.
|
||||
checkmark_ = new QLabel(this);
|
||||
checkmark_->setFont(checkmarkFont);
|
||||
checkmark_->setFixedWidth(QFontMetrics{checkmarkFont}.width(CHECKMARK));
|
||||
statusIndicator_ = new StatusIndicator(this);
|
||||
statusIndicator_->setFixedWidth(tsFm.height() - tsFm.leading());
|
||||
statusIndicator_->setFixedHeight(tsFm.height() - tsFm.leading());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -147,20 +226,14 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
|
|||
generateBody(userid, displayName, body);
|
||||
setupAvatarLayout(displayName);
|
||||
|
||||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
generateBody(body);
|
||||
setupSimpleLayout();
|
||||
|
||||
messageLayout_->addWidget(body_, 1);
|
||||
}
|
||||
|
||||
messageLayout_->addWidget(checkmark_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
mainLayout_->addLayout(messageLayout_);
|
||||
adjustMessageLayout();
|
||||
}
|
||||
|
||||
TimelineItem::TimelineItem(ImageItem *image,
|
||||
|
@ -316,20 +389,14 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
|
|||
generateBody(sender, displayName, body);
|
||||
setupAvatarLayout(displayName);
|
||||
|
||||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
generateBody(body);
|
||||
setupSimpleLayout();
|
||||
|
||||
messageLayout_->addWidget(body_, 1);
|
||||
}
|
||||
|
||||
messageLayout_->addWidget(checkmark_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
mainLayout_->addLayout(messageLayout_);
|
||||
adjustMessageLayout();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -364,20 +431,14 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote>
|
|||
generateBody(sender, displayName, emoteMsg);
|
||||
setupAvatarLayout(displayName);
|
||||
|
||||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
generateBody(emoteMsg);
|
||||
setupSimpleLayout();
|
||||
|
||||
messageLayout_->addWidget(body_, 1);
|
||||
}
|
||||
|
||||
messageLayout_->addWidget(checkmark_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
mainLayout_->addLayout(messageLayout_);
|
||||
adjustMessageLayout();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -417,28 +478,31 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
|
|||
generateBody(sender, displayName, body);
|
||||
setupAvatarLayout(displayName);
|
||||
|
||||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
generateBody(body);
|
||||
setupSimpleLayout();
|
||||
|
||||
messageLayout_->addWidget(body_, 1);
|
||||
}
|
||||
|
||||
messageLayout_->addWidget(checkmark_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
mainLayout_->addLayout(messageLayout_);
|
||||
adjustMessageLayout();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineItem::markReceived()
|
||||
TimelineItem::markSent()
|
||||
{
|
||||
statusIndicator_->setState(StatusIndicatorState::Sent);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineItem::markReceived(bool isEncrypted)
|
||||
{
|
||||
isReceived_ = true;
|
||||
checkmark_->setText(CHECKMARK);
|
||||
checkmark_->setAlignment(Qt::AlignTop);
|
||||
|
||||
if (isEncrypted)
|
||||
statusIndicator_->setState(StatusIndicatorState::Encrypted);
|
||||
else
|
||||
statusIndicator_->setState(StatusIndicatorState::Received);
|
||||
|
||||
sendReadReceipt();
|
||||
}
|
||||
|
@ -506,17 +570,10 @@ TimelineItem::generateTimestamp(const QDateTime &time)
|
|||
QFont timestampFont;
|
||||
timestampFont.setPixelSize(conf::timeline::fonts::timestamp);
|
||||
|
||||
QFontMetrics fm(timestampFont);
|
||||
int topMargin = QFontMetrics(font_).ascent() - fm.ascent();
|
||||
|
||||
timestamp_ = new QLabel(this);
|
||||
timestamp_->setAlignment(Qt::AlignTop);
|
||||
timestamp_->setFont(timestampFont);
|
||||
timestamp_->setText(
|
||||
QString("<span style=\"color: #999\"> %1 </span>").arg(time.toString("HH:mm")));
|
||||
timestamp_->setContentsMargins(0, topMargin, 0, 0);
|
||||
timestamp_->setStyleSheet(
|
||||
QString("font-size: %1px;").arg(conf::timeline::fonts::timestamp));
|
||||
}
|
||||
|
||||
QString
|
||||
|
@ -557,15 +614,8 @@ TimelineItem::setupAvatarLayout(const QString &userName)
|
|||
topLayout_->insertWidget(0, userAvatar_);
|
||||
topLayout_->setAlignment(userAvatar_, Qt::AlignTop);
|
||||
|
||||
headerLayout_ = new QVBoxLayout;
|
||||
headerLayout_->setMargin(0);
|
||||
headerLayout_->setSpacing(conf::timeline::headerSpacing);
|
||||
|
||||
if (userName_)
|
||||
headerLayout_->addWidget(userName_);
|
||||
|
||||
if (body_)
|
||||
headerLayout_->addWidget(body_);
|
||||
mainLayout_->insertWidget(0, userName_);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -647,33 +697,8 @@ TimelineItem::addAvatar()
|
|||
userName_->setFont(usernameFont_);
|
||||
userName_->setText(fm.elidedText(displayName, Qt::ElideRight, 500));
|
||||
|
||||
QWidget *widget = nullptr;
|
||||
|
||||
// Extract the widget before we delete its layout.
|
||||
if (widgetLayout_)
|
||||
widget = widgetLayout_->itemAt(0)->widget();
|
||||
|
||||
// Remove all items from the layout.
|
||||
QLayoutItem *item;
|
||||
while ((item = messageLayout_->takeAt(0)) != 0)
|
||||
delete item;
|
||||
|
||||
setupAvatarLayout(displayName);
|
||||
|
||||
// Restore widget's layout.
|
||||
if (widget) {
|
||||
widgetLayout_ = new QHBoxLayout();
|
||||
widgetLayout_->setContentsMargins(0, 2, 0, 2);
|
||||
widgetLayout_->addWidget(widget);
|
||||
widgetLayout_->addStretch(1);
|
||||
|
||||
headerLayout_->addLayout(widgetLayout_);
|
||||
}
|
||||
|
||||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
messageLayout_->addWidget(checkmark_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
}
|
||||
|
|
|
@ -625,7 +625,7 @@ TimelineView::updatePendingMessage(const std::string &txn_id, const QString &eve
|
|||
// If the response comes after we have received the event from sync
|
||||
// we've already marked the widget as received.
|
||||
if (!msg.widget->isReceived()) {
|
||||
msg.widget->markReceived();
|
||||
msg.widget->markReceived(msg.is_encrypted);
|
||||
pending_sent_msgs_.append(msg);
|
||||
}
|
||||
} else {
|
||||
|
@ -690,6 +690,9 @@ TimelineView::sendNextPendingMessage()
|
|||
|
||||
nhlog::ui()->info("[{}] sending next queued message", m.txn_id);
|
||||
|
||||
if (m.widget)
|
||||
m.widget->markSent();
|
||||
|
||||
if (m.is_encrypted) {
|
||||
nhlog::ui()->info("[{}] sending encrypted event", m.txn_id);
|
||||
prepareEncryptedMessage(std::move(m));
|
||||
|
@ -835,7 +838,7 @@ TimelineView::removePendingMessage(const std::string &txn_id)
|
|||
for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); ++it) {
|
||||
if (it->txn_id == txn_id) {
|
||||
if (it->widget)
|
||||
it->widget->markReceived();
|
||||
it->widget->markReceived(it->is_encrypted);
|
||||
|
||||
nhlog::ui()->info("[{}] received sync before message response", txn_id);
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue