Display the most recent message instead of the topic

closes #11
This commit is contained in:
Konstantinos Sideris 2017-08-06 18:53:31 +03:00
parent 245fdb1917
commit 146aaa8746
11 changed files with 139 additions and 7 deletions

View file

@ -26,6 +26,12 @@
#include "RoomSettings.h" #include "RoomSettings.h"
#include "RoomState.h" #include "RoomState.h"
struct DescInfo {
QString username;
QString body;
QString timestamp;
};
class RoomInfoListItem : public QWidget class RoomInfoListItem : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -46,6 +52,7 @@ public:
inline RoomState state() const; inline RoomState state() const;
inline void setAvatar(const QImage &avatar_image); inline void setAvatar(const QImage &avatar_image);
inline int unreadMessageCount() const; inline int unreadMessageCount() const;
inline void setDescriptionMessage(const DescInfo &info);
signals: signals:
void clicked(const QString &room_id); void clicked(const QString &room_id);
@ -71,8 +78,8 @@ private:
QString roomId_; QString roomId_;
QString roomName_; QString roomName_;
QString lastMessage_;
QString lastTimestamp_; DescInfo lastMsgInfo_;
QPixmap roomAvatar_; QPixmap roomAvatar_;
@ -107,3 +114,8 @@ inline void RoomInfoListItem::setAvatar(const QImage &img)
roomAvatar_ = QPixmap::fromImage(img.scaled(IconSize, IconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); roomAvatar_ = QPixmap::fromImage(img.scaled(IconSize, IconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
update(); update();
} }
inline void RoomInfoListItem::setDescriptionMessage(const DescInfo &info)
{
lastMsgInfo_ = info;
}

View file

@ -49,6 +49,7 @@ public slots:
void updateRoomAvatar(const QString &roomid, const QPixmap &img); void updateRoomAvatar(const QString &roomid, const QPixmap &img);
void highlightSelectedRoom(const QString &room_id); void highlightSelectedRoom(const QString &room_id);
void updateUnreadMessageCount(const QString &roomid, int count); void updateUnreadMessageCount(const QString &roomid, int count);
void updateRoomDescription(const QString &roomid, const DescInfo &info);
private: private:
void calculateUnreadMessageCount(); void calculateUnreadMessageCount();

View file

@ -28,6 +28,7 @@
#include "Image.h" #include "Image.h"
#include "MessageEvent.h" #include "MessageEvent.h"
#include "Notice.h" #include "Notice.h"
#include "RoomInfoListItem.h"
#include "Text.h" #include "Text.h"
namespace events = matrix::events; namespace events = matrix::events;
@ -48,6 +49,7 @@ public:
TimelineItem(ImageItem *img, const events::MessageEvent<msgs::Image> &e, QWidget *parent); TimelineItem(ImageItem *img, const events::MessageEvent<msgs::Image> &e, QWidget *parent);
void setUserAvatar(const QImage &pixmap); void setUserAvatar(const QImage &pixmap);
inline DescInfo descriptionMessage() const;
~TimelineItem(); ~TimelineItem();
@ -57,12 +59,15 @@ private:
void generateBody(const QString &body); void generateBody(const QString &body);
void generateBody(const QString &userid, const QString &color, const QString &body); void generateBody(const QString &userid, const QString &color, const QString &body);
void generateTimestamp(const QDateTime &time); void generateTimestamp(const QDateTime &time);
QString descriptiveTime(const QDateTime &then);
void setupAvatarLayout(const QString &userName); void setupAvatarLayout(const QString &userName);
void setupSimpleLayout(); void setupSimpleLayout();
QString replaceEmoji(const QString &body); QString replaceEmoji(const QString &body);
DescInfo descriptionMsg_;
QHBoxLayout *topLayout_; QHBoxLayout *topLayout_;
QVBoxLayout *sideLayout_; // Avatar or Timestamp QVBoxLayout *sideLayout_; // Avatar or Timestamp
QVBoxLayout *mainLayout_; // Header & Message body QVBoxLayout *mainLayout_; // Header & Message body
@ -77,3 +82,8 @@ private:
QLabel *userName_; QLabel *userName_;
QLabel *body_; QLabel *body_;
}; };
inline DescInfo TimelineItem::descriptionMessage() const
{
return descriptionMsg_;
}

