Add a timeline message when encryption is enabled

This commit is contained in:
Konstantinos Sideris 2018-07-07 13:39:53 +03:00
parent 67458dd2f8
commit 9a0e18dea7
8 changed files with 181 additions and 107 deletions

View File

@ -170,6 +170,7 @@ set(SRC_FILES
src/ui/Avatar.cc src/ui/Avatar.cc
src/ui/Badge.cc src/ui/Badge.cc
src/ui/LoadingIndicator.cc src/ui/LoadingIndicator.cc
src/ui/InfoMessage.cpp
src/ui/FlatButton.cc src/ui/FlatButton.cc
src/ui/FloatingButton.cc src/ui/FloatingButton.cc
src/ui/Label.cc src/ui/Label.cc
@ -283,6 +284,7 @@ qt5_wrap_cpp(MOC_HEADERS
include/ui/Avatar.h include/ui/Avatar.h
include/ui/Badge.h include/ui/Badge.h
include/ui/LoadingIndicator.h include/ui/LoadingIndicator.h
include/ui/InfoMessage.hpp
include/ui/FlatButton.h include/ui/FlatButton.h
include/ui/Label.h include/ui/Label.h
include/ui/FloatingButton.h include/ui/FloatingButton.h

View File

@ -107,40 +107,6 @@ enum class TimelineDirection
Bottom, Bottom,
}; };
class DateSeparator : public QWidget
{
Q_OBJECT
Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
Q_PROPERTY(QColor boxColor WRITE setBoxColor READ boxColor)
public:
DateSeparator(QDateTime datetime, QWidget *parent = nullptr);
void setTextColor(QColor color) { textColor_ = color; }
void setBoxColor(QColor color) { boxColor_ = color; }
QColor textColor() const { return textColor_; }
QColor boxColor() const { return boxColor_; }
protected:
void paintEvent(QPaintEvent *event) override;
private:
static constexpr int VPadding = 6;
static constexpr int HPadding = 12;
static constexpr int HMargin = 20;
int width_;
int height_;
QString msg_;
QFont font_;
QColor textColor_ = QColor("black");
QColor boxColor_ = QColor("white");
};
class TimelineView : public QWidget class TimelineView : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -162,7 +128,6 @@ public:
uint64_t size); uint64_t size);
void updatePendingMessage(const std::string &txn_id, const QString &event_id); void updatePendingMessage(const std::string &txn_id, const QString &event_id);
void scrollDown(); void scrollDown();
QLabel *createDateSeparator(QDateTime datetime);
//! Remove an item from the timeline with the given Event ID. //! Remove an item from the timeline with the given Event ID.
void removeEvent(const QString &event_id); void removeEvent(const QString &event_id);
@ -220,7 +185,7 @@ private:
void getMessages(); void getMessages();
//! HACK: Fixing layout flickering when adding to the bottom //! HACK: Fixing layout flickering when adding to the bottom
//! of the timeline. //! of the timeline.
void pushTimelineItem(TimelineItem *item) void pushTimelineItem(QWidget *item)
{ {
item->hide(); item->hide();
scroll_layout_->addWidget(item); scroll_layout_->addWidget(item);
@ -230,7 +195,7 @@ private:
//! Decides whether or not to show or hide the scroll down button. //! Decides whether or not to show or hide the scroll down button.
void toggleScrollDownButton(); void toggleScrollDownButton();
void init(); void init();
void addTimelineItem(TimelineItem *item, void addTimelineItem(QWidget *item,
TimelineDirection direction = TimelineDirection::Bottom); TimelineDirection direction = TimelineDirection::Bottom);
void updateLastSender(const QString &user_id, TimelineDirection direction); void updateLastSender(const QString &user_id, TimelineDirection direction);
void notifyForLastEvent(); void notifyForLastEvent();
@ -295,8 +260,8 @@ private:
const QDateTime &second = QDateTime::currentDateTime()) const; const QDateTime &second = QDateTime::currentDateTime()) const;
// Return nullptr if the event couldn't be parsed. // Return nullptr if the event couldn't be parsed.
TimelineItem *parseMessageEvent(const mtx::events::collections::TimelineEvents &event, QWidget *parseMessageEvent(const mtx::events::collections::TimelineEvents &event,
TimelineDirection direction); TimelineDirection direction);
QVBoxLayout *top_layout_; QVBoxLayout *top_layout_;
QVBoxLayout *scroll_layout_; QVBoxLayout *scroll_layout_;

View File

