diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0ac70fd9f..3176ed87a 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1359,6 +1359,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_comments_open_none" = "Leave a comment"; "lng_replies_view_original" = "View in chat"; "lng_replies_messages" = "Replies"; +"lng_replies_discussion_started" = "Discussion started"; +"lng_replies_no_comments" = "No comments here yet..."; "lng_archived_name" = "Archived chats"; "lng_archived_add" = "Archive"; diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index ec9d6765e..511ffb2e3 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -9,12 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" +#include "history/history_service.h" #include "main/main_session.h" #include "data/data_histories.h" #include "data/data_session.h" #include "data/data_changes.h" #include "data/data_channel.h" #include "data/data_messages.h" +#include "lang/lang_keys.h" #include "apiwrap.h" namespace Data { @@ -22,6 +24,17 @@ namespace { constexpr auto kMessagesPerPage = 50; +[[nodiscard]] HistoryService *GenerateDivider( + not_null history, + TimeId date, + const QString &text) { + return history->makeServiceMessage( + history->session().data().nextNonHistoryEntryId(), + MTPDmessage_ClientFlag::f_fake_history_item, + date, + HistoryService::PreparedText{ text }); +} + } // namespace struct RepliesList::Viewer { @@ -40,6 +53,9 @@ RepliesList::RepliesList(not_null history, MsgId rootId) RepliesList::~RepliesList() { histories().cancelRequest(base::take(_beforeId)); histories().cancelRequest(base::take(_afterId)); + if (_divider) { + _divider->destroy(); + } } rpl::producer RepliesList::source( @@ -157,13 +173,21 @@ rpl::producer RepliesList::fullCount() const { return _fullCount.value() | rpl::filter_optional(); } +void RepliesList::injectRootMessageAndReverse( + not_null slice) { + injectRootMessage(slice); + ranges::reverse(slice->ids); +} + void RepliesList::injectRootMessage(not_null slice) { if (slice->skippedBefore != 0) { return; } if (const auto root = lookupRoot()) { + injectRootDivider(root, slice); + if (const auto group = _history->owner().groups().find(root)) { - for (const auto item : group->items) { + for (const auto item : ranges::view::reverse(group->items)) { slice->ids.push_back(item->fullId()); } if (slice->fullCount) { @@ -178,6 +202,28 @@ void RepliesList::injectRootMessage(not_null slice) { } } +void RepliesList::injectRootDivider( + not_null root, + not_null slice) { + const auto withComments = !slice->ids.empty(); + const auto text = [&] { + return withComments + ? tr::lng_replies_discussion_started(tr::now) + : tr::lng_replies_no_comments(tr::now); + }; + if (!_divider) { + _dividerWithComments = withComments; + _divider = GenerateDivider( + _history, + root->date(), + text()); + } else if (_dividerWithComments != withComments) { + _dividerWithComments = withComments; + _divider->setServiceText(HistoryService::PreparedText{ text() }); + } + slice->ids.push_back(_divider->fullId()); +} + bool RepliesList::buildFromData(not_null viewer) { if (_list.empty() && _skippedBefore == 0 && _skippedAfter == 0) { viewer->slice.ids.clear(); @@ -185,7 +231,7 @@ bool RepliesList::buildFromData(not_null viewer) { = viewer->slice.skippedBefore = viewer->slice.skippedAfter = 0; - injectRootMessage(&viewer->slice); + injectRootMessageAndReverse(&viewer->slice); return true; } const auto around = [&] { @@ -230,9 +276,7 @@ bool RepliesList::buildFromData(not_null viewer) { } slice->fullCount = _fullCount.current(); - injectRootMessage(slice); - - ranges::reverse(slice->ids); + injectRootMessageAndReverse(slice); if (_skippedBefore != 0 && useBefore < viewer->limitBefore + 1) { loadBefore(); diff --git a/Telegram/SourceFiles/data/data_replies_list.h b/Telegram/SourceFiles/data/data_replies_list.h index dedc9982c..2de7e2b7f 100644 --- a/Telegram/SourceFiles/data/data_replies_list.h +++ b/Telegram/SourceFiles/data/data_replies_list.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/weak_ptr.h" class History; +class HistoryService; namespace Data { @@ -46,7 +47,11 @@ private: [[nodiscard]] bool applyUpdate( not_null viewer, const MessageUpdate &update); + void injectRootMessageAndReverse(not_null slice); void injectRootMessage(not_null slice); + void injectRootDivider( + not_null root, + not_null slice); bool processMessagesIsEmpty(const MTPmessages_Messages &result); void loadAround(MsgId id); void loadBefore(); @@ -60,6 +65,8 @@ private: rpl::variable> _fullCount; rpl::event_stream<> _partLoaded; std::optional _loadingAround; + HistoryService *_divider = nullptr; + bool _dividerWithComments = false; int _beforeId = 0; int _afterId = 0; diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index f91e205e8..200f6c8f6 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -108,6 +108,8 @@ public: not_null delegate, HistoryView::Element *replacing = nullptr) override; + void setServiceText(const PreparedText &prepared); + ~HistoryService(); protected: @@ -115,8 +117,6 @@ protected: void markMediaAsReadHook() override; - void setServiceText(const PreparedText &prepared); - QString fromLinkText() const; ClickHandlerPtr fromLink() const; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 5dcb230da..f0cfb57d6 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -123,7 +123,6 @@ RepliesWidget::RepliesWidget( , _root(lookupRoot()) , _commentsRoot(lookupCommentsRoot()) , _areComments(computeAreComments()) -, _scroll(this, st::historyScroll, false) , _topBar(this, controller) , _topBarShadow(this) , _composeControls(std::make_unique( @@ -132,7 +131,8 @@ RepliesWidget::RepliesWidget( ComposeControls::Mode::Normal)) , _rootView(this, object_ptr(this)) , _rootShadow(this) -, _scrollDown(_scroll, st::historyToDown) +, _scroll(std::make_unique(this, st::historyScroll, false)) +, _scrollDown(_scroll.get(), st::historyToDown) , _readRequestTimer([=] { sendReadTillRequest(); }) { setupRoot(); setupRootView(); @@ -162,8 +162,9 @@ RepliesWidget::RepliesWidget( clearSelected(); }, _topBar->lifetime()); - _topBarShadow->raise(); + _rootView->raise(); _rootShadow->raise(); + _topBarShadow->raise(); updateAdaptiveLayout(); subscribe(Adaptive::Changed(), [=] { updateAdaptiveLayout(); }); @@ -173,7 +174,7 @@ RepliesWidget::RepliesWidget( static_cast(this))); _scroll->move(0, _topBar->height()); _scroll->show(); - connect(_scroll, &Ui::ScrollArea::scrolled, [=] { onScroll(); }); + connect(_scroll.get(), &Ui::ScrollArea::scrolled, [=] { onScroll(); }); _inner->editMessageRequested( ) | rpl::start_with_next([=](auto fullId) { @@ -1366,7 +1367,13 @@ void RepliesWidget::updatePinnedVisibility() { setPinnedVisibility(true); return; } - const auto view = _inner->viewByPosition(_root->position()); + const auto item = [&] { + if (const auto group = _history->owner().groups().find(_root)) { + return group->items.front().get(); + } + return _root; + }(); + const auto view = _inner->viewByPosition(item->position()); setPinnedVisibility(!view || (view->y() + view->height() <= _scroll->scrollTop())); } @@ -1502,40 +1509,11 @@ void RepliesWidget::listVisibleItemsChanged(HistoryItemsList &&items) { MessagesBarData RepliesWidget::listMessagesBar( const std::vector> &elements) { if (!_commentsRoot || elements.empty()) { - return MessagesBarData(); + return {}; } - const auto rootBar = [&] { - const auto fromRoot = (elements.front()->data().get() == _root); - if (elements.size() < 2 || !fromRoot) { - return MessagesBarData(); - } - auto text = rpl::combine( - _replies->fullCount(), - _areComments.value() - ) | rpl::map([=](int count, bool areComments) { - return count - ? (areComments - ? tr::lng_comments_header - : tr::lng_replies_header)( - lt_count, - rpl::single(count) | tr::to_count()) - : (areComments - ? tr::lng_comments_header_none - : tr::lng_replies_header_none)(); - }) | rpl::flatten_latest(); - - return MessagesBarData{ - // Designated initializers here crash MSVC 16.7.3. - MessagesBar{ - .element = elements[1], - .focus = false, - }, - std::move(text), - }; - }; const auto till = _commentsRoot->commentsReadTill(); if (till < 2) { - return rootBar(); + return {}; } for (auto i = 0, count = int(elements.size()); i != count; ++i) { const auto item = elements[i]->data(); @@ -1554,7 +1532,7 @@ MessagesBarData RepliesWidget::listMessagesBar( } } } - return rootBar(); + return {}; } void RepliesWidget::listContentRefreshed() { diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 21b157ae1..0702940b6 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -237,7 +237,6 @@ private: HistoryItem *_commentsRoot = nullptr; std::shared_ptr _replies; rpl::variable _areComments = false; - object_ptr _scroll; QPointer _inner; object_ptr _topBar; object_ptr _topBarShadow; @@ -249,6 +248,8 @@ private: object_ptr> _rootView; object_ptr _rootShadow; + std::unique_ptr _scroll; + std::vector _replyReturns; HistoryItem *_replyReturn = nullptr;