View file

@ -29,6 +29,7 @@
#include "Image.h" #include "Image.h"
#include "Notice.h" #include "Notice.h"
#include "RoomInfoListItem.h"
#include "Text.h" #include "Text.h"
namespace msgs = matrix::events::messages; namespace msgs = matrix::events::messages;
@ -83,11 +84,15 @@ public slots:
// Add old events at the top of the timeline. // Add old events at the top of the timeline.
void addBackwardsEvents(const QString &room_id, const RoomMessages &msgs); void addBackwardsEvents(const QString &room_id, const RoomMessages &msgs);
signals:
void updateLastTimelineMessage(const QString &user, const DescInfo &info);
private: private:
void init(); void init();
void removePendingMessage(const events::MessageEvent<msgs::Text> &e); void removePendingMessage(const events::MessageEvent<msgs::Text> &e);
void addTimelineItem(TimelineItem *item, TimelineDirection direction); void addTimelineItem(TimelineItem *item, TimelineDirection direction);
void updateLastSender(const QString &user_id, TimelineDirection direction); void updateLastSender(const QString &user_id, TimelineDirection direction);
void notifyForLastEvent();
// Used to determine whether or not we should prefix a message with the sender's name. // Used to determine whether or not we should prefix a message with the sender's name.
bool isSenderRendered(const QString &user_id, TimelineDirection direction); bool isSenderRendered(const QString &user_id, TimelineDirection direction);

View file

@ -23,6 +23,7 @@
#include <QWidget> #include <QWidget>
#include "MatrixClient.h" #include "MatrixClient.h"
#include "RoomInfoListItem.h"
#include "Sync.h" #include "Sync.h"
#include "TimelineView.h" #include "TimelineView.h"
@ -50,6 +51,7 @@ public:
signals: signals:
void unreadMessages(QString roomid, int count); void unreadMessages(QString roomid, int count);
void updateRoomsLastMessage(const QString &user, const DescInfo &info);
public slots: public slots:
void setHistoryView(const QString &room_id); void setHistoryView(const QString &room_id);

View file

@ -138,6 +138,11 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
room_list_->updateUnreadMessageCount(roomid, count); room_list_->updateUnreadMessageCount(roomid, count);
}); });
connect(view_manager_,
&TimelineViewManager::updateRoomsLastMessage,
room_list_,
&RoomList::updateRoomDescription);
connect(room_list_, connect(room_list_,
SIGNAL(totalUnreadMessageCountUpdated(int)), SIGNAL(totalUnreadMessageCountUpdated(int)),
this, this,

View file

@ -135,10 +135,34 @@ void RoomInfoListItem::paintEvent(QPaintEvent *event)
font.setPixelSize(conf::fontSize); font.setPixelSize(conf::fontSize);
p.setFont(font); p.setFont(font);
auto description = metrics.elidedText(state_.getTopic(), Qt::ElideRight, width() * descPercentage - 2 * Padding - IconSize); auto msgStampWidth = QFontMetrics(font).width(lastMsgInfo_.timestamp) + 5;
p.drawText(QPoint(2 * Padding + IconSize, bottom_y), description);
// The limit is the space between the end of the avatar and the start of the timestamp.
int usernameLimit = std::max(0, width() - 3 * Padding - msgStampWidth - IconSize - 20);
auto userName = metrics.elidedText(lastMsgInfo_.username, Qt::ElideRight, usernameLimit);
font.setBold(true);
p.setFont(font);
p.drawText(QPoint(2 * Padding + IconSize, bottom_y), userName);
int nameWidth = QFontMetrics(font).width(userName);
font.setBold(false);
p.setFont(font);
// The limit is the space between the end of the username and the start of the timestamp.
int descriptionLimit = std::max(0, width() - 3 * Padding - msgStampWidth - IconSize - nameWidth - 5);
auto description = metrics.elidedText(lastMsgInfo_.body, Qt::ElideRight, descriptionLimit);
p.drawText(QPoint(2 * Padding + IconSize + nameWidth, bottom_y), description);
// We either show the bubble or the last message timestamp.
if (unreadMsgCount_ == 0) {
font.setBold(true);
p.drawText(QPoint(width() - Padding - msgStampWidth, bottom_y), lastMsgInfo_.timestamp);
}
} }
font.setBold(false);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
// We using the first letter of room's name. // We using the first letter of room's name.