@ -0,0 +1,47 @@
#pragma once
#include <QColor>
#include <QDateTime>
#include <QWidget>
class InfoMessage : public QWidget
{
Q_OBJECT
Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
Q_PROPERTY(QColor boxColor WRITE setBoxColor READ boxColor)
public:
explicit InfoMessage(QWidget *parent = nullptr);
InfoMessage(QString msg, QWidget *parent = nullptr);
void setTextColor(QColor color) { textColor_ = color; }
void setBoxColor(QColor color) { boxColor_ = color; }
void saveDatetime(QDateTime datetime) { datetime_ = datetime; }
QColor textColor() const { return textColor_; }
QColor boxColor() const { return boxColor_; }
QDateTime datetime() const { return datetime_; }
protected:
void paintEvent(QPaintEvent *event) override;
int width_;
int height_;
QString msg_;
QFont font_;
QDateTime datetime_;
QColor textColor_ = QColor("black");
QColor boxColor_ = QColor("white");
};
class DateSeparator : public InfoMessage
{
Q_OBJECT
public:
DateSeparator(QDateTime datetime, QWidget *parent = nullptr);
};

View File

@ -23,7 +23,7 @@ QuickSwitcher {
background-color: #202228; background-color: #202228;
} }
DateSeparator { InfoMessage {
qproperty-textColor: #caccd1; qproperty-textColor: #caccd1;
qproperty-boxColor: rgba(45, 49, 57, 120); qproperty-boxColor: rgba(45, 49, 57, 120);
} }

View File

@ -23,7 +23,7 @@ QuickSwitcher {
background-color: white; background-color: white;
} }
DateSeparator { InfoMessage {
qproperty-textColor: #333; qproperty-textColor: #333;
qproperty-boxColor: rgba(220, 220, 220, 120); qproperty-boxColor: rgba(220, 220, 220, 120);
} }

View File

@ -25,7 +25,7 @@ QuickSwitcher {
background-color: palette(window); background-color: palette(window);
} }
DateSeparator { InfoMessage {
qproperty-textColor: palette(text); qproperty-textColor: palette(text);
qproperty-boxColor: palette(window); qproperty-boxColor: palette(window);
} }

View File

