From 78d83a2c69fe4c0faeb1b64858f93dd50ac3e1af Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 4 Sep 2020 20:11:36 +0400 Subject: [PATCH] Show local sending messages in replies section. --- .../SourceFiles/data/data_replies_list.cpp | 76 ++++++++++++- Telegram/SourceFiles/data/data_replies_list.h | 6 ++ Telegram/SourceFiles/history/history.cpp | 4 + Telegram/SourceFiles/history/history.h | 3 + .../history/view/history_view_list_widget.cpp | 101 ++++++++++++++---- .../history/view/history_view_list_widget.h | 11 +- .../view/history_view_replies_section.cpp | 22 ++++ .../view/history_view_replies_section.h | 2 + .../view/history_view_scheduled_section.cpp | 5 + .../view/history_view_scheduled_section.h | 1 + Telegram/SourceFiles/mainwidget.cpp | 3 +- 11 files changed, 207 insertions(+), 27 deletions(-) diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index 73ea150fa..35945d979 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { namespace { -constexpr auto kMessagesPerPage = 16; +constexpr auto kMessagesPerPage = 50; } // namespace @@ -45,6 +45,21 @@ rpl::producer RepliesList::source( MessagePosition aroundId, int limitBefore, int limitAfter) { + return rpl::combine( + sourceFromServer(aroundId, limitBefore, limitAfter), + _history->session().changes().historyFlagsValue( + _history, + Data::HistoryUpdate::Flag::LocalMessages) + ) | rpl::map([=](MessagesSlice &&server, const auto &) { + appendLocalMessages(server); + return std::move(server); + }); +} + +rpl::producer RepliesList::sourceFromServer( + MessagePosition aroundId, + int limitBefore, + int limitAfter) { const auto around = aroundId.fullId.msg; return [=](auto consumer) { auto lifetime = rpl::lifetime(); @@ -78,6 +93,65 @@ rpl::producer RepliesList::source( }; } +void RepliesList::appendLocalMessages(MessagesSlice &slice) { + const auto &local = _history->localMessages(); + if (local.empty()) { + return; + } else if (slice.ids.empty()) { + if (slice.skippedBefore != 0 || slice.skippedAfter != 0) { + return; + } + slice.ids.reserve(local.size()); + for (const auto item : local) { + if (item->replyToTop() != _rootId) { + continue; + } + slice.ids.push_back(item->fullId()); + } + ranges::sort(slice.ids); + return; + } + auto &owner = _history->owner(); + auto dates = std::vector(); + dates.reserve(slice.ids.size()); + for (const auto &id : slice.ids) { + const auto message = owner.message(id); + Assert(message != nullptr); + + dates.push_back(message->date()); + } + for (const auto item : local) { + if (item->replyToTop() != _rootId) { + continue; + } + const auto date = item->date(); + if (date < dates.front()) { + if (slice.skippedBefore != 0) { + if (slice.skippedBefore) { + ++*slice.skippedBefore; + } + continue; + } + dates.insert(dates.begin(), date); + slice.ids.insert(slice.ids.begin(), item->fullId()); + } else { + auto to = dates.size(); + for (; to != 0; --to) { + const auto checkId = slice.ids[to - 1].msg; + if (dates[to - 1] > date) { + continue; + } else if (dates[to - 1] < date + || IsServerMsgId(checkId) + || checkId < item->id) { + break; + } + } + dates.insert(dates.begin() + to, date); + slice.ids.insert(slice.ids.begin() + to, item->fullId()); + } + } +} + rpl::producer RepliesList::fullCount() const { return _fullCount.value() | rpl::filter_optional(); } diff --git a/Telegram/SourceFiles/data/data_replies_list.h b/Telegram/SourceFiles/data/data_replies_list.h index 76d9fa1b7..cd0de887f 100644 --- a/Telegram/SourceFiles/data/data_replies_list.h +++ b/Telegram/SourceFiles/data/data_replies_list.h @@ -35,6 +35,12 @@ private: [[nodiscard]] Histories &histories(); + [[nodiscard]] rpl::producer sourceFromServer( + MessagePosition aroundId, + int limitBefore, + int limitAfter); + void appendLocalMessages(MessagesSlice &slice); + [[nodiscard]] bool buildFromData(not_null viewer); [[nodiscard]] bool applyUpdate( not_null viewer, diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 040dc6add..222627431 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -1338,6 +1338,10 @@ void History::unregisterLocalMessage(not_null item) { session().changes().historyUpdated(this, UpdateFlag::LocalMessages); } +const base::flat_set> &History::localMessages() { + return _localMessages; +} + HistoryItem *History::latestSendingMessage() const { auto sending = ranges::view::all( _localMessages diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 4eede4a83..3ede42830 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -77,6 +77,7 @@ public: void checkLocalMessages(); void removeJoinedMessage(); + bool isEmpty() const; bool isDisplayedEmpty() const; Element *findFirstNonEmpty() const; @@ -181,6 +182,8 @@ public: void registerLocalMessage(not_null item); void unregisterLocalMessage(not_null item); + [[nodiscard]] auto localMessages() + -> const base::flat_set> &; [[nodiscard]] HistoryItem *latestSendingMessage() const; [[nodiscard]] bool readInboxTillNeedsRequest(MsgId tillId); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index aa8199587..4b046fe68 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -552,13 +552,36 @@ not_null ListWidget::enforceViewForItem( } void ListWidget::updateAroundPositionFromRows() { - _aroundIndex = findNearestItem(_aroundPosition); - if (_aroundIndex >= 0) { - const auto newPosition = _items[_aroundIndex]->data()->position(); - if (_aroundPosition != newPosition) { - _aroundPosition = newPosition; - crl::on_main(this, [=] { refreshViewer(); }); + const auto nearestIndex = findNearestItem(_aroundPosition); + if (nearestIndex < 0) { + _aroundIndex = -1; + return; + } + const auto isGoodIndex = [&](int index) { + Expects(index >= 0 && index < _items.size()); + + return _delegate->listIsGoodForAroundPosition(_items[index]); + }; + _aroundIndex = [&] { + for (auto index = nearestIndex; index < _items.size(); ++index) { + if (isGoodIndex(index)) { + return index; + } } + for (auto index = nearestIndex; index != 0;) { + if (isGoodIndex(--index)) { + return index; + } + } + return -1; + }(); + if (_aroundIndex < 0) { + return; + } + const auto newPosition = _items[_aroundIndex]->data()->position(); + if (_aroundPosition != newPosition) { + _aroundPosition = newPosition; + crl::on_main(this, [=] { refreshViewer(); }); } } @@ -1055,8 +1078,8 @@ void ListWidget::checkMoveToOtherViewer() { return; } - auto topItem = findItemByY(_visibleTop); - auto bottomItem = findItemByY(_visibleBottom); + auto topItemIndex = findItemIndexByY(_visibleTop); + auto bottomItemIndex = findItemIndexByY(_visibleBottom); auto preloadedHeight = kPreloadedScreensCountFull * visibleHeight; auto preloadedCount = preloadedHeight / _itemAverageHeight; auto preloadIdsLimitMin = (preloadedCount / 2) + 1; @@ -1075,32 +1098,64 @@ void ListWidget::checkMoveToOtherViewer() { - kPreloadIfLessThanScreens; auto minUniversalIdDelta = (minScreenDelta * visibleHeight) / _itemAverageHeight; - auto preloadAroundMessage = [&](not_null view) { + const auto preloadAroundMessage = [&](int index) { + Expects(index >= 0 && index < _items.size()); + auto preloadRequired = false; - auto itemPosition = view->data()->position(); - auto itemIndex = ranges::find(_items, view) - begin(_items); - Assert(itemIndex < _items.size()); + auto itemPosition = _items[index]->data()->position(); if (!preloadRequired) { preloadRequired = (_idsLimit < preloadIdsLimitMin); } if (!preloadRequired) { Assert(_aroundIndex >= 0); - auto delta = std::abs(itemIndex - _aroundIndex); + auto delta = std::abs(index - _aroundIndex); preloadRequired = (delta >= minUniversalIdDelta); } if (preloadRequired) { _idsLimit = preloadIdsLimit; _aroundPosition = itemPosition; - _aroundIndex = itemIndex; + _aroundIndex = index; refreshViewer(); } }; + const auto findGoodAbove = [&](int index) { + Expects(index >= 0 && index < _items.size()); + + for (; index != _items.size(); ++index) { + if (_delegate->listIsGoodForAroundPosition(_items[index])) { + return index; + } + } + return -1; + }; + const auto findGoodBelow = [&](int index) { + Expects(index >= 0 && index < _items.size()); + + for (++index; index != 0;) { + if (_delegate->listIsGoodForAroundPosition(_items[--index])) { + return index; + } + } + return -1; + }; if (preloadTop && !topLoaded) { - preloadAroundMessage(topItem); + const auto goodAboveIndex = findGoodAbove(topItemIndex); + const auto goodIndex = (goodAboveIndex >= 0) + ? goodAboveIndex + : findGoodBelow(topItemIndex); + if (goodIndex >= 0) { + preloadAroundMessage(goodIndex); + } } else if (preloadBottom && !bottomLoaded) { - preloadAroundMessage(bottomItem); + const auto goodBelowIndex = findGoodBelow(bottomItemIndex); + const auto goodIndex = (goodBelowIndex >= 0) + ? goodBelowIndex + : findGoodAbove(bottomItemIndex); + if (goodIndex >= 0) { + preloadAroundMessage(goodIndex); + } } } @@ -1553,20 +1608,24 @@ MessageIdsList ListWidget::getSelectedItems() const { return collectSelectedIds(); } -not_null ListWidget::findItemByY(int y) const { +int ListWidget::findItemIndexByY(int y) const { Expects(!_items.empty()); if (y < _itemsTop) { - return _items.front(); + return 0; } auto i = std::lower_bound( begin(_items), end(_items), y, [this](auto &elem, int top) { - return this->itemTop(elem) + elem->height() <= top; - }); - return (i != end(_items)) ? i->get() : _items.back().get(); + return this->itemTop(elem) + elem->height() <= top; + }); + return std::min(int(i - begin(_items)), int(_items.size() - 1)); +} + +not_null ListWidget::findItemByY(int y) const { + return _items[findItemIndexByY(y)]; } Element *ListWidget::strictFindItemByY(int y) const { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 0388e0eb8..284fe1197 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -75,6 +75,8 @@ public: virtual void listContentRefreshed() = 0; virtual ClickHandlerPtr listDateLink(not_null view) = 0; virtual bool listElementHideReply(not_null view) = 0; + virtual bool listIsGoodForAroundPosition( + not_null view) = 0; }; @@ -322,11 +324,12 @@ private: void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false); - not_null findItemByY(int y) const; - Element *strictFindItemByY(int y) const; - int findNearestItem(Data::MessagePosition position) const; + [[nodiscard]] int findItemIndexByY(int y) const; + [[nodiscard]] not_null findItemByY(int y) const; + [[nodiscard]] Element *strictFindItemByY(int y) const; + [[nodiscard]] int findNearestItem(Data::MessagePosition position) const; void viewReplaced(not_null was, Element *now); - HistoryItemsList collectVisibleItems() const; + [[nodiscard]] HistoryItemsList collectVisibleItems() const; void checkMoveToOtherViewer(); void updateVisibleTopItem(); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index e5e8e0b57..bcebdd639 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -135,6 +135,10 @@ RepliesWidget::RepliesWidget( ) | rpl::start_with_next([=] { confirmDeleteSelected(); }, _topBar->lifetime()); + _topBar->forwardSelectionRequest( + ) | rpl::start_with_next([=] { + confirmForwardSelected(); + }, _topBar->lifetime()); _topBar->clearSelectionRequest( ) | rpl::start_with_next([=] { clearSelected(); @@ -1380,6 +1384,11 @@ bool RepliesWidget::listElementHideReply(not_null view) { return (view->data()->replyToId() == _rootId); } +bool RepliesWidget::listIsGoodForAroundPosition( + not_null view) { + return IsServerMsgId(view->data()->id); +} + void RepliesWidget::confirmSendNowSelected() { auto items = _inner->getSelectedItems(); if (items.empty()) { @@ -1409,6 +1418,19 @@ void RepliesWidget::confirmDeleteSelected() { }); } +void RepliesWidget::confirmForwardSelected() { + auto items = _inner->getSelectedItems(); + if (items.empty()) { + return; + } + const auto weak = Ui::MakeWeak(this); + Window::ShowForwardMessagesBox(controller(), std::move(items), [=] { + if (const auto strong = weak.data()) { + strong->clearSelected(); + } + }); +} + void RepliesWidget::clearSelected() { _inner->cancelSelection(); } diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 69eda8c4a..43a3f13e7 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -120,6 +120,7 @@ public: void listContentRefreshed() override; ClickHandlerPtr listDateLink(not_null view) override; bool listElementHideReply(not_null view) override; + bool listIsGoodForAroundPosition(not_null view) override; protected: void resizeEvent(QResizeEvent *e) override; @@ -158,6 +159,7 @@ private: void confirmSendNowSelected(); void confirmDeleteSelected(); + void confirmForwardSelected(); void clearSelected(); void send(); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 26439e363..0833531d9 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1150,6 +1150,11 @@ bool ScheduledWidget::listElementHideReply(not_null view) { return false; } +bool ScheduledWidget::listIsGoodForAroundPosition( + not_null view) { + return true; +} + void ScheduledWidget::confirmSendNowSelected() { auto items = _inner->getSelectedItems(); if (items.empty()) { diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index 926b920ab..c7f4fa330 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -111,6 +111,7 @@ public: void listContentRefreshed() override; ClickHandlerPtr listDateLink(not_null view) override; bool listElementHideReply(not_null view) override; + bool listIsGoodForAroundPosition(not_null view) override; protected: void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index dbf28f652..8c9163236 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -709,8 +709,9 @@ void MainWidget::cancelUploadLayer(not_null item) { auto &data = session().data(); if (const auto item = data.message(itemId)) { if (!item->isEditingMedia()) { + const auto history = item->history(); item->destroy(); - item->history()->requestChatListMessage(); + history->requestChatListMessage(); } else { item->returnSavedMedia(); session().uploader().cancel(item->fullId());