First version of reading-while-scrolling.

This commit is contained in:
John Preston 2020-02-11 15:23:51 +04:00
parent db2aa7000a
commit 70408f0e22
12 changed files with 238 additions and 113 deletions

View File

@ -6009,13 +6009,16 @@ void ApiWrap::readServerHistory(not_null<History*> history) {
}
}
void ApiWrap::readServerHistoryForce(not_null<History*> history) {
void ApiWrap::readServerHistoryForce(
not_null<History*> history,
MsgId upTo) {
const auto peer = history->peer;
const auto upTo = history->readInbox();
if (!upTo) {
return;
upTo = history->readInbox();
if (!upTo) {
return;
}
}
if (const auto channel = peer->asChannel()) {
if (!channel->amIn()) {
return; // no read request for channels that I didn't join
@ -6099,7 +6102,7 @@ void ApiWrap::sendReadRequest(not_null<PeerData*> peer, MsgId upTo) {
sendReadRequest(peer, *next);
} else if (const auto history
= _session->data().historyLoaded(peer)) {
if (!history->unreadCountKnown()) {
if (history->unreadCountRefreshNeeded()) {
requestDialogEntry(history);
}
}

View File

@ -390,7 +390,7 @@ public:
const SendAction &action);
void shareContact(not_null<UserData*> user, const SendAction &action);
void readServerHistory(not_null<History*> history);
void readServerHistoryForce(not_null<History*> history);
void readServerHistoryForce(not_null<History*> history, MsgId upTo = 0);
//void readFeed( // #feed
// not_null<Data::Feed*> feed,
// Data::MessagePosition position);

View File

@ -1592,11 +1592,12 @@ void History::calculateFirstUnreadMessage() {
}
void History::readClientSideMessages() {
for (const auto &block : blocks) {
for (const auto &view : block->messages) {
const auto item = view->data();
if (!item->out()) {
item->markClientSideAsRead();
auto unread = unreadCount();
for (const auto item : _localMessages) {
if (!item->out() && item->unread()) {
item->markClientSideAsRead();
if (unread > 0) {
setUnreadCount(--unread);
}
}
}
@ -1604,16 +1605,117 @@ void History::readClientSideMessages() {
MsgId History::readInbox() {
const auto upTo = msgIdForRead();
readClientSideMessages();
if (unreadCountKnown()) {
setUnreadCount(0);
}
readClientSideMessages();
if (upTo) {
inboxRead(upTo);
}
return upTo;
}
void History::readInboxTill(not_null<HistoryItem*> item) {
if (!IsServerMsgId(item->id)) {
auto view = item->mainView();
if (!view) {
return;
}
auto block = view->block();
auto blockIndex = block->indexInHistory();
auto itemIndex = view->indexInBlock();
while (blockIndex > 0 || itemIndex > 0) {
if (itemIndex > 0) {
view = block->messages[--itemIndex].get();
} else {
while (blockIndex > 0) {
block = blocks[--blockIndex].get();
itemIndex = block->messages.size();
if (itemIndex > 0) {
view = block->messages[--itemIndex].get();
break;
}
}
}
item = view->data();
if (IsServerMsgId(item->id)) {
break;
}
}
if (!IsServerMsgId(item->id)) {
LOG(("App Error: "
"Can't read history till unknown local message."));
return;
}
}
readClientSideMessages();
if (unreadMark()) {
session().api().changeDialogUnreadMark(this, false);
}
if (_inboxReadTillLocal >= item->id) {
return;
}
_inboxReadTillLocal = item->id;
const auto stillUnread = countStillUnreadLocal();
if (!stillUnread) {
session().api().readServerHistoryForce(this, _inboxReadTillLocal);
return;
}
setInboxReadTill(_inboxReadTillLocal);
if (stillUnread && _unreadCount && *stillUnread == *_unreadCount) {
return;
}
setUnreadCount(*stillUnread);
session().api().readServerHistoryForce(this, _inboxReadTillLocal);
updateChatListEntry();
}
bool History::unreadCountRefreshNeeded() const {
return !unreadCountKnown()
|| ((_inboxReadTillLocal + 1) > _inboxReadBefore.value_or(0));
}
std::optional<int> History::countStillUnreadLocal() const {
if (isEmpty()) {
return std::nullopt;
}
const auto till = _inboxReadTillLocal;
if (_inboxReadBefore) {
const auto before = *_inboxReadBefore;
if (minMsgId() <= before && maxMsgId() >= till) {
auto result = 0;
for (const auto &block : blocks) {
for (const auto &message : block->messages) {
const auto item = message->data();
if (item->out() || !IsServerMsgId(item->id)) {
continue;
} else if (item->id > till) {
break;
} else if (item->id >= before) {
++result;
}
}
}
if (_unreadCount) {
return std::max(*_unreadCount - result, 0);
}
}
}
if (!loadedAtBottom() || minMsgId() > till) {
return std::nullopt;
}
auto result = 0;
for (const auto &block : blocks) {
for (const auto &message : block->messages) {
const auto item = message->data();
if (!item->out() && IsServerMsgId(item->id) && item->id > till) {
++result;
}
}
}
return result;
}
void History::applyInboxReadUpdate(
FolderId folderId,
MsgId upTo,
@ -1625,10 +1727,12 @@ void History::applyInboxReadUpdate(
session().api().requestDialogEntry(this);
session().api().requestDialogEntry(folder);
}
if (!peer->isChannel() || peer->asChannel()->pts() == channelPts) {
inboxRead(upTo, stillUnread);
} else {
inboxRead(upTo);
if (_inboxReadTillLocal <= upTo) {
if (!peer->isChannel() || peer->asChannel()->pts() == channelPts) {
inboxRead(upTo, stillUnread);
} else {
inboxRead(upTo);
}
}
}
@ -1645,9 +1749,9 @@ void History::inboxRead(MsgId upTo, std::optional<int> stillUnread) {
}
setInboxReadTill(upTo);
updateChatListEntry();
if (peer->migrateTo()) {
if (auto migrateTo = peer->owner().historyLoaded(peer->migrateTo()->id)) {
migrateTo->updateChatListEntry();
if (const auto to = peer->migrateTo()) {
if (const auto migrated = peer->owner().historyLoaded(to->id)) {
migrated->updateChatListEntry();
}
}
@ -2656,7 +2760,7 @@ void History::applyDialogFields(
} else {
clearFolder();
}
if (!skipUnreadUpdate()) {
if (!skipUnreadUpdate() && maxInboxRead >= _inboxReadTillLocal) {
setUnreadCount(unreadCount);
setInboxReadTill(maxInboxRead);
}
@ -2690,6 +2794,7 @@ void History::setInboxReadTill(MsgId upTo) {
} else {
_inboxReadBefore = upTo + 1;
}
accumulate_max(_inboxReadTillLocal, upTo);
}
void History::setOutboxReadTill(MsgId upTo) {

View File

@ -159,6 +159,7 @@ public:
[[nodiscard]] HistoryItem *latestSendingMessage() const;
MsgId readInbox();
void readInboxTill(not_null<HistoryItem*> item);
void applyInboxReadUpdate(
FolderId folderId,
MsgId upTo,
@ -174,6 +175,10 @@ public:
[[nodiscard]] int unreadCount() const;
[[nodiscard]] bool unreadCountKnown() const;
// Some old unread count is known, but we read history till some place.
[[nodiscard]] bool unreadCountRefreshNeeded() const;
void setUnreadCount(int newUnreadCount);
void setUnreadMark(bool unread);
[[nodiscard]] bool unreadMark() const;
@ -469,6 +474,7 @@ private:
void getNextFirstUnreadMessage();
bool nonEmptyCountMoreThan(int count) const;
std::optional<int> countUnread(MsgId upTo) const;
std::optional<int> countStillUnreadLocal() const;
// Creates if necessary a new block for adding item.
// Depending on isBuildingFrontBlock() gets front or back block.
@ -497,6 +503,7 @@ private:
std::optional<MsgId> _inboxReadBefore;
std::optional<MsgId> _outboxReadBefore;
MsgId _inboxReadTillLocal = 0;
std::optional<int> _unreadCount;
std::optional<int> _unreadMentionsCount;
base::flat_set<MsgId> _unreadMentions;

View File

@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "window/window_peer_menu.h"
#include "window/window_controller.h"
#include "window/notifications_manager.h"
#include "boxes/confirm_box.h"
#include "boxes/report_box.h"
#include "boxes/sticker_set_box.h"
@ -655,13 +656,13 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
auto iItem = (_curHistory == _history ? _curItem : 0);
auto view = block->messages[iItem].get();
auto item = view->data();
auto readTill = (HistoryItem*)nullptr;
auto hclip = clip.intersected(QRect(0, hdrawtop, width(), clip.top() + clip.height()));
auto y = htop + block->y() + view->y();
p.save();
p.translate(0, y);
while (y < drawToY) {
auto h = view->height();
const auto h = view->height();
if (hclip.y() < y + h && hdrawtop < y + h) {
const auto selection = itemRenderSelection(
view,
@ -669,12 +670,20 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
seltoy - htop);
view->draw(p, hclip.translated(0, -y), selection, ms);
if (item->hasViews()) {
App::main()->scheduleViewIncrement(item);
const auto middle = y + h / 2;
const auto bottom = y + h;
if (_visibleAreaBottom >= bottom) {
readTill = view->data();
}
if (item->isUnreadMention() && !item->isUnreadMedia()) {
readMentions.insert(item);
_widget->enqueueMessageHighlight(view);
if (_visibleAreaBottom >= middle
&& _visibleAreaTop <= middle) {
if (item->hasViews()) {
App::main()->scheduleViewIncrement(item);
}
if (item->isUnreadMention() && !item->isUnreadMedia()) {
readMentions.insert(item);
_widget->enqueueMessageHighlight(view);
}
}
}
p.translate(0, h);
@ -693,9 +702,13 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
item = view->data();
}
p.restore();
if (readTill) {
_history->readInboxTill(readTill);
}
}
if (!readMentions.empty() && App::wnd()->doWeReadMentions()) {
if (!readMentions.empty() && _widget->doWeReadMentions()) {
session().api().markMediaRead(readMentions);
}
@ -2013,6 +2026,42 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) {
}
}
void HistoryInner::checkHistoryActivation() {
if (!_widget->doWeReadServerHistory()) {
return;
}
adjustCurrent(_visibleAreaBottom);
if (_history->loadedAtBottom() && _visibleAreaBottom >= height()) {
// Clear possible scheduled messages notifications.
session().notifications().clearFromHistory(_history);
}
if (_curHistory != _history || _history->isEmpty()) {
return;
}
auto block = _history->blocks[_curBlock].get();
auto view = block->messages[_curItem].get();
while (_curBlock > 0 || _curItem > 0) {
const auto top = itemTop(view);
const auto bottom = itemTop(view) + view->height();
if (_visibleAreaBottom >= bottom) {
break;
}
if (_curItem > 0) {
view = block->messages[--_curItem].get();
} else {
while (_curBlock > 0) {
block = _history->blocks[--_curBlock].get();
_curItem = block->messages.size();
if (_curItem > 0) {
view = block->messages[--_curItem].get();
break;
}
}
}
}
_history->readInboxTill(view->data());
}
void HistoryInner::recountHistoryGeometry() {
_contentWidth = _scroll->width();
@ -2178,6 +2227,7 @@ void HistoryInner::visibleAreaUpdated(int top, int bottom) {
const auto from = _visibleAreaTop - pages * visibleAreaHeight;
const auto till = _visibleAreaBottom + pages * visibleAreaHeight;
session().data().unloadHeavyViewParts(ElementDelegate(), from, till);
checkHistoryActivation();
}
bool HistoryInner::displayScrollDate() const {
@ -2326,7 +2376,8 @@ void HistoryInner::adjustCurrent(int32 y) const {
}
void HistoryInner::adjustCurrent(int32 y, History *history) const {
Assert(!history->isEmpty());
Expects(!history->isEmpty());
_curHistory = history;
if (_curBlock >= history->blocks.size()) {
_curBlock = history->blocks.size() - 1;

View File

@ -62,6 +62,7 @@ public:
void touchScrollUpdated(const QPoint &screenPos);
void checkHistoryActivation();
void recountHistoryGeometry();
void updateSize();

View File

@ -2265,16 +2265,18 @@ void HistoryWidget::unreadMessageAdded(not_null<HistoryItem*> item) {
// - on second we get wrong doWeReadServerHistory() and read both.
session().data().sendHistoryChangeNotifications();
if (_scroll->scrollTop() + 1 > _scroll->scrollTopMax()) {
destroyUnreadBar();
const auto atBottom = (_scroll->scrollTop() >= _scroll->scrollTopMax());
if (!atBottom) {
return;
}
if (!App::wnd()->doWeReadServerHistory()) {
destroyUnreadBar();
if (!doWeReadServerHistory()) {
return;
}
if (item->isUnreadMention() && !item->isUnreadMedia()) {
session().api().markMediaRead(item);
}
session().api().readServerHistoryForce(_history);
_history->readInboxTill(item);
// Also clear possible scheduled messages notifications.
session().notifications().clearFromHistory(_history);
@ -2403,7 +2405,9 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages
addMessagesToBack(peer, *histList);
_preloadDownRequest = 0;
preloadHistoryIfNeeded();
if (_history->loadedAtBottom() && App::wnd()) App::wnd()->checkHistoryActivation();
if (_history->loadedAtBottom()) {
App::wnd()->checkHistoryActivation();
}
} else if (_firstLoadRequest == requestId) {
if (toMigrated) {
_history->clear(History::ClearType::Unload);
@ -2472,30 +2476,21 @@ void HistoryWidget::windowShown() {
}
bool HistoryWidget::doWeReadServerHistory() const {
if (!_history || !_list) return true;
if (_firstLoadRequest || _a_show.animating()) return false;
if (_history->loadedAtBottom()) {
int scrollTop = _scroll->scrollTop();
if (scrollTop + 1 > _scroll->scrollTopMax()) return true;
if (const auto unread = firstUnreadMessage()) {
const auto scrollBottom = scrollTop + _scroll->height();
if (scrollBottom > _list->itemTop(unread)) {
return true;
}
}
}
if (_history->hasNotFreezedUnreadBar()
|| (_migrated && _migrated->hasNotFreezedUnreadBar())) {
return true;
}
return false;
return doWeReadMentions() && !session().supportMode();
}
bool HistoryWidget::doWeReadMentions() const {
if (!_history || !_list) return true;
if (_firstLoadRequest || _a_show.animating()) return false;
return true;
return _history
&& _list
&& !_firstLoadRequest
&& !_a_show.animating()
&& App::wnd()->doWeMarkAsRead();
}
void HistoryWidget::checkHistoryActivation() {
if (_list) {
_list->checkHistoryActivation();
}
}
void HistoryWidget::firstLoadMessages() {
@ -2714,24 +2709,6 @@ void HistoryWidget::visibleAreaUpdated() {
const auto scrollBottom = scrollTop + _scroll->height();
_list->visibleAreaUpdated(scrollTop, scrollBottom);
controller()->floatPlayerAreaUpdated().notify(true);
const auto atBottom = (scrollTop >= _scroll->scrollTopMax());
if (_history->loadedAtBottom()
&& atBottom
&& App::wnd()->doWeReadServerHistory()) {
// Clear possible scheduled messages notifications.
session().api().readServerHistory(_history);
session().notifications().clearFromHistory(_history);
} else if (_history->loadedAtBottom()
&& (_history->unreadCount() > 0
|| (_migrated && _migrated->unreadCount() > 0))) {
const auto unread = firstUnreadMessage();
const auto unreadVisible = unread
&& (scrollBottom > _list->itemTop(unread));
if (unreadVisible && App::wnd()->doWeReadServerHistory()) {
session().api().readServerHistory(_history);
}
}
}
}
@ -2820,9 +2797,8 @@ void HistoryWidget::historyDownClicked() {
} else if (_replyReturn && _replyReturn->history() == _migrated) {
showHistory(_peer->id, -_replyReturn->id);
} else if (_peer) {
showHistory(
_peer->id,
session().supportMode() ? ShowAtTheEndMsgId : ShowAtUnreadMsgId);
showHistory(_peer->id, ShowAtTheEndMsgId); // #TODO reading
// session().supportMode() ? ShowAtTheEndMsgId : ShowAtUnreadMsgId);
}
}
@ -3178,10 +3154,8 @@ void HistoryWidget::doneShow() {
handlePendingHistoryUpdate();
}
preloadHistoryIfNeeded();
if (App::wnd()) {
App::wnd()->checkHistoryActivation();
App::wnd()->setInnerFocus();
}
App::wnd()->checkHistoryActivation();
App::wnd()->setInnerFocus();
}
void HistoryWidget::finishAnimating() {

View File

@ -109,9 +109,10 @@ public:
void historyLoaded();
void windowShown();
bool doWeReadServerHistory() const;
bool doWeReadMentions() const;
[[nodiscard]] bool doWeReadServerHistory() const;
[[nodiscard]] bool doWeReadMentions() const;
bool skipItemRepaint();
void checkHistoryActivation();
void leaveToChildEvent(QEvent *e, QWidget *child) override;
void dragEnterEvent(QDragEnterEvent *e) override;

View File

@ -2217,10 +2217,8 @@ void MainWidget::dialogsToUp() {
_dialogs->jumpToTop();
}
void MainWidget::markActiveHistoryAsRead() {
if (const auto activeHistory = _history->history()) {
session().api().readServerHistory(activeHistory);
}
void MainWidget::checkHistoryActivation() {
_history->checkHistoryActivation();
}
void MainWidget::showAnimated(const QPixmap &bgAnimCache, bool back) {
@ -3519,15 +3517,8 @@ bool MainWidget::isActive() const {
return !_isIdle && isVisible() && !_a_show.animating();
}
bool MainWidget::doWeReadServerHistory() const {
return isActive()
&& !session().supportMode()
&& !_mainSection
&& _history->doWeReadServerHistory();
}
bool MainWidget::doWeReadMentions() const {
return isActive() && !_mainSection && _history->doWeReadMentions();
bool MainWidget::doWeMarkAsRead() const {
return isActive() && !_mainSection;
}
bool MainWidget::lastWasOnline() const {

View File

@ -148,7 +148,7 @@ public:
bool deleteChannelFailed(const RPCError &error);
void historyToDown(History *hist);
void dialogsToUp();
void markActiveHistoryAsRead();
void checkHistoryActivation();
PeerData *peer();
@ -173,8 +173,7 @@ public:
void updateOnlineDisplayIn(int32 msecs);
bool isActive() const;
bool doWeReadServerHistory() const;
bool doWeReadMentions() const;
[[nodiscard]] bool doWeMarkAsRead() const;
bool lastWasOnline() const;
crl::time lastSetOnline() const;

View File

@ -503,23 +503,17 @@ void MainWindow::themeUpdated(const Window::Theme::BackgroundUpdate &data) {
}
}
bool MainWindow::doWeReadServerHistory() {
bool MainWindow::doWeMarkAsRead() {
if (!_main || Ui::isLayerShown()) {
return false;
}
updateIsActive(0);
return isActive()
&& !Ui::isLayerShown()
&& (_main ? _main->doWeReadServerHistory() : false);
}
bool MainWindow::doWeReadMentions() {
updateIsActive(0);
return isActive()
&& !Ui::isLayerShown()
&& (_main ? _main->doWeReadMentions() : false);
return isActive();
}
void MainWindow::checkHistoryActivation() {
if (doWeReadServerHistory()) {
_main->markActiveHistoryAsRead();
if (_main) {
_main->checkHistoryActivation();
}
}

View File

@ -62,8 +62,7 @@ public:
MainWidget *mainWidget();
bool doWeReadServerHistory();
bool doWeReadMentions();
[[nodiscard]] bool doWeMarkAsRead();
void activate();