@ -23,6 +23,7 @@
#include "ChatPage.h" #include "ChatPage.h"
#include "Config.h" #include "Config.h"
#include "FloatingButton.h" #include "FloatingButton.h"
#include "InfoMessage.hpp"
#include "Logging.hpp" #include "Logging.hpp"
#include "Olm.hpp" #include "Olm.hpp"
#include "UserSettingsPage.h" #include "UserSettingsPage.h"
@ -36,55 +37,19 @@
using TimelineEvent = mtx::events::collections::TimelineEvents; using TimelineEvent = mtx::events::collections::TimelineEvents;
DateSeparator::DateSeparator(QDateTime datetime, QWidget *parent) //! Retrieve the timestamp of the event represented by the given widget.
: QWidget{parent} QDateTime
getDate(QWidget *widget)
{ {
auto now = QDateTime::currentDateTime(); auto item = qobject_cast<TimelineItem *>(widget);
auto days = now.daysTo(datetime); if (item)
return item->descriptionMessage().datetime;
font_.setWeight(60); auto infoMsg = qobject_cast<InfoMessage *>(widget);
font_.setPixelSize(conf::timeline::fonts::dateSeparator); if (infoMsg)
return infoMsg->datetime();
QString fmt; return QDateTime();
if (now.date().year() != datetime.date().year())
fmt = QString("ddd d MMMM yy");
else
fmt = QString("ddd d MMMM");
if (days == 0)
msg_ = tr("Today");
else if (std::abs(days) == 1)
msg_ = tr("Yesterday");
else
msg_ = datetime.toString(fmt);
QFontMetrics fm{font_};
width_ = fm.width(msg_) + HPadding * 2;
height_ = fm.ascent() + 2 * VPadding;
setFixedHeight(height_ + 2 * HMargin);
}
void
DateSeparator::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.setFont(font_);
// Center the box horizontally & vertically.
auto textRegion = QRectF(width() / 2 - width_ / 2, HMargin, width_, height_);
QPainterPath ppath;
ppath.addRoundedRect(textRegion, height_ / 2, height_ / 2);
p.setPen(Qt::NoPen);
p.fillPath(ppath, boxColor());
p.drawPath(ppath);
p.setPen(QPen(textColor()));
p.drawText(textRegion, Qt::AlignCenter, msg_);
} }
TimelineView::TimelineView(const mtx::responses::Timeline &timeline, TimelineView::TimelineView(const mtx::responses::Timeline &timeline,
@ -231,7 +196,7 @@ TimelineView::addBackwardsEvents(const mtx::responses::Messages &msgs)
isPaginationInProgress_ = false; isPaginationInProgress_ = false;
} }
TimelineItem * QWidget *
TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &event, TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &event,
TimelineDirection direction) TimelineDirection direction)
{ {
@ -255,6 +220,12 @@ TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &
}); });
return nullptr; return nullptr;
} else if (mpark::holds_alternative<StateEvent<state::Encryption>>(event)) {
auto msg = mpark::get<StateEvent<state::Encryption>>(event);
auto item = new InfoMessage(tr("Encryption is enabled"), this);
item->saveDatetime(QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts));
return item;
} else if (mpark::holds_alternative<RoomEvent<msg::Audio>>(event)) { } else if (mpark::holds_alternative<RoomEvent<msg::Audio>>(event)) {
auto audio = mpark::get<RoomEvent<msg::Audio>>(event); auto audio = mpark::get<RoomEvent<msg::Audio>>(event);
return processMessageEvent<AudioEvent, AudioItem>(audio, direction); return processMessageEvent<AudioEvent, AudioItem>(audio, direction);
@ -280,13 +251,18 @@ TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &
return processMessageEvent<Sticker, StickerItem>(mpark::get<Sticker>(event), return processMessageEvent<Sticker, StickerItem>(mpark::get<Sticker>(event),
direction); direction);
} else if (mpark::holds_alternative<EncryptedEvent<msg::Encrypted>>(event)) { } else if (mpark::holds_alternative<EncryptedEvent<msg::Encrypted>>(event)) {
auto res = parseEncryptedEvent(mpark::get<EncryptedEvent<msg::Encrypted>>(event)); auto res = parseEncryptedEvent(mpark::get<EncryptedEvent<msg::Encrypted>>(event));
auto item = parseMessageEvent(res.event, direction); auto widget = parseMessageEvent(res.event, direction);
if (item != nullptr && res.isDecrypted) if (widget == nullptr)
return nullptr;
auto item = qobject_cast<TimelineItem *>(widget);
if (item && res.isDecrypted)
item->markReceived(true); item->markReceived(true);
return item; return widget;
} }
return nullptr; return nullptr;
@ -374,7 +350,7 @@ TimelineView::renderBottomEvents(const std::vector<TimelineEvent> &events)
int counter = 0; int counter = 0;
for (const auto &event : events) { for (const auto &event : events) {
TimelineItem *item = parseMessageEvent(event, TimelineDirection::Bottom); QWidget *item = parseMessageEvent(event, TimelineDirection::Bottom);
if (item != nullptr) { if (item != nullptr) {
addTimelineItem(item, TimelineDirection::Bottom); addTimelineItem(item, TimelineDirection::Bottom);
@ -395,7 +371,7 @@ TimelineView::renderBottomEvents(const std::vector<TimelineEvent> &events)
void void
TimelineView::renderTopEvents(const std::vector<TimelineEvent> &events) TimelineView::renderTopEvents(const std::vector<TimelineEvent> &events)
{ {
std::vector<TimelineItem *> items; std::vector<QWidget *> items;
// Reset the sender of the first message in the timeline // Reset the sender of the first message in the timeline
// cause we're about to insert a new one. // cause we're about to insert a new one.
@ -408,7 +384,7 @@ TimelineView::renderTopEvents(const std::vector<TimelineEvent> &events)
while (ii != 0) { while (ii != 0) {
--ii; --ii;
TimelineItem *item = parseMessageEvent(events[ii], TimelineDirection::Top); auto item = parseMessageEvent(events[ii], TimelineDirection::Top);
if (item != nullptr) if (item != nullptr)
items.push_back(item); items.push_back(item);
@ -429,9 +405,16 @@ TimelineView::renderTopEvents(const std::vector<TimelineEvent> &events)
// If this batch is the first being rendered (i.e the first and the last // If this batch is the first being rendered (i.e the first and the last
// events originate from this batch), set the last sender. // events originate from this batch), set the last sender.
if (lastSender_.isEmpty() && !items.empty()) if (lastSender_.isEmpty() && !items.empty()) {
saveLastMessageInfo(items.at(0)->descriptionMessage().userid, for (const auto &w : items) {
items.at(0)->descriptionMessage().datetime); auto timelineItem = qobject_cast<TimelineItem *>(w);
if (timelineItem) {
saveLastMessageInfo(timelineItem->descriptionMessage().userid,
timelineItem->descriptionMessage().datetime);
break;
}
}
}
} }
void void
@ -569,17 +552,16 @@ TimelineView::isSenderRendered(const QString &user_id,
} }
void void
TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction) TimelineView::addTimelineItem(QWidget *item, TimelineDirection direction)
{ {
const auto newDate = item->descriptionMessage().datetime; const auto newDate = getDate(item);
if (direction == TimelineDirection::Bottom) { if (direction == TimelineDirection::Bottom) {
const auto lastItemPosition = scroll_layout_->count() - 1; const auto lastItemPosition = scroll_layout_->count() - 1;
auto lastItem = const auto lastItem = scroll_layout_->itemAt(lastItemPosition)->widget();
qobject_cast<TimelineItem *>(scroll_layout_->itemAt(lastItemPosition)->widget());
if (lastItem) { if (lastItem) {
auto oldDate = lastItem->descriptionMessage().datetime; const auto oldDate = getDate(lastItem);
if (oldDate.daysTo(newDate) != 0) { if (oldDate.daysTo(newDate) != 0) {
auto separator = new DateSeparator(newDate, this); auto separator = new DateSeparator(newDate, this);
@ -594,11 +576,10 @@ TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction)
// The first item (position 0) is a stretch widget that pushes // The first item (position 0) is a stretch widget that pushes
// the widgets to the bottom of the page. // the widgets to the bottom of the page.
if (scroll_layout_->count() > 1) { if (scroll_layout_->count() > 1) {
auto firstItem = const auto firstItem = scroll_layout_->itemAt(1)->widget();
qobject_cast<TimelineItem *>(scroll_layout_->itemAt(1)->widget());
if (firstItem) { if (firstItem) {
auto oldDate = firstItem->descriptionMessage().datetime; const auto oldDate = getDate(firstItem);
if (newDate.daysTo(oldDate) != 0) { if (newDate.daysTo(oldDate) != 0) {
auto separator = new DateSeparator(oldDate); auto separator = new DateSeparator(oldDate);

79
src/ui/InfoMessage.cpp Normal file
View File

@ -0,0 +1,79 @@
#include "Config.h"
#include "InfoMessage.hpp"
#include <QDateTime>
#include <QPainter>
#include <QPen>
constexpr int VPadding = 6;
constexpr int HPadding = 12;
constexpr int HMargin = 20;
InfoMessage::InfoMessage(QWidget *parent)
: QWidget{parent}
{
font_.setWeight(60);
font_.setPixelSize(conf::timeline::fonts::dateSeparator);
}
InfoMessage::InfoMessage(QString msg, QWidget *parent)
: QWidget{parent}
, msg_{msg}
{
font_.setWeight(60);
font_.setPixelSize(conf::timeline::fonts::dateSeparator);
QFontMetrics fm{font_};
width_ = fm.width(msg_) + HPadding * 2;
height_ = fm.ascent() + 2 * VPadding;
setFixedHeight(height_ + 2 * HMargin);
}
void
InfoMessage::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.setFont(font_);
// Center the box horizontally & vertically.
auto textRegion = QRectF(width() / 2 - width_ / 2, HMargin, width_, height_);
QPainterPath ppath;
ppath.addRoundedRect(textRegion, height_ / 2, height_ / 2);
p.setPen(Qt::NoPen);
p.fillPath(ppath, boxColor());
p.drawPath(ppath);
p.setPen(QPen(textColor()));
p.drawText(textRegion, Qt::AlignCenter, msg_);
}
DateSeparator::DateSeparator(QDateTime datetime, QWidget *parent)
: InfoMessage{parent}
{
auto now = QDateTime::currentDateTime();
auto days = now.daysTo(datetime);
QString fmt;
if (now.date().year() != datetime.date().year())
fmt = QString("ddd d MMMM yy");
else
fmt = QString("ddd d MMMM");
if (days == 0)
msg_ = tr("Today");
else if (std::abs(days) == 1)
msg_ = tr("Yesterday");
else
msg_ = datetime.toString(fmt);
QFontMetrics fm{font_};
width_ = fm.width(msg_) + HPadding * 2;
height_ = fm.ascent() + 2 * VPadding;
setFixedHeight(height_ + 2 * HMargin);
}