View file

@ -174,10 +174,19 @@ void RoomList::highlightSelectedRoom(const QString &room_id)
void RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img) void RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img)
{ {
if (!rooms_.contains(roomid)) { if (!rooms_.contains(roomid)) {
qDebug() << "Avatar update on non existent room" << roomid; qWarning() << "Avatar update on non existent room" << roomid;
return; return;
} }
auto list_item = rooms_.value(roomid); rooms_.value(roomid)->setAvatar(img.toImage());
list_item->setAvatar(img.toImage()); }
void RoomList::updateRoomDescription(const QString &roomid, const DescInfo &info)
{
if (!rooms_.contains(roomid)) {
qWarning() << "Description update on non existent room" << roomid << info.body;
return;
}
rooms_.value(roomid)->setDescriptionMessage(info);
} }

View file

@ -72,6 +72,7 @@ TimelineItem::TimelineItem(const QString &userid, const QString &color, QString
: QWidget(parent) : QWidget(parent)
{ {
init(); init();
descriptionMsg_ = {"You: ", body, descriptiveTime(QDateTime::currentDateTime())};
body.replace(URL_REGEX, URL_HTML); body.replace(URL_REGEX, URL_HTML);
auto displayName = TimelineViewManager::displayName(userid); auto displayName = TimelineViewManager::displayName(userid);
@ -94,6 +95,7 @@ TimelineItem::TimelineItem(QString body, QWidget *parent)
: QWidget(parent) : QWidget(parent)
{ {
init(); init();
descriptionMsg_ = {"You: ", body, descriptiveTime(QDateTime::currentDateTime())};
body.replace(URL_REGEX, URL_HTML); body.replace(URL_REGEX, URL_HTML);
@ -119,6 +121,10 @@ TimelineItem::TimelineItem(ImageItem *image,
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp());
auto displayName = TimelineViewManager::displayName(event.sender()); auto displayName = TimelineViewManager::displayName(event.sender());
descriptionMsg_ = {displayName,
" sent an image",
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))};
generateTimestamp(timestamp); generateTimestamp(timestamp);
generateBody(displayName, color, ""); generateBody(displayName, color, "");
@ -141,6 +147,9 @@ TimelineItem::TimelineItem(ImageItem *image, const events::MessageEvent<msgs::Im
: QWidget(parent) : QWidget(parent)
{ {
init(); init();
descriptionMsg_ = {TimelineViewManager::displayName(event.sender()),
" sent an image",
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))};
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp());
generateTimestamp(timestamp); generateTimestamp(timestamp);
@ -162,6 +171,10 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Notice> &event, bool
: QWidget(parent) : QWidget(parent)
{ {
init(); init();
descriptionMsg_ = {
TimelineViewManager::displayName(event.sender()),
" sent a notification",
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))};
auto body = event.content().body().trimmed().toHtmlEscaped(); auto body = event.content().body().trimmed().toHtmlEscaped();
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp());
@ -199,6 +212,11 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Text> &event, bool w
auto body = event.content().body().trimmed().toHtmlEscaped(); auto body = event.content().body().trimmed().toHtmlEscaped();
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp());
descriptionMsg_ = {
TimelineViewManager::displayName(event.sender()),
QString(": %1").arg(body),
descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))};
generateTimestamp(timestamp); generateTimestamp(timestamp);
body.replace(URL_REGEX, URL_HTML); body.replace(URL_REGEX, URL_HTML);
@ -352,6 +370,23 @@ void TimelineItem::setUserAvatar(const QImage &avatar)
userAvatar_->setImage(avatar); userAvatar_->setImage(avatar);
} }
QString TimelineItem::descriptiveTime(const QDateTime &then)
{
auto now = QDateTime::currentDateTime();
auto days = then.daysTo(now);
if (days == 0) {
return then.toString("HH:mm");
} else if (days < 2) {
return QString("Yesterday");
} else if (days < 365) {
return then.toString("dd/MM");
}
return then.toString("dd/MM/yy");
}
TimelineItem::~TimelineItem() TimelineItem::~TimelineItem()
{ {
} }

