summary refs log tree commit diff
path: root/src/TimelineView.cc
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2017-05-12 15:43:35 +0300
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2017-05-12 15:43:35 +0300
commit0368d854cfc5278df7bb29b0a75ee5db1c11a0d7 (patch)
tree80c35bf0537fb718d0842351e0bdd38f9b852061 /src/TimelineView.cc
parentHighlight URLs in TimelineView (diff)
downloadnheko-0368d854cfc5278df7bb29b0a75ee5db1c11a0d7.tar.xz
Initial support for backwards pagination
Diffstat (limited to 'src/TimelineView.cc')
-rw-r--r--src/TimelineView.cc274
1 files changed, 193 insertions, 81 deletions
diff --git a/src/TimelineView.cc b/src/TimelineView.cc

index c57d8f7b..9f80db66 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc
@@ -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); + } + + // Reverse again to render them. + std::reverse(items.begin(), items.end()); - try { - img.deserialize(event.toObject()); - } catch (const DeserializationException &e) { - qWarning() << e.what() << event; - continue; - } + for (const auto &item : items) + addTimelineItem(item, TimelineDirection::Top); - auto with_sender = last_sender_ != img.sender(); - auto color = TimelineViewManager::getUserColor(img.sender()); + prev_batch_token_ = msgs.end(); + isPaginationInProgress_ = false; +} + +TimelineItem *TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection direction) +{ + events::EventType ty = events::extractEventType(event); - addHistoryItem(img, color, with_sender); + if (ty == events::EventType::RoomMessage) { + events::MessageEventType msg_type = events::extractMessageEventType(event); - last_sender_ = img.sender(); - message_count += 1; - } else if (msg_type == events::MessageEventType::Unknown) { - qWarning() << "Unknown message type" << event.toObject(); - continue; + 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::updateLastSender(const QString &user_id, TimelineDirection direction) +{ + if (direction == TimelineDirection::Bottom) + last_sender_ = user_id; + else + last_sender_backwards_ = user_id; } -void TimelineView::addHistoryItem(const events::MessageEvent<msgs::Image> &event, const QString &color, bool with_sender) +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() -{ -}