diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index c6e7e5e56c..cfefab8437 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -2104,7 +2104,7 @@ void History::addUnreadBar() { } if (const auto count = chatListUnreadCount()) { _unreadBarView = _firstUnreadView; - _unreadBarView->createUnreadBar(); + _unreadBarView->createUnreadBar(tr::lng_unread_bar_some()); } } diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 1e996d0cb2..f8f0073b28 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -144,8 +144,8 @@ TextSelection ShiftItemSelection( return ShiftItemSelection(selection, byText.length()); } -void UnreadBar::init() { - text = tr::lng_unread_bar_some(tr::now); +void UnreadBar::init(const QString &string) { + text = string; width = st::semiboldFont->width(text); } @@ -440,12 +440,18 @@ bool Element::computeIsAttachToPrevious(not_null previous) { return false; } -void Element::createUnreadBar() { +void Element::createUnreadBar(rpl::producer text) { if (!AddComponents(UnreadBar::Bit())) { return; } const auto bar = Get(); - bar->init(); + std::move( + text + ) | rpl::start_with_next([=](const QString &text) { + if (const auto bar = Get()) { + bar->init(text); + } + }, bar->lifetime); if (data()->mainView() == this) { recountAttachToPreviousInBlocks(); } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 9c96f56b95..85e90ed573 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -118,7 +118,7 @@ TextSelection ShiftItemSelection( // Any HistoryView::Element can have this Component for // displaying the unread messages bar above the message. struct UnreadBar : public RuntimeComponent { - void init(); + void init(const QString &string); static int height(); static int marginTop(); @@ -127,6 +127,7 @@ struct UnreadBar : public RuntimeComponent { QString text; int width = 0; + rpl::lifetime lifetime; }; @@ -206,7 +207,7 @@ public: bool computeIsAttachToPrevious(not_null previous); - void createUnreadBar(); + void createUnreadBar(rpl::producer text); void destroyUnreadBar(); int displayedDateHeight() const; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 3be3bf18cd..232d41e2aa 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -394,13 +394,23 @@ void ListWidget::animatedScrollTo( _delegate->listScrollTo(scrollTop); return; } + const auto transition = (type == AnimatedScroll::Full) + ? anim::sineInOut + : anim::easeOutCubic; + if (delta > 0 && scrollTop == height() - (_visibleBottom - _visibleTop)) { + // Animated scroll to bottom. + _scrollToAnimation.start( + [=] { scrollToAnimationCallback(FullMsgId(), 0); }, + -delta, + 0, + st::slideDuration, + transition); + return; + } const auto index = findNearestItem(attachPosition); Assert(index >= 0 && index < int(_items.size())); const auto attachTo = _items[index]; const auto attachToId = attachTo->data()->fullId(); - const auto transition = (type == AnimatedScroll::Full) - ? anim::sineInOut - : anim::easeOutCubic; const auto initial = scrollTop - delta; _delegate->listScrollTo(initial); @@ -422,6 +432,14 @@ bool ListWidget::animatedScrolling() const { void ListWidget::scrollToAnimationCallback( FullMsgId attachToId, int relativeTo) { + if (!attachToId) { + // Animated scroll to bottom. + const auto current = int(std::round(_scrollToAnimation.value(0))); + _delegate->listScrollTo(height() + - (_visibleBottom - _visibleTop) + + current); + return; + } const auto attachTo = session().data().message(attachToId); const auto attachToView = viewForItem(attachTo); if (!attachToView) { @@ -483,11 +501,14 @@ void ListWidget::updateHighlightedMessage() { } void ListWidget::checkUnreadBarCreation() { - if (!_unreadBarElement) { - if (const auto index = _delegate->listUnreadBarView(_items)) { - _unreadBarElement = _items[*index].get(); - _unreadBarElement->createUnreadBar(); - refreshAttachmentsAtIndex(*index); + if (!_bar.element) { + if (auto data = _delegate->listMessagesBar(_items); data.bar.element) { + _bar = std::move(data.bar); + _barText = std::move(data.text); + _bar.element->createUnreadBar(_barText.value()); + const auto i = ranges::find(_items, not_null{ _bar.element }); + Assert(i != end(_items)); + refreshAttachmentsAtIndex(i - begin(_items)); } } } @@ -507,10 +528,10 @@ void ListWidget::restoreScrollState() { return; } if (!_scrollTopState.item) { - if (!_unreadBarElement) { + if (!_bar.element || !_bar.focus) { return; } - _scrollTopState.item = _unreadBarElement->data()->position(); + _scrollTopState.item = _bar.element->data()->position(); _scrollTopState.shift = st::lineWidth + st::historyUnreadBarMargin; } const auto index = findNearestItem(_scrollTopState.item); @@ -2605,11 +2626,11 @@ void ListWidget::viewReplaced(not_null was, Element *now) { if (_visibleTopItem == was) _visibleTopItem = now; if (_scrollDateLastItem == was) _scrollDateLastItem = now; if (_overElement == was) _overElement = now; - if (_unreadBarElement == was) { - const auto bar = _unreadBarElement->Get(); - _unreadBarElement = now; + if (_bar.element == was.get()) { + const auto bar = _bar.element->Get(); + _bar.element = now; if (now && bar) { - _unreadBarElement->createUnreadBar(); + _bar.element->createUnreadBar(_barText.value()); } } } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 1d76f4d0c6..100e7da74d 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -48,7 +48,16 @@ struct SelectedItem { bool canDelete = false; bool canForward = false; bool canSendNow = false; +}; +struct MessagesBar { + Element *element = nullptr; + bool focus = false; +}; + +struct MessagesBarData { + MessagesBar bar; + rpl::producer text; }; using SelectedItems = std::vector; @@ -70,7 +79,7 @@ public: not_null second) = 0; virtual void listSelectionChanged(SelectedItems &&items) = 0; virtual void listVisibleItemsChanged(HistoryItemsList &&items) = 0; - virtual std::optional listUnreadBarView( + virtual MessagesBarData listMessagesBar( const std::vector> &elements) = 0; virtual void listContentRefreshed() = 0; virtual ClickHandlerPtr listDateLink(not_null view) = 0; @@ -493,7 +502,8 @@ private: ClickHandlerPtr _scrollDateLink; SingleQueuedInvokation _applyUpdatedScrollState; - Element *_unreadBarElement = nullptr; + MessagesBar _bar; + rpl::variable _barText; MouseAction _mouseAction = MouseAction::None; TextSelectType _mouseSelectType = TextSelectType::Letters; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 1191a025f5..127a076672 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -1212,26 +1212,7 @@ void RepliesWidget::saveState(not_null memento) { void RepliesWidget::restoreState(not_null memento) { const auto setReplies = [&](std::shared_ptr replies) { _replies = std::move(replies); - - rpl::single( - tr::lng_contacts_loading() - ) | rpl::then(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( - ) | rpl::start_with_next([=](const QString &text) { - _topBar->setCustomTitle(text); - }, lifetime()); + _topBar->setCustomTitle(tr::lng_manage_discussion_group(tr::now)); }; if (auto replies = memento->getReplies()) { setReplies(std::move(replies)); @@ -1447,15 +1428,19 @@ void RepliesWidget::listSelectionChanged(SelectedItems &&items) { _topBar->showSelected(state); } -void RepliesWidget::readTill(MsgId tillId) { +void RepliesWidget::readTill(not_null item) { if (!_commentsRoot) { return; } - const auto now = _commentsRoot->commentsReadTill(); - if (now < tillId) { - _commentsRoot->setCommentsReadTill(tillId); + const auto was = _commentsRoot->commentsReadTill(); + const auto now = item->id; + const auto fast = item->out(); + if (was < now) { + _commentsRoot->setCommentsReadTill(now); if (!_readRequestTimer.isActive()) { - _readRequestTimer.callOnce(kReadRequestTimeout); + _readRequestTimer.callOnce(fast ? 0 : kReadRequestTimeout); + } else if (fast && _readRequestTimer.remainingTime() > 0) { + _readRequestTimer.callOnce(0); } } } @@ -1466,31 +1451,66 @@ void RepliesWidget::listVisibleItemsChanged(HistoryItemsList &&items) { return IsServerMsgId(item->id); }); if (good != end(reversed)) { - readTill((*good)->id); + readTill(*good); } } -std::optional RepliesWidget::listUnreadBarView( +MessagesBarData RepliesWidget::listMessagesBar( const std::vector> &elements) { - if (!_commentsRoot) { - return std::nullopt; + if (!_commentsRoot || elements.empty()) { + return MessagesBarData(); } + 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 std::nullopt; + return rootBar(); } for (auto i = 0, count = int(elements.size()); i != count; ++i) { const auto item = elements[i]->data(); - if (item->id > till) { + if (IsServerMsgId(item->id) && item->id > till) { if (item->out()) { - _commentsRoot->setCommentsReadTill(item->id); - _readRequestTimer.callOnce(kReadRequestTimeout); + readTill(item); } else { - return i; + return MessagesBarData{ + // Designated initializers here crash MSVC 16.7.3. + MessagesBar{ + .element = elements[i], + .focus = true, + }, + tr::lng_unread_bar_some(), + }; } } } - return std::nullopt; + return rootBar(); } 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 c073509b3c..b35cc2bba4 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -116,7 +116,7 @@ public: not_null second) override; void listSelectionChanged(SelectedItems &&items) override; void listVisibleItemsChanged(HistoryItemsList &&items) override; - std::optional listUnreadBarView( + MessagesBarData listMessagesBar( const std::vector> &elements) override; void listContentRefreshed() override; ClickHandlerPtr listDateLink(not_null view) override; @@ -155,7 +155,7 @@ private: void refreshRootView(); void setupDragArea(); void sendReadTillRequest(); - void readTill(MsgId id); + void readTill(not_null item); void setupScrollDownButton(); void scrollDownClicked(); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 0833531d99..5fee974c99 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1134,9 +1134,9 @@ void ScheduledWidget::listSelectionChanged(SelectedItems &&items) { void ScheduledWidget::listVisibleItemsChanged(HistoryItemsList &&items) { } -std::optional ScheduledWidget::listUnreadBarView( +MessagesBarData ScheduledWidget::listMessagesBar( const std::vector> &elements) { - return std::nullopt; + return MessagesBarData(); } void ScheduledWidget::listContentRefreshed() { diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index c7f4fa3306..8eb7f63424 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -106,7 +106,7 @@ public: not_null second) override; void listSelectionChanged(SelectedItems &&items) override; void listVisibleItemsChanged(HistoryItemsList &&items) override; - std::optional listUnreadBarView( + MessagesBarData listMessagesBar( const std::vector> &elements) override; void listContentRefreshed() override; ClickHandlerPtr listDateLink(not_null view) override;