View file

@ -179,6 +179,10 @@ void TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages
prev_batch_token_ = msgs.end(); prev_batch_token_ = msgs.end();
isPaginationInProgress_ = false; isPaginationInProgress_ = false;
isPaginationScrollPending_ = true; isPaginationScrollPending_ = true;
// Exclude the top stretch.
if (!msgs.chunk().isEmpty() && scroll_layout_->count() > 1)
notifyForLastEvent();
} }
TimelineItem *TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection direction) TimelineItem *TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection direction)
@ -295,6 +299,10 @@ int TimelineView::addEvents(const Timeline &timeline)
client_->messages(room_id_, prev_batch_token_); client_->messages(room_id_, prev_batch_token_);
} }
// Exclude the top stretch.
if (!timeline.events().isEmpty() && scroll_layout_->count() > 1)
notifyForLastEvent();
return message_count; return message_count;
} }
@ -441,3 +449,14 @@ void TimelineView::addUserTextMessage(const QString &body, int txn_id)
pending_msgs_.push_back(message); pending_msgs_.push_back(message);
} }
void TimelineView::notifyForLastEvent()
{
auto lastItem = scroll_layout_->itemAt(scroll_layout_->count() - 1);
auto *lastTimelineItem = qobject_cast<TimelineItem *>(lastItem->widget());
if (lastTimelineItem)
emit updateLastTimelineMessage(room_id_, lastTimelineItem->descriptionMessage());
else
qWarning() << "Cast to TimelineView failed" << room_id_;
}

View file

@ -80,6 +80,11 @@ void TimelineViewManager::initialize(const Rooms &rooms)
TimelineView *view = new TimelineView(it.value().timeline(), client_, it.key()); TimelineView *view = new TimelineView(it.value().timeline(), client_, it.key());
views_.insert(it.key(), QSharedPointer<TimelineView>(view)); views_.insert(it.key(), QSharedPointer<TimelineView>(view));
connect(view,
&TimelineView::updateLastTimelineMessage,
this,
&TimelineViewManager::updateRoomsLastMessage);
// Add the view in the widget stack. // Add the view in the widget stack.
addWidget(view); addWidget(view);
} }
@ -92,6 +97,11 @@ void TimelineViewManager::initialize(const QList<QString> &rooms)
TimelineView *view = new TimelineView(client_, roomid); TimelineView *view = new TimelineView(client_, roomid);
views_.insert(roomid, QSharedPointer<TimelineView>(view)); views_.insert(roomid, QSharedPointer<TimelineView>(view));
connect(view,
&TimelineView::updateLastTimelineMessage,
this,
&TimelineViewManager::updateRoomsLastMessage);
// Add the view in the widget stack. // Add the view in the widget stack.
addWidget(view); addWidget(view);
} }