From 6fce7182529402efd2b142675ca062c26f919fb1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 5 Sep 2024 14:40:05 +0400 Subject: [PATCH] Add jump-to-end button to chat preview. --- .../data/data_history_messages.cpp | 3 +- .../view/history_view_chat_preview.cpp | 84 +++++++++++++++++-- .../view/history_view_corner_buttons.cpp | 49 ++++++++--- .../view/history_view_corner_buttons.h | 16 +++- 4 files changed, 130 insertions(+), 22 deletions(-) diff --git a/Telegram/SourceFiles/data/data_history_messages.cpp b/Telegram/SourceFiles/data/data_history_messages.cpp index 66e888e285966..5d5dffa30b074 100644 --- a/Telegram/SourceFiles/data/data_history_messages.cpp +++ b/Telegram/SourceFiles/data/data_history_messages.cpp @@ -189,7 +189,8 @@ rpl::producer HistoryMessagesViewer( }; const auto messageId = (aroundId.fullId.msg == ShowAtUnreadMsgId) ? computeUnreadAroundId() - : (aroundId.fullId.msg == ShowAtTheEndMsgId) + : ((aroundId.fullId.msg == ShowAtTheEndMsgId) + || (aroundId == MaxMessagePosition)) ? (ServerMaxMsgId - 1) : (aroundId.fullId.peer == history->peer->id) ? aroundId.fullId.msg diff --git a/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp b/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp index 4c4fce6b47de8..3ab2ada3249f8 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp @@ -19,6 +19,7 @@ For license and copyright information please follow this link: #include "data/data_session.h" #include "data/data_thread.h" #include "history/view/reactions/history_view_reactions_button.h" +#include "history/view/history_view_corner_buttons.h" #include "history/view/history_view_list_widget.h" #include "history/history.h" #include "history/history_item.h" @@ -52,7 +53,8 @@ namespace { class Item final : public Ui::Menu::ItemBase - , private HistoryView::ListDelegate { + , private ListDelegate + , private CornerButtonsDelegate { public: Item(not_null parent, not_null thread); @@ -73,6 +75,7 @@ class Item final void setupHistory(); void updateInnerVisibleArea(); + // ListDelegate delegate. Context listContext() override; bool listScrollTo(int top, bool syntetic = true) override; void listCancelRequest() override; @@ -164,6 +167,16 @@ class Item final std::unique_ptr data, Fn finished) override; + // CornerButtonsDelegate delegate. + void cornerButtonsShowAtPosition( + Data::MessagePosition position) override; + Data::Thread *cornerButtonsThread() override; + FullMsgId cornerButtonsCurrentId() override; + bool cornerButtonsIgnoreVisibility() override; + std::optional cornerButtonsDownShown() override; + bool cornerButtonsUnreadMayBeShown() override; + bool cornerButtonsHas(CornerButtonType type) override; + const not_null _dummyAction; const not_null _session; const not_null _thread; @@ -176,7 +189,8 @@ class Item final const std::unique_ptr _scroll; const std::unique_ptr _markRead; - QPointer _inner; + QPointer _inner; + std::unique_ptr _cornerButtons; rpl::event_stream _actions; QImage _bg; @@ -446,11 +460,16 @@ void Item::setupHistory() { this, _session, static_cast(this))); + _cornerButtons = std::make_unique( + _scroll.get(), + _chatStyle.get(), + static_cast(this)); _markRead->shownValue() | rpl::start_with_next([=](bool shown) { const auto top = _top->height(); const auto bottom = shown ? _markRead->height() : 0; _scroll->setGeometry(rect().marginsRemoved({ 0, top, 0, bottom })); + _cornerButtons->updatePositions(); }, _markRead->lifetime()); _scroll->scrolls( @@ -495,6 +514,7 @@ void Item::paintEvent(QPaintEvent *e) { void Item::updateInnerVisibleArea() { const auto scrollTop = _scroll->scrollTop(); _inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height()); + _cornerButtons->updateJumpDownVisibility(); } Context Item::listContext() { @@ -592,18 +612,28 @@ MessagesBarData Item::listMessagesBar( return {}; } + auto skipped = false; const auto hidden = _replies && (repliesTill < 2); for (auto i = 0, count = int(elements.size()); i != count; ++i) { const auto item = elements[i]->data(); - if (!item->isRegular() - || item->out() - || (_replies && !item->replyToId())) { + if (!item->isRegular() || (_replies && !item->replyToId())) { continue; } const auto inHistory = (item->history() == _history); - if ((_replies && item->id > repliesTill) + const auto unread = (_replies && item->id > repliesTill) || (migratedTill && (inHistory || item->id > migratedTill)) - || (historyTill && inHistory && item->id > historyTill)) { + || (historyTill && inHistory && item->id > historyTill); + if (!unread) { + skipped = true; + } + if (item->out()) { + continue; + } + if (unread) { + if (!skipped) { + // Don't show jumping unread bar if scrolling up from bottom. + return {}; + } return { .bar = { .element = elements[i], @@ -800,6 +830,46 @@ void Item::listLaunchDrag( Fn finished) { } +void Item::cornerButtonsShowAtPosition(Data::MessagePosition position) { + if (position == Data::UnreadMessagePosition) { + position = Data::MaxMessagePosition; + } + _inner->showAtPosition( + position, + {}, + _cornerButtons->doneJumpFrom(position.fullId, {}, true)); +} + +Data::Thread *Item::cornerButtonsThread() { + return _thread; +} + +FullMsgId Item::cornerButtonsCurrentId() { + return {}; +} + +bool Item::cornerButtonsIgnoreVisibility() { + return false; +} + +std::optional Item::cornerButtonsDownShown() { + const auto top = _scroll->scrollTop() + st::historyToDownShownAfter; + if (top < _scroll->scrollTopMax()) { + return true; + } else if (_inner->loadedAtBottomKnown()) { + return !_inner->loadedAtBottom(); + } + return std::nullopt; +} + +bool Item::cornerButtonsUnreadMayBeShown() { + return _inner->loadedAtBottomKnown(); +} + +bool Item::cornerButtonsHas(CornerButtonType type) { + return (type == CornerButtonType::Down); +} + } // namespace ChatPreview MakeChatPreview( diff --git a/Telegram/SourceFiles/history/view/history_view_corner_buttons.cpp b/Telegram/SourceFiles/history/view/history_view_corner_buttons.cpp index 0ea99bc500465..37885d8e0e5b9 100644 --- a/Telegram/SourceFiles/history/view/history_view_corner_buttons.cpp +++ b/Telegram/SourceFiles/history/view/history_view_corner_buttons.cpp @@ -7,9 +7,10 @@ For license and copyright information please follow this link: */ #include "history/view/history_view_corner_buttons.h" -#include "ui/widgets/scroll_area.h" #include "ui/chat/chat_style.h" #include "ui/controls/jump_down_button.h" +#include "ui/widgets/elastic_scroll.h" +#include "ui/widgets/scroll_area.h" #include "base/qt/qt_key_modifiers.h" #include "history/history.h" #include "history/history_item.h" @@ -33,17 +34,41 @@ CornerButtons::CornerButtons( not_null parent, not_null st, not_null delegate) -: _scroll(parent) +: CornerButtons( + parent, + [=](QEvent *e) { return parent->viewportEvent(e); }, + st, + delegate) { +} + +CornerButtons::CornerButtons( + not_null parent, + not_null st, + not_null delegate) +: CornerButtons( + parent, + [=](QEvent *e) { return parent->viewportEvent(e); }, + st, + delegate) { +} + +CornerButtons::CornerButtons( + not_null parent, + Fn scrollViewportEvent, + not_null st, + not_null delegate) +: _parent(parent) +, _scrollViewportEvent(std::move(scrollViewportEvent)) , _delegate(delegate) , _down( parent, - st->value(parent->lifetime(), st::historyToDown)) + st->value(_stLifetime, st::historyToDown)) , _mentions( parent, - st->value(parent->lifetime(), st::historyUnreadMentions)) + st->value(_stLifetime, st::historyUnreadMentions)) , _reactions( parent, - st->value(parent->lifetime(), st::historyUnreadReactions)) { + st->value(_stLifetime, st::historyUnreadReactions)) { _down.widget->addClickHandler([=] { downClick(); }); _mentions.widget->addClickHandler([=] { mentionsClick(); }); _reactions.widget->addClickHandler([=] { reactionsClick(); }); @@ -68,7 +93,7 @@ bool CornerButtons::eventFilter(QObject *o, QEvent *e) { && (o == _down.widget || o == _mentions.widget || o == _reactions.widget)) { - return _scroll->viewportEvent(e); + return _scrollViewportEvent(e); } return QObject::eventFilter(o, e); } @@ -200,9 +225,7 @@ void CornerButtons::showAt(MsgId id) { } } -void CornerButtons::updateVisibility( - CornerButtonType type, - bool shown) { +void CornerButtons::updateVisibility(Type type, bool shown) { auto &button = buttonByType(type); if (button.shown != shown) { button.shown = shown; @@ -291,7 +314,7 @@ void CornerButtons::updatePositions() { historyDownShown); _down.widget->moveToRight( st::historyToDownPosition.x(), - _scroll->height() - top); + _parent->height() - top); } { const auto right = anim::interpolate( @@ -302,7 +325,7 @@ void CornerButtons::updatePositions() { 0, _down.widget->height() + skip, historyDownShown); - const auto top = _scroll->height() + const auto top = _parent->height() - _mentions.widget->height() - st::historyToDownPosition.y() - shift; @@ -321,7 +344,7 @@ void CornerButtons::updatePositions() { 0, _mentions.widget->height() + skip, unreadMentionsShown); - const auto top = _scroll->height() + const auto top = _parent->height() - _reactions.widget->height() - st::historyToDownPosition.y() - shift; @@ -355,7 +378,7 @@ Fn CornerButtons::doneJumpFrom( } if (!found && !ignoreMessageNotFound) { Ui::Toast::Show( - _scroll.get(), + _parent.get(), tr::lng_message_not_found(tr::now)); } }; diff --git a/Telegram/SourceFiles/history/view/history_view_corner_buttons.h b/Telegram/SourceFiles/history/view/history_view_corner_buttons.h index 03181813109f3..ae7b5b110cced 100644 --- a/Telegram/SourceFiles/history/view/history_view_corner_buttons.h +++ b/Telegram/SourceFiles/history/view/history_view_corner_buttons.h @@ -17,6 +17,7 @@ struct FullMsgId; namespace Ui { class ChatStyle; class ScrollArea; +class ElasticScroll; class JumpDownButton; } // namespace Ui @@ -61,6 +62,10 @@ class CornerButtons final : private QObject { not_null parent, not_null st, not_null delegate); + CornerButtons( + not_null parent, + not_null st, + not_null delegate); using Type = CornerButtonType; @@ -91,6 +96,12 @@ class CornerButtons final : private QObject { bool ignoreMessageNotFound = false); private: + CornerButtons( + not_null parent, + Fn scrollViewportEvent, + not_null st, + not_null delegate); + bool eventFilter(QObject *o, QEvent *e) override; void computeCurrentReplyReturn(); @@ -99,9 +110,12 @@ class CornerButtons final : private QObject { [[nodiscard]] History *lookupHistory() const; void showAt(MsgId id); - const not_null _scroll; + const not_null _parent; + const Fn _scrollViewportEvent; const not_null _delegate; + rpl::lifetime _stLifetime; + CornerButton _down; CornerButton _mentions; CornerButton _reactions;