mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
Initial support for backwards pagination
This commit is contained in:
parent
ff611c1b39
commit
0368d854cf
10 changed files with 388 additions and 94 deletions
|
@ -91,6 +91,7 @@ set(SRC_FILES
|
|||
src/MatrixClient.cc
|
||||
src/Profile.cc
|
||||
src/RoomInfoListItem.cc
|
||||
src/RoomMessages.cc
|
||||
src/RoomList.cc
|
||||
src/RoomState.cc
|
||||
src/Register.cc
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QtNetwork/QNetworkAccessManager>
|
||||
|
||||
#include "Profile.h"
|
||||
#include "RoomMessages.h"
|
||||
#include "Sync.h"
|
||||
|
||||
/*
|
||||
|
@ -43,6 +44,7 @@ public:
|
|||
void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
|
||||
void fetchOwnAvatar(const QUrl &avatar_url);
|
||||
void downloadImage(const QString &event_id, const QUrl &url);
|
||||
void messages(const QString &room_id, const QString &from_token) noexcept;
|
||||
|
||||
inline QUrl getHomeServer();
|
||||
inline int transactionId();
|
||||
|
@ -77,19 +79,21 @@ signals:
|
|||
void syncCompleted(const SyncResponse &response);
|
||||
void syncFailed(const QString &msg);
|
||||
void messageSent(const QString &event_id, const QString &roomid, const int txn_id);
|
||||
void messagesRetrieved(const QString &room_id, const RoomMessages &msgs);
|
||||
|
||||
private slots:
|
||||
void onResponse(QNetworkReply *reply);
|
||||
|
||||
private:
|
||||
enum class Endpoint {
|
||||
GetOwnProfile,
|
||||
GetOwnAvatar,
|
||||
GetOwnProfile,
|
||||
GetProfile,
|
||||
Image,
|
||||
InitialSync,
|
||||
Login,
|
||||
Logout,
|
||||
Messages,
|
||||
Register,
|
||||
RoomAvatar,
|
||||
SendTextMessage,
|
||||
|
@ -109,6 +113,7 @@ private:
|
|||
void onSyncResponse(QNetworkReply *reply);
|
||||
void onRoomAvatarResponse(QNetworkReply *reply);
|
||||
void onImageResponse(QNetworkReply *reply);
|
||||
void onMessagesResponse(QNetworkReply *reply);
|
||||
|
||||
// Client API prefix.
|
||||
QString api_url_;
|
||||
|
|
56
include/RoomMessages.h
Normal file
56
include/RoomMessages.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ROOM_MESSAGES_H
|
||||
#define ROOM_MESSAGES_H
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "Deserializable.h"
|
||||
|
||||
class RoomMessages : public Deserializable
|
||||
{
|
||||
public:
|
||||
void deserialize(const QJsonDocument &data) override;
|
||||
|
||||
inline QString start() const;
|
||||
inline QString end() const;
|
||||
inline QJsonArray chunk() const;
|
||||
|
||||
private:
|
||||
QString start_;
|
||||
QString end_;
|
||||
QJsonArray chunk_;
|
||||
};
|
||||
|
||||
inline QString RoomMessages::start() const
|
||||
{
|
||||
return start_;
|
||||
}
|
||||
|
||||
inline QString RoomMessages::end() const
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
|
||||
inline QJsonArray RoomMessages::chunk() const
|
||||
{
|
||||
return chunk_;
|
||||
}
|
||||
|
||||
#endif // ROOM_MESSAGES_H
|
|
@ -51,32 +51,50 @@ struct PendingMessage {
|
|||
}
|
||||
};
|
||||
|
||||
// In which place new TimelineItems should be inserted.
|
||||
enum class TimelineDirection {
|
||||
Top,
|
||||
Bottom,
|
||||
};
|
||||
|
||||
class TimelineView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TimelineView(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
|
||||
TimelineView(const QJsonArray &events, QSharedPointer<MatrixClient> client, QWidget *parent = 0);
|
||||
~TimelineView();
|
||||
TimelineView(const Timeline &timeline, QSharedPointer<MatrixClient> client, const QString &room_id, QWidget *parent = 0);
|
||||
|
||||
void addHistoryItem(const events::MessageEvent<msgs::Image> &e, const QString &color, bool with_sender);
|
||||
void addHistoryItem(const events::MessageEvent<msgs::Notice> &e, const QString &color, bool with_sender);
|
||||
void addHistoryItem(const events::MessageEvent<msgs::Text> &e, const QString &color, bool with_sender);
|
||||
TimelineItem *createTimelineItem(const events::MessageEvent<msgs::Image> &e, const QString &color, bool with_sender);
|
||||
TimelineItem *createTimelineItem(const events::MessageEvent<msgs::Notice> &e, const QString &color, bool with_sender);
|
||||
TimelineItem *createTimelineItem(const events::MessageEvent<msgs::Text> &e, const QString &color, bool with_sender);
|
||||
|
||||
int addEvents(const QJsonArray &events);
|
||||
// Add new events at the end of the timeline.
|
||||
int addEvents(const Timeline &timeline);
|
||||
void addUserTextMessage(const QString &msg, int txn_id);
|
||||
void updatePendingMessage(int txn_id, QString event_id);
|
||||
void scrollDown();
|
||||
void clear();
|
||||
|
||||
public slots:
|
||||
void sliderRangeChanged(int min, int max);
|
||||
void sliderMoved(int position);
|
||||
|
||||
// Add old events at the top of the timeline.
|
||||
void addBackwardsEvents(const QString &room_id, const RoomMessages &msgs);
|
||||
|
||||
private:
|
||||
void init();
|
||||
void removePendingMessage(const events::MessageEvent<msgs::Text> &e);
|
||||
void addTimelineItem(TimelineItem *item, TimelineDirection direction);
|
||||
void updateLastSender(const QString &user_id, TimelineDirection direction);
|
||||
|
||||
// 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 isPendingMessage(const events::MessageEvent<msgs::Text> &e, const QString &userid);
|
||||
|
||||
// Return nullptr if the event couldn't be parsed.
|
||||
TimelineItem *parseMessageEvent(const QJsonObject &event, TimelineDirection direction);
|
||||
|
||||
QVBoxLayout *top_layout_;
|
||||
QVBoxLayout *scroll_layout_;
|
||||
|
||||
|
@ -84,6 +102,19 @@ private:
|
|||
QWidget *scroll_widget_;
|
||||
|
||||
QString last_sender_;
|
||||
QString last_sender_backwards_;
|
||||
QString room_id_;
|
||||
QString prev_batch_token_;
|
||||
QString local_user_;
|
||||
|
||||
bool isPaginationInProgress_ = false;
|
||||
bool isInitialized = false;
|
||||
bool isTimelineFinished = false;
|
||||
|
||||
const int SCROLL_BAR_GAP = 300;
|
||||
|
||||
int scroll_height_ = 0;
|
||||
int previous_max_height_ = 0;
|
||||
|
||||
QList<PendingMessage> pending_msgs_;
|
||||
QSharedPointer<MatrixClient> client_;
|
||||
|
|
|
@ -108,6 +108,7 @@ void MainWindow::showChatPage(QString userid, QString homeserver, QString token)
|
|||
if (progress_modal_ == nullptr) {
|
||||
progress_modal_ = new OverlayModal(this, spinner_);
|
||||
progress_modal_->fadeIn();
|
||||
progress_modal_->setDuration(300);
|
||||
}
|
||||
|
||||
login_page_->reset();
|
||||
|
|
|
@ -333,6 +333,32 @@ void MatrixClient::onImageResponse(QNetworkReply *reply)
|
|||
emit imageDownloaded(event_id, pixmap);
|
||||
}
|
||||
|
||||
void MatrixClient::onMessagesResponse(QNetworkReply *reply)
|
||||
{
|
||||
reply->deleteLater();
|
||||
|
||||
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
if (status == 0 || status >= 400) {
|
||||
qWarning() << reply->errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = reply->readAll();
|
||||
auto room_id = reply->property("room_id").toString();
|
||||
|
||||
RoomMessages msgs;
|
||||
|
||||
try {
|
||||
msgs.deserialize(QJsonDocument::fromJson(data));
|
||||
} catch (const DeserializationException &e) {
|
||||
qWarning() << "Room messages from" << room_id << e.what();
|
||||
return;
|
||||
}
|
||||
|
||||
emit messagesRetrieved(room_id, msgs);
|
||||
}
|
||||
|
||||
void MatrixClient::onResponse(QNetworkReply *reply)
|
||||
{
|
||||
switch (static_cast<Endpoint>(reply->property("endpoint").toInt())) {
|
||||
|
@ -369,6 +395,9 @@ void MatrixClient::onResponse(QNetworkReply *reply)
|
|||
case Endpoint::GetOwnAvatar:
|
||||
onGetOwnAvatarResponse(reply);
|
||||
break;
|
||||
case Endpoint::Messages:
|
||||
onMessagesResponse(reply);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -581,3 +610,21 @@ void MatrixClient::fetchOwnAvatar(const QUrl &avatar_url)
|
|||
QNetworkReply *reply = get(avatar_request);
|
||||
reply->setProperty("endpoint", static_cast<int>(Endpoint::GetOwnAvatar));
|
||||
}
|
||||
|
||||
void MatrixClient::messages(const QString &room_id, const QString &from_token) noexcept
|
||||
{
|
||||
QUrlQuery query;
|
||||
query.addQueryItem("access_token", token_);
|
||||
query.addQueryItem("from", from_token);
|
||||
query.addQueryItem("dir", "b");
|
||||
|
||||
QUrl endpoint(server_);
|
||||
endpoint.setPath(api_url_ + QString("/rooms/%1/messages").arg(room_id));
|
||||
endpoint.setQuery(query);
|
||||
|
||||
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||
|
||||
QNetworkReply *reply = get(request);
|
||||
reply->setProperty("endpoint", static_cast<int>(Endpoint::Messages));
|
||||
reply->setProperty("room_id", room_id);
|
||||
}
|
||||
|
|
42
src/RoomMessages.cc
Normal file
42
src/RoomMessages.cc
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "RoomMessages.h"
|
||||
|
||||
void RoomMessages::deserialize(const QJsonDocument &data)
|
||||
{
|
||||
if (!data.isObject())
|
||||
throw DeserializationException("response is not a JSON object");
|
||||
|
||||
QJsonObject object = data.object();
|
||||
|
||||
if (!object.contains("start"))
|
||||
throw DeserializationException("start key is missing");
|
||||
|
||||
if (!object.contains("end"))
|
||||
throw DeserializationException("end key is missing");
|
||||
|
||||
if (!object.contains("chunk"))
|
||||
throw DeserializationException("chunk key is missing");
|
||||
|
||||
if (!object.value("chunk").isArray())
|
||||
throw DeserializationException("chunk isn't a JSON array");
|
||||
|
||||
start_ = object.value("start").toString();
|
||||
end_ = object.value("end").toString();
|
||||
chunk_ = object.value("chunk").toArray();
|
||||
}
|
|
@ -34,19 +34,19 @@
|
|||
namespace events = matrix::events;
|
||||
namespace msgs = matrix::events::messages;
|
||||
|
||||
TimelineView::TimelineView(const QJsonArray &events, QSharedPointer<MatrixClient> client, QWidget *parent)
|
||||
TimelineView::TimelineView(const Timeline &timeline,
|
||||
QSharedPointer<MatrixClient> client,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, room_id_{room_id}
|
||||
, client_{client}
|
||||
{
|
||||
init();
|
||||
addEvents(events);
|
||||
}
|
||||
QSettings settings;
|
||||
local_user_ = settings.value("auth/user_id").toString();
|
||||
|
||||
TimelineView::TimelineView(QSharedPointer<MatrixClient> client, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, client_{client}
|
||||
{
|
||||
init();
|
||||
addEvents(timeline);
|
||||
}
|
||||
|
||||
void TimelineView::clear()
|
||||
|
@ -58,83 +58,175 @@ void TimelineView::clear()
|
|||
void TimelineView::sliderRangeChanged(int min, int max)
|
||||
{
|
||||
Q_UNUSED(min);
|
||||
scroll_area_->verticalScrollBar()->setValue(max);
|
||||
|
||||
if (!scroll_area_->verticalScrollBar()->isVisible())
|
||||
return;
|
||||
|
||||
if (max - scroll_area_->verticalScrollBar()->value() < SCROLL_BAR_GAP)
|
||||
scroll_area_->verticalScrollBar()->setValue(max);
|
||||
}
|
||||
|
||||
int TimelineView::addEvents(const QJsonArray &events)
|
||||
void TimelineView::scrollDown()
|
||||
{
|
||||
QSettings settings;
|
||||
auto local_user = settings.value("auth/user_id").toString();
|
||||
int current = scroll_area_->verticalScrollBar()->value();
|
||||
int max = scroll_area_->verticalScrollBar()->maximum();
|
||||
|
||||
int message_count = 0;
|
||||
events::EventType ty;
|
||||
// The first time we enter the room move the scroll bar to the bottom.
|
||||
if (!isInitialized) {
|
||||
scroll_area_->ensureVisible(0, scroll_widget_->size().height(), 0, 0);
|
||||
isInitialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &event : events) {
|
||||
ty = events::extractEventType(event.toObject());
|
||||
// If the gap is small enough move the scroll bar down. e.g when a new message appears.
|
||||
if (max - current < SCROLL_BAR_GAP)
|
||||
scroll_area_->verticalScrollBar()->setValue(max);
|
||||
}
|
||||
|
||||
if (ty == events::EventType::RoomMessage) {
|
||||
events::MessageEventType msg_type = events::extractMessageEventType(event.toObject());
|
||||
void TimelineView::sliderMoved(int position)
|
||||
{
|
||||
if (!scroll_area_->verticalScrollBar()->isVisible())
|
||||
return;
|
||||
|
||||
if (msg_type == events::MessageEventType::Text) {
|
||||
events::MessageEvent<msgs::Text> text;
|
||||
// The scrollbar is high enough so we can start retrieving old events.
|
||||
if (position < SCROLL_BAR_GAP) {
|
||||
if (isTimelineFinished)
|
||||
return;
|
||||
|
||||
try {
|
||||
text.deserialize(event.toObject());
|
||||
} catch (const DeserializationException &e) {
|
||||
qWarning() << e.what() << event;
|
||||
continue;
|
||||
}
|
||||
// Prevent user from moving up when there is pagination in progress.
|
||||
if (isPaginationInProgress_) {
|
||||
scroll_area_->verticalScrollBar()->setValue(SCROLL_BAR_GAP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPendingMessage(text, local_user)) {
|
||||
removePendingMessage(text);
|
||||
continue;
|
||||
}
|
||||
isPaginationInProgress_ = true;
|
||||
scroll_height_ = scroll_area_->verticalScrollBar()->value();
|
||||
previous_max_height_ = scroll_area_->verticalScrollBar()->maximum();
|
||||
|
||||
auto with_sender = last_sender_ != text.sender();
|
||||
auto color = TimelineViewManager::getUserColor(text.sender());
|
||||
// FIXME: Maybe move this to TimelineViewManager to remove the extra calls?
|
||||
client_.data()->messages(room_id_, prev_batch_token_);
|
||||
}
|
||||
}
|
||||
|
||||
addHistoryItem(text, color, with_sender);
|
||||
last_sender_ = text.sender();
|
||||
void TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msgs)
|
||||
{
|
||||
if (room_id_ != room_id)
|
||||
return;
|
||||
|
||||
message_count += 1;
|
||||
} else if (msg_type == events::MessageEventType::Notice) {
|
||||
events::MessageEvent<msgs::Notice> notice;
|
||||
if (msgs.chunk().count() == 0) {
|
||||
isTimelineFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
notice.deserialize(event.toObject());
|
||||
} catch (const DeserializationException &e) {
|
||||
qWarning() << e.what() << event;
|
||||
continue;
|
||||
}
|
||||
isTimelineFinished = false;
|
||||
last_sender_backwards_.clear();
|
||||
QList<TimelineItem *> items;
|
||||
|
||||
auto with_sender = last_sender_ != notice.sender();
|
||||
auto color = TimelineViewManager::getUserColor(notice.sender());
|
||||
// Parse in reverse order to determine where we should not show sender's name.
|
||||
auto it = msgs.chunk().constEnd();
|
||||
while (it != msgs.chunk().constBegin()) {
|
||||
--it;
|
||||
|
||||
addHistoryItem(notice, color, with_sender);
|
||||
last_sender_ = notice.sender();
|
||||
TimelineItem *item = parseMessageEvent((*it).toObject(), TimelineDirection::Top);
|
||||
|
||||
message_count += 1;
|
||||
} else if (msg_type == events::MessageEventType::Image) {
|
||||
events::MessageEvent<msgs::Image> img;
|
||||
if (item != nullptr)
|
||||
items.push_back(item);
|
||||
}
|
||||
|
||||
try {
|
||||
img.deserialize(event.toObject());
|
||||
} catch (const DeserializationException &e) {
|
||||
qWarning() << e.what() << event;
|
||||
continue;
|
||||
}
|
||||
// Reverse again to render them.
|
||||
std::reverse(items.begin(), items.end());
|
||||
|
||||
auto with_sender = last_sender_ != img.sender();
|
||||
auto color = TimelineViewManager::getUserColor(img.sender());
|
||||
for (const auto &item : items)
|
||||
addTimelineItem(item, TimelineDirection::Top);
|
||||
|
||||
addHistoryItem(img, color, with_sender);
|
||||
prev_batch_token_ = msgs.end();
|
||||
isPaginationInProgress_ = false;
|
||||
}
|
||||
|
||||
last_sender_ = img.sender();
|
||||
message_count += 1;
|
||||
} else if (msg_type == events::MessageEventType::Unknown) {
|
||||
qWarning() << "Unknown message type" << event.toObject();
|
||||
continue;
|
||||
TimelineItem *TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection direction)
|
||||
{
|
||||
events::EventType ty = events::extractEventType(event);
|
||||
|
||||
if (ty == events::EventType::RoomMessage) {
|
||||
events::MessageEventType msg_type = events::extractMessageEventType(event);
|
||||
|
||||
if (msg_type == events::MessageEventType::Text) {
|
||||
events::MessageEvent<msgs::Text> text;
|
||||
|
||||
try {
|
||||
text.deserialize(event);
|
||||
} catch (const DeserializationException &e) {
|
||||
qWarning() << e.what() << event;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (isPendingMessage(text, local_user_)) {
|
||||
removePendingMessage(text);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto with_sender = isSenderRendered(text.sender(), direction);
|
||||
updateLastSender(text.sender(), direction);
|
||||
|
||||
auto color = TimelineViewManager::getUserColor(text.sender());
|
||||
last_sender_ = text.sender();
|
||||
|
||||
return createTimelineItem(text, color, with_sender);
|
||||
} else if (msg_type == events::MessageEventType::Notice) {
|
||||
events::MessageEvent<msgs::Notice> notice;
|
||||
|
||||
try {
|
||||
notice.deserialize(event);
|
||||
} catch (const DeserializationException &e) {
|
||||
qWarning() << e.what() << event;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto with_sender = isSenderRendered(notice.sender(), direction);
|
||||
updateLastSender(notice.sender(), direction);
|
||||
|
||||
auto color = TimelineViewManager::getUserColor(notice.sender());
|
||||
last_sender_ = notice.sender();
|
||||
|
||||
return createTimelineItem(notice, color, with_sender);
|
||||
} else if (msg_type == events::MessageEventType::Image) {
|
||||
events::MessageEvent<msgs::Image> img;
|
||||
|
||||
try {
|
||||
img.deserialize(event);
|
||||
} catch (const DeserializationException &e) {
|
||||
qWarning() << e.what() << event;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto with_sender = isSenderRendered(img.sender(), direction);
|
||||
updateLastSender(img.sender(), direction);
|
||||
|
||||
auto color = TimelineViewManager::getUserColor(img.sender());
|
||||
last_sender_ = img.sender();
|
||||
|
||||
return createTimelineItem(img, color, with_sender);
|
||||
} else if (msg_type == events::MessageEventType::Unknown) {
|
||||
qWarning() << "Unknown message type" << event;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int TimelineView::addEvents(const Timeline &timeline)
|
||||
{
|
||||
int message_count = 0;
|
||||
|
||||
prev_batch_token_ = timeline.previousBatch();
|
||||
|
||||
for (const auto &event : timeline.events()) {
|
||||
TimelineItem *item = parseMessageEvent(event.toObject(), TimelineDirection::Bottom);
|
||||
|
||||
if (item != nullptr) {
|
||||
message_count += 1;
|
||||
addTimelineItem(item, TimelineDirection::Bottom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,35 +257,59 @@ void TimelineView::init()
|
|||
|
||||
setLayout(top_layout_);
|
||||
|
||||
connect(scroll_area_->verticalScrollBar(),
|
||||
SIGNAL(rangeChanged(int, int)),
|
||||
this,
|
||||
SLOT(sliderRangeChanged(int, int)));
|
||||
connect(client_.data(), &MatrixClient::messagesRetrieved, this, &TimelineView::addBackwardsEvents);
|
||||
|
||||
connect(scroll_area_->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(sliderMoved(int)));
|
||||
connect(scroll_area_->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(sliderRangeChanged(int, int)));
|
||||
}
|
||||
|
||||
void TimelineView::addHistoryItem(const events::MessageEvent<msgs::Image> &event, const QString &color, bool with_sender)
|
||||
void TimelineView::updateLastSender(const QString &user_id, TimelineDirection direction)
|
||||
{
|
||||
if (direction == TimelineDirection::Bottom)
|
||||
last_sender_ = user_id;
|
||||
else
|
||||
last_sender_backwards_ = user_id;
|
||||
}
|
||||
|
||||
bool TimelineView::isSenderRendered(const QString &user_id, TimelineDirection direction)
|
||||
{
|
||||
if (direction == TimelineDirection::Bottom)
|
||||
return last_sender_ != user_id;
|
||||
else
|
||||
return last_sender_backwards_ != user_id;
|
||||
}
|
||||
|
||||
TimelineItem *TimelineView::createTimelineItem(const events::MessageEvent<msgs::Image> &event, const QString &color, bool with_sender)
|
||||
{
|
||||
auto image = new ImageItem(client_, event);
|
||||
|
||||
if (with_sender) {
|
||||
auto item = new TimelineItem(image, event, color, scroll_widget_);
|
||||
scroll_layout_->addWidget(item);
|
||||
} else {
|
||||
auto item = new TimelineItem(image, event, scroll_widget_);
|
||||
scroll_layout_->addWidget(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
auto item = new TimelineItem(image, event, scroll_widget_);
|
||||
return item;
|
||||
}
|
||||
|
||||
void TimelineView::addHistoryItem(const events::MessageEvent<msgs::Notice> &event, const QString &color, bool with_sender)
|
||||
TimelineItem *TimelineView::createTimelineItem(const events::MessageEvent<msgs::Notice> &event, const QString &color, bool with_sender)
|
||||
{
|
||||
TimelineItem *item = new TimelineItem(event, with_sender, color, scroll_widget_);
|
||||
scroll_layout_->addWidget(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
void TimelineView::addHistoryItem(const events::MessageEvent<msgs::Text> &event, const QString &color, bool with_sender)
|
||||
TimelineItem *TimelineView::createTimelineItem(const events::MessageEvent<msgs::Text> &event, const QString &color, bool with_sender)
|
||||
{
|
||||
TimelineItem *item = new TimelineItem(event, with_sender, color, scroll_widget_);
|
||||
scroll_layout_->addWidget(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
void TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction)
|
||||
{
|
||||
if (direction == TimelineDirection::Bottom)
|
||||
scroll_layout_->addWidget(item);
|
||||
else
|
||||
scroll_layout_->insertWidget(0, item);
|
||||
}
|
||||
|
||||
void TimelineView::updatePendingMessage(int txn_id, QString event_id)
|
||||
|
@ -254,7 +370,3 @@ void TimelineView::addUserTextMessage(const QString &body, int txn_id)
|
|||
|
||||
pending_msgs_.push_back(message);
|
||||
}
|
||||
|
||||
TimelineView::~TimelineView()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -78,10 +78,9 @@ void TimelineViewManager::initialize(const Rooms &rooms)
|
|||
{
|
||||
for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); it++) {
|
||||
auto roomid = it.key();
|
||||
auto events = it.value().timeline().events();
|
||||
|
||||
// Create a history view with the room events.
|
||||
TimelineView *view = new TimelineView(events, client_);
|
||||
TimelineView *view = new TimelineView(it.value().timeline(), client_, it.key());
|
||||
views_.insert(it.key(), view);
|
||||
|
||||
// Add the view in the widget stack.
|
||||
|
@ -100,9 +99,8 @@ void TimelineViewManager::sync(const Rooms &rooms)
|
|||
}
|
||||
|
||||
auto view = views_.value(roomid);
|
||||
auto events = it.value().timeline().events();
|
||||
|
||||
int msgs_added = view->addEvents(events);
|
||||
int msgs_added = view->addEvents(it.value().timeline());
|
||||
|
||||
if (msgs_added > 0) {
|
||||
// TODO: When the app window gets active the current
|
||||
|
@ -124,6 +122,7 @@ void TimelineViewManager::setHistoryView(const QString &room_id)
|
|||
|
||||
active_room_ = room_id;
|
||||
auto widget = views_.value(room_id);
|
||||
widget->scrollDown();
|
||||
|
||||
setCurrentWidget(widget);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
app.setStyleSheet(
|
||||
"QScrollBar:vertical { background-color: #f8fbfe; width: 8px; border: none; margin: 2px; }"
|
||||
"QScrollBar::handle:vertical { background-color : #d6dde3; }"
|
||||
"QScrollBar::handle:vertical { min-height: 40px; background-color : #d6dde3; }"
|
||||
"QScrollBar::add-line:vertical { border: none; background: none; }"
|
||||
"QScrollBar::sub-line:vertical { border: none; background: none; }");
|
||||
|
||||
|
|
Loading…
Reference in a new issue