Support unread mentions / reactions in topics.

This commit is contained in:
John Preston 2022-10-10 16:46:13 +04:00
parent 6a7f030ee7
commit 3999bca823
15 changed files with 229 additions and 175 deletions

View file

@ -128,12 +128,12 @@ void UnreadThings::requestMentions(
MTP_int(maxId), MTP_int(maxId),
MTP_int(minId) MTP_int(minId)
)).done([=](const MTPmessages_Messages &result) { )).done([=](const MTPmessages_Messages &result) {
_mentionsRequests.remove(history); _mentionsRequests.remove(entry);
history->unreadMentions().addSlice(result, loaded); entry->unreadMentions().addSlice(result, loaded);
}).fail([=] { }).fail([=] {
_mentionsRequests.remove(history); _mentionsRequests.remove(entry);
}).send(); }).send();
_mentionsRequests.emplace(history, requestId); _mentionsRequests.emplace(entry, requestId);
} }
void UnreadThings::requestReactions( void UnreadThings::requestReactions(
@ -162,12 +162,12 @@ void UnreadThings::requestReactions(
MTP_int(maxId), MTP_int(maxId),
MTP_int(minId) MTP_int(minId)
)).done([=](const MTPmessages_Messages &result) { )).done([=](const MTPmessages_Messages &result) {
_reactionsRequests.remove(history); _reactionsRequests.remove(entry);
history->unreadReactions().addSlice(result, loaded); entry->unreadReactions().addSlice(result, loaded);
}).fail([=] { }).fail([=] {
_reactionsRequests.remove(history); _reactionsRequests.remove(entry);
}).send(); }).send();
_reactionsRequests.emplace(history, requestId); _reactionsRequests.emplace(entry, requestId);
} }
} // namespace UnreadThings } // namespace UnreadThings

View file

@ -271,6 +271,7 @@ void Changes::sendNotifications() {
_historyChanges.sendNotifications(); _historyChanges.sendNotifications();
_messageChanges.sendNotifications(); _messageChanges.sendNotifications();
_entryChanges.sendNotifications(); _entryChanges.sendNotifications();
_topicChanges.sendNotifications();
} }
} // namespace Data } // namespace Data

View file

@ -914,7 +914,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
width(), width(),
std::min(st::msgMaxWidth / 2, width() / 2)); std::min(st::msgMaxWidth / 2, width() / 2));
const auto now = crl::now();
const auto historyDisplayedEmpty = _history->isDisplayedEmpty() const auto historyDisplayedEmpty = _history->isDisplayedEmpty()
&& (!_migrated || _migrated->isDisplayedEmpty()); && (!_migrated || _migrated->isDisplayedEmpty());
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) { if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
@ -1015,7 +1014,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
_widget->enqueueMessageHighlight(view); _widget->enqueueMessageHighlight(view);
} }
} }
session().data().reactions().poll(item, now); session().data().reactions().poll(item, context.now);
if (item->hasExtendedMediaPreview()) { if (item->hasExtendedMediaPreview()) {
session().api().views().pollExtendedMedia(item); session().api().views().pollExtendedMedia(item);
} }

View file

@ -5769,7 +5769,7 @@ bool HistoryWidget::cornerButtonsIgnoreVisibility() {
return _a_show.animating(); return _a_show.animating();
} }
bool HistoryWidget::cornerButtonsDownShown() { std::optional<bool> HistoryWidget::cornerButtonsDownShown() {
if (!_list || _firstLoadRequest) { if (!_list || _firstLoadRequest) {
return false; return false;
} }

View file

@ -328,7 +328,7 @@ private:
Dialogs::Entry *cornerButtonsEntry() override; Dialogs::Entry *cornerButtonsEntry() override;
FullMsgId cornerButtonsCurrentId() override; FullMsgId cornerButtonsCurrentId() override;
bool cornerButtonsIgnoreVisibility() override; bool cornerButtonsIgnoreVisibility() override;
bool cornerButtonsDownShown() override; std::optional<bool> cornerButtonsDownShown() override;
bool cornerButtonsUnreadMayBeShown() override; bool cornerButtonsUnreadMayBeShown() override;
void checkSuggestToGigagroup(); void checkSuggestToGigagroup();

View file

@ -251,8 +251,9 @@ void CornerButtons::updateUnreadThingsVisibility() {
} }
void CornerButtons::updateJumpDownVisibility(std::optional<int> counter) { void CornerButtons::updateJumpDownVisibility(std::optional<int> counter) {
const auto shown = _delegate->cornerButtonsDownShown(); if (const auto shown = _delegate->cornerButtonsDownShown()) {
updateVisibility(CornerButtonType::Down, shown); updateVisibility(CornerButtonType::Down, *shown);
}
if (counter) { if (counter) {
down.widget->setUnreadCount(*counter); down.widget->setUnreadCount(*counter);
} }

View file

@ -53,7 +53,7 @@ public:
[[nodiscard]] virtual Dialogs::Entry *cornerButtonsEntry() = 0; [[nodiscard]] virtual Dialogs::Entry *cornerButtonsEntry() = 0;
[[nodiscard]] virtual FullMsgId cornerButtonsCurrentId() = 0; [[nodiscard]] virtual FullMsgId cornerButtonsCurrentId() = 0;
[[nodiscard]] virtual bool cornerButtonsIgnoreVisibility() = 0; [[nodiscard]] virtual bool cornerButtonsIgnoreVisibility() = 0;
[[nodiscard]] virtual bool cornerButtonsDownShown() = 0; [[nodiscard]] virtual std::optional<bool> cornerButtonsDownShown() = 0;
[[nodiscard]] virtual bool cornerButtonsUnreadMayBeShown() = 0; [[nodiscard]] virtual bool cornerButtonsUnreadMayBeShown() = 0;
}; };

View file

@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/core_settings.h" #include "core/core_settings.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "api/api_who_reacted.h" #include "api/api_who_reacted.h"
#include "api/api_views.h"
#include "layout/layout_selection.h" #include "layout/layout_selection.h"
#include "window/section_widget.h" #include "window/section_widget.h"
#include "window/window_adaptive.h" #include "window/window_adaptive.h"
@ -54,6 +55,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/premium_preview_box.h" #include "boxes/premium_preview_box.h"
#include "boxes/peers/edit_participant_box.h" #include "boxes/peers/edit_participant_box.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_sponsored_messages.h"
#include "data/data_changes.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_document.h" #include "data/data_document.h"
@ -350,6 +353,13 @@ ListWidget::ListWidget(
itemRemoved(item); itemRemoved(item);
}, lifetime()); }, lifetime());
using MessageUpdateFlag = Data::MessageUpdate::Flag;
session().changes().realtimeMessageUpdates(
MessageUpdateFlag::NewUnreadReaction
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
maybeMarkReactionsRead(update.item);
}, lifetime());
session().data().itemVisibilityQueries( session().data().itemVisibilityQueries(
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](
const Data::Session::ItemVisibilityQuery &query) { const Data::Session::ItemVisibilityQuery &query) {
@ -787,7 +797,6 @@ void ListWidget::visibleTopBottomUpdated(
void ListWidget::applyUpdatedScrollState() { void ListWidget::applyUpdatedScrollState() {
checkMoveToOtherViewer(); checkMoveToOtherViewer();
_delegate->listVisibleItemsChanged(collectVisibleItems());
} }
void ListWidget::updateVisibleTopItem() { void ListWidget::updateVisibleTopItem() {
@ -1764,7 +1773,15 @@ void ListWidget::paintEvent(QPaintEvent *e) {
return; return;
} }
auto readTill = (HistoryItem*)nullptr;
auto readContents = base::flat_set<not_null<HistoryItem*>>();
const auto guard = gsl::finally([&] { const auto guard = gsl::finally([&] {
if (readTill) {
_delegate->listMarkReadTill(readTill);
}
if (!readContents.empty()) {
_delegate->listMarkContentsRead(readContents);
}
_userpicsCache.clear(); _userpicsCache.clear();
}); });
@ -1787,23 +1804,61 @@ void ListWidget::paintEvent(QPaintEvent *e) {
if (from != end(_items)) { if (from != end(_items)) {
_reactionsManager->startEffectsCollection(); _reactionsManager->startEffectsCollection();
const auto session = &controller()->session();
auto top = itemTop(from->get()); auto top = itemTop(from->get());
auto context = preparePaintContext(clip).translated(0, -top); auto context = preparePaintContext(clip).translated(0, -top);
p.translate(0, top); p.translate(0, top);
const auto &sendingAnimation = _controller->sendingAnimation(); const auto &sendingAnimation = _controller->sendingAnimation();
for (auto i = from; i != to; ++i) { for (auto i = from; i != to; ++i) {
const auto view = *i; const auto view = *i;
if (!sendingAnimation.hasAnimatedMessage(view->data())) { const auto item = view->data();
const auto height = view->height();
if (!sendingAnimation.hasAnimatedMessage(item)) {
context.reactionInfo context.reactionInfo
= _reactionsManager->currentReactionPaintInfo(); = _reactionsManager->currentReactionPaintInfo();
context.outbg = view->hasOutLayout(); context.outbg = view->hasOutLayout();
context.selection = itemRenderSelection(view); context.selection = itemRenderSelection(view);
view->draw(p, context); view->draw(p, context);
} }
const auto isSponsored = item->isSponsored();
const auto isUnread = _delegate->listElementShownUnread(view)
&& item->isRegular();
const auto withReaction = item->hasUnreadReaction();
const auto yShown = [&](int y) {
return (_visibleBottom >= y && _visibleTop <= y);
};
const auto markShown = isSponsored
? view->markSponsoredViewed(_visibleBottom - top)
: withReaction
? yShown(top + context.reactionInfo->position.y())
: isUnread
? yShown(top + height)
: yShown(top + height / 2);
if (markShown) {
if (isSponsored) {
session->data().sponsoredMessages().view(
item->fullId());
} else if (isUnread) {
readTill = item;
}
if (item->hasViews()) {
session->api().views().scheduleIncrement(item);
}
if (withReaction) {
readContents.insert(item);
} else if (item->isUnreadMention()
&& !item->isUnreadMedia()) {
readContents.insert(item);
_highlighter.enqueue(view);
}
}
session->data().reactions().poll(item, context.now);
if (item->hasExtendedMediaPreview()) {
session->api().views().pollExtendedMedia(item);
}
_reactionsManager->recordCurrentReactionEffect( _reactionsManager->recordCurrentReactionEffect(
view->data()->fullId(), item->fullId(),
QPoint(0, top)); QPoint(0, top));
const auto height = view->height();
top += height; top += height;
context.translate(0, -height); context.translate(0, -height);
p.translate(0, height); p.translate(0, height);
@ -1847,7 +1902,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
auto &v = _sponsoredUserpics[itemId.msg]; auto &v = _sponsoredUserpics[itemId.msg];
if (!info->customUserpic.isCurrentView(v)) { if (!info->customUserpic.isCurrentView(v)) {
v = info->customUserpic.createView(); v = info->customUserpic.createView();
info->customUserpic.load(&session(), itemId); info->customUserpic.load(session, itemId);
} }
} }
} }
@ -1909,6 +1964,21 @@ void ListWidget::paintEvent(QPaintEvent *e) {
} }
} }
void ListWidget::maybeMarkReactionsRead(not_null<HistoryItem*> item) {
const auto view = viewForItem(item);
if (!view) {
return;
}
const auto top = itemTop(view);
const auto reactionCenter
= view->reactionButtonParameters({}, {}).center.y();
if (top + reactionCenter < _visibleTop
|| top + view->height() > _visibleBottom) {
return;
}
_delegate->listMarkContentsRead({ item });
}
bool ListWidget::eventHook(QEvent *e) { bool ListWidget::eventHook(QEvent *e) {
if (e->type() == QEvent::TouchBegin if (e->type() == QEvent::TouchBegin
|| e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchUpdate

View file

@ -100,7 +100,9 @@ public:
not_null<HistoryItem*> first, not_null<HistoryItem*> first,
not_null<HistoryItem*> second) = 0; not_null<HistoryItem*> second) = 0;
virtual void listSelectionChanged(SelectedItems &&items) = 0; virtual void listSelectionChanged(SelectedItems &&items) = 0;
virtual void listVisibleItemsChanged(HistoryItemsList &&items) = 0; virtual void listMarkReadTill(not_null<HistoryItem*> item) = 0;
virtual void listMarkContentsRead(
const base::flat_set<not_null<HistoryItem*>> &items) = 0;
virtual MessagesBarData listMessagesBar( virtual MessagesBarData listMessagesBar(
const std::vector<not_null<Element*>> &elements) = 0; const std::vector<not_null<Element*>> &elements) = 0;
virtual void listContentRefreshed() = 0; virtual void listContentRefreshed() = 0;
@ -529,6 +531,7 @@ private:
void scrollToAnimationCallback(FullMsgId attachToId, int relativeTo); void scrollToAnimationCallback(FullMsgId attachToId, int relativeTo);
void startItemRevealAnimations(); void startItemRevealAnimations();
void revealItemsCallback(); void revealItemsCallback();
void maybeMarkReactionsRead(not_null<HistoryItem*> item);
void startMessageSendingAnimation(not_null<HistoryItem*> item); void startMessageSendingAnimation(not_null<HistoryItem*> item);
void showPremiumStickerTooltip( void showPremiumStickerTooltip(

View file

@ -636,7 +636,11 @@ void PinnedWidget::listSelectionChanged(SelectedItems &&items) {
_topBar->showSelected(state); _topBar->showSelected(state);
} }
void PinnedWidget::listVisibleItemsChanged(HistoryItemsList &&items) { void PinnedWidget::listMarkReadTill(not_null<HistoryItem*> item) {
}
void PinnedWidget::listMarkContentsRead(
const base::flat_set<not_null<HistoryItem*>> &items) {
} }
MessagesBarData PinnedWidget::listMessagesBar( MessagesBarData PinnedWidget::listMessagesBar(

View file

@ -90,7 +90,9 @@ public:
not_null<HistoryItem*> first, not_null<HistoryItem*> first,
not_null<HistoryItem*> second) override; not_null<HistoryItem*> second) override;
void listSelectionChanged(SelectedItems &&items) override; void listSelectionChanged(SelectedItems &&items) override;
void listVisibleItemsChanged(HistoryItemsList &&items) override; void listMarkReadTill(not_null<HistoryItem*> item) override;
void listMarkContentsRead(
const base::flat_set<not_null<HistoryItem*>> &items) override;
MessagesBarData listMessagesBar( MessagesBarData listMessagesBar(
const std::vector<not_null<Element*>> &elements) override; const std::vector<not_null<Element*>> &elements) override;
void listContentRefreshed() override; void listContentRefreshed() override;

View file

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_schedule_box.h" #include "history/view/history_view_schedule_box.h"
#include "history/view/history_view_pinned_bar.h" #include "history/view/history_view_pinned_bar.h"
#include "history/view/history_view_sticker_toast.h" #include "history/view/history_view_sticker_toast.h"
#include "history/view/history_view_cursor_state.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_drag_area.h" #include "history/history_drag_area.h"
#include "history/history_item_components.h" #include "history/history_item_components.h"
@ -212,9 +213,10 @@ RepliesWidget::RepliesWidget(
this, this,
controller->chatStyle()->value(lifetime(), st::historyScroll), controller->chatStyle()->value(lifetime(), st::historyScroll),
false)) false))
, _scrollDown( , _cornerButtons(
_scroll.get(), _scroll.get(),
controller->chatStyle()->value(lifetime(), st::historyToDown)) controller->chatStyle(),
static_cast<HistoryView::CornerButtonsDelegate*>(this))
, _readRequestTimer([=] { sendReadTillRequest(); }) { , _readRequestTimer([=] { sendReadTillRequest(); }) {
controller->chatStyle()->paletteChanged( controller->chatStyle()->paletteChanged(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -335,8 +337,8 @@ RepliesWidget::RepliesWidget(
controller->showBackFromStack(); controller->showBackFromStack();
} }
} }
while (update.item == _replyReturn) { while (update.item == _cornerButtons.replyReturn()) {
calculateNextReplyReturn(); _cornerButtons.calculateNextReplyReturn();
} }
}, lifetime()); }, lifetime());
@ -347,7 +349,6 @@ RepliesWidget::RepliesWidget(
_inner->update(); _inner->update();
}, lifetime()); }, lifetime());
setupScrollDownButton();
setupComposeControls(); setupComposeControls();
orderWidgets(); orderWidgets();
} }
@ -469,14 +470,34 @@ void RepliesWidget::setupTopicViewer() {
_inner->update(); _inner->update();
} }
}, lifetime()); }, lifetime());
if (_topic) {
subscribeToTopic();
}
}
void RepliesWidget::subscribeToTopic() {
using TopicUpdateFlag = Data::TopicUpdate::Flag;
session().changes().topicUpdates(
_topic,
(TopicUpdateFlag::UnreadMentions
| TopicUpdateFlag::UnreadReactions)
) | rpl::start_with_next([=](const Data::TopicUpdate &update) {
_cornerButtons.updateUnreadThingsVisibility();
}, _topicLifetime);
} }
void RepliesWidget::setTopic(Data::ForumTopic *topic) { void RepliesWidget::setTopic(Data::ForumTopic *topic) {
if (_topic != topic) { if (_topic == topic) {
_topic = topic; return;
refreshReplies(); }
refreshTopBarActiveChat(); _topicLifetime.destroy();
if (_topic && _rootView) { _topic = topic;
refreshReplies();
refreshTopBarActiveChat();
if (_topic) {
subscribeToTopic();
if (_rootView) {
_rootView = nullptr; _rootView = nullptr;
_rootViewHeight = 0; _rootViewHeight = 0;
updateControlsGeometry(); updateControlsGeometry();
@ -684,7 +705,8 @@ void RepliesWidget::setupComposeControls() {
_composeControls->lockShowStarts( _composeControls->lockShowStarts(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
updateScrollDownVisibility(); _cornerButtons.updateJumpDownVisibility();
_cornerButtons.updateUnreadThingsVisibility();
}, lifetime()); }, lifetime());
_composeControls->viewportEvents( _composeControls->viewportEvents(
@ -919,48 +941,21 @@ std::optional<QString> RepliesWidget::writeRestriction() const {
void RepliesWidget::pushReplyReturn(not_null<HistoryItem*> item) { void RepliesWidget::pushReplyReturn(not_null<HistoryItem*> item) {
if (item->history() == _history && item->inThread(_rootId)) { if (item->history() == _history && item->inThread(_rootId)) {
_replyReturns.push_back(item->id); _cornerButtons.pushReplyReturn(item);
} else {
return;
}
_replyReturn = item;
updateScrollDownVisibility();
}
void RepliesWidget::restoreReplyReturns(const std::vector<MsgId> &list) {
_replyReturns = list;
computeCurrentReplyReturn();
if (!_replyReturn) {
calculateNextReplyReturn();
}
}
void RepliesWidget::computeCurrentReplyReturn() {
_replyReturn = _replyReturns.empty()
? nullptr
: _history->owner().message(_history->peer, _replyReturns.back());
}
void RepliesWidget::calculateNextReplyReturn() {
_replyReturn = nullptr;
while (!_replyReturns.empty() && !_replyReturn) {
_replyReturns.pop_back();
computeCurrentReplyReturn();
}
if (!_replyReturn) {
updateScrollDownVisibility();
} }
} }
void RepliesWidget::checkReplyReturns() { void RepliesWidget::checkReplyReturns() {
const auto currentTop = _scroll->scrollTop(); const auto currentTop = _scroll->scrollTop();
for (; _replyReturn != nullptr; calculateNextReplyReturn()) { while (const auto replyReturn = _cornerButtons.replyReturn()) {
const auto position = _replyReturn->position(); const auto position = replyReturn->position();
const auto scrollTop = _inner->scrollTopForPosition(position); const auto scrollTop = _inner->scrollTopForPosition(position);
const auto scrolledBelow = scrollTop const auto below = scrollTop
? (currentTop >= std::min(*scrollTop, _scroll->scrollTopMax())) ? (currentTop >= std::min(*scrollTop, _scroll->scrollTopMax()))
: _inner->isBelowPosition(position); : _inner->isBelowPosition(position);
if (!scrolledBelow) { if (below) {
_cornerButtons.calculateNextReplyReturn();
} else {
break; break;
} }
} }
@ -1057,6 +1052,10 @@ void RepliesWidget::send(Api::SendOptions options) {
return; return;
} }
if (!options.scheduled) {
_cornerButtons.clearReplyReturns();
}
const auto webPageId = _composeControls->webPageId(); const auto webPageId = _composeControls->webPageId();
auto message = ApiWrap::MessageToSend(prepareSendAction(options)); auto message = ApiWrap::MessageToSend(prepareSendAction(options));
@ -1355,24 +1354,9 @@ MsgId RepliesWidget::replyToId() const {
: _rootId; : _rootId;
} }
void RepliesWidget::setupScrollDownButton() {
_scrollDown->setClickedCallback([=] {
scrollDownClicked();
});
base::install_event_filter(_scrollDown, [=](not_null<QEvent*> event) {
if (event->type() != QEvent::Wheel) {
return base::EventFilterResult::Continue;
}
return _scroll->viewportEvent(event)
? base::EventFilterResult::Cancel
: base::EventFilterResult::Continue;
});
updateScrollDownVisibility();
}
void RepliesWidget::refreshUnreadCountBadge(std::optional<int> count) { void RepliesWidget::refreshUnreadCountBadge(std::optional<int> count) {
if (count.has_value()) { if (count.has_value()) {
_scrollDown->setUnreadCount(*count); _cornerButtons.updateJumpDownVisibility(count);
} else if (!_readRequestTimer.isActive() && !_readRequestId) { } else if (!_readRequestTimer.isActive() && !_readRequestId) {
reloadUnreadCountIfNeeded(); reloadUnreadCountIfNeeded();
} }
@ -1389,14 +1373,39 @@ void RepliesWidget::reloadUnreadCountIfNeeded() {
} }
} }
void RepliesWidget::scrollDownClicked() { void RepliesWidget::cornerButtonsShowAtPosition(
if (base::IsCtrlPressed()) { Data::MessagePosition position) {
showAtEnd(); showAtPosition(position);
} else if (_replyReturn) { }
showAtPosition(_replyReturn->position());
} else { Dialogs::Entry *RepliesWidget::cornerButtonsEntry() {
showAtEnd(); return _topic;
}
FullMsgId RepliesWidget::cornerButtonsCurrentId() {
return _lastShownAt;
}
bool RepliesWidget::cornerButtonsIgnoreVisibility() {
return animatingShow();
}
std::optional<bool> RepliesWidget::cornerButtonsDownShown() {
if (_composeControls->isLockPresent()) {
return false;
} }
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
if (top < _scroll->scrollTopMax() || _cornerButtons.replyReturn()) {
return true;
} else if (_inner->loadedAtBottomKnown()) {
return !_inner->loadedAtBottom();
}
return std::nullopt;
}
bool RepliesWidget::cornerButtonsUnreadMayBeShown() {
return _inner->loadedAtBottomKnown()
&& !_composeControls->isLockPresent();
} }
void RepliesWidget::showAtStart() { void RepliesWidget::showAtStart() {
@ -1435,9 +1444,7 @@ bool RepliesWidget::showAtPositionNow(
: nullptr; : nullptr;
const auto use = item ? item->position() : position; const auto use = item ? item->position() : position;
if (const auto scrollTop = _inner->scrollTopForPosition(use)) { if (const auto scrollTop = _inner->scrollTopForPosition(use)) {
while (_replyReturn && use.fullId.msg == _replyReturn->id) { _cornerButtons.skipReplyReturn(use.fullId);
calculateNextReplyReturn();
}
const auto currentScrollTop = _scroll->scrollTop(); const auto currentScrollTop = _scroll->scrollTop();
const auto wanted = std::clamp( const auto wanted = std::clamp(
*scrollTop, *scrollTop,
@ -1452,6 +1459,7 @@ bool RepliesWidget::showAtPositionNow(
? AnimatedScroll::Part ? AnimatedScroll::Part
: AnimatedScroll::Full; : AnimatedScroll::Full;
_inner->scrollTo(wanted, use, scrollDelta, type); _inner->scrollTo(wanted, use, scrollDelta, type);
_lastShownAt = use.fullId;
if (use != Data::MaxMessagePosition if (use != Data::MaxMessagePosition
&& use != Data::UnreadMessagePosition) { && use != Data::UnreadMessagePosition) {
_inner->highlightMessage(use.fullId); _inner->highlightMessage(use.fullId);
@ -1464,57 +1472,6 @@ bool RepliesWidget::showAtPositionNow(
return false; return false;
} }
void RepliesWidget::updateScrollDownVisibility() {
if (animatingShow()) {
return;
}
const auto scrollDownIsVisible = [&]() -> std::optional<bool> {
if (_composeControls->isLockPresent()) {
return false;
}
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
if (top < _scroll->scrollTopMax() || _replyReturn) {
return true;
} else if (_inner->loadedAtBottomKnown()) {
return !_inner->loadedAtBottom();
}
return std::nullopt;
};
const auto scrollDownIsShown = scrollDownIsVisible();
if (!scrollDownIsShown) {
return;
}
if (_scrollDownIsShown != *scrollDownIsShown) {
_scrollDownIsShown = *scrollDownIsShown;
_scrollDownShown.start(
[=] { updateScrollDownPosition(); },
_scrollDownIsShown ? 0. : 1.,
_scrollDownIsShown ? 1. : 0.,
st::historyToDownDuration);
}
}
void RepliesWidget::updateScrollDownPosition() {
// _scrollDown is a child widget of _scroll, not me.
auto top = anim::interpolate(
0,
_scrollDown->height() + st::historyToDownPosition.y(),
_scrollDownShown.value(_scrollDownIsShown ? 1. : 0.));
_scrollDown->moveToRight(
st::historyToDownPosition.x(),
_scroll->height() - top);
auto shouldBeHidden = !_scrollDownIsShown && !_scrollDownShown.animating();
if (shouldBeHidden != _scrollDown->isHidden()) {
_scrollDown->setVisible(!shouldBeHidden);
}
}
void RepliesWidget::scrollDownAnimationFinish() {
_scrollDownShown.stop();
updateScrollDownPosition();
}
void RepliesWidget::updateAdaptiveLayout() { void RepliesWidget::updateAdaptiveLayout() {
_topBarShadow->moveToLeft( _topBarShadow->moveToLeft(
controller()->adaptive().isOneColumn() ? 0 : st::lineWidth, controller()->adaptive().isOneColumn() ? 0 : st::lineWidth,
@ -1662,7 +1619,8 @@ bool RepliesWidget::showMessage(
if (!originMessage) { if (!originMessage) {
return false; return false;
} }
const auto originItem = (!originMessage || _replyReturn == originMessage) const auto originItem = (!originMessage
|| _cornerButtons.replyReturn() == originMessage)
? nullptr ? nullptr
: originMessage; : originMessage;
showAtPosition(message->position(), originItem); showAtPosition(message->position(), originItem);
@ -1685,7 +1643,7 @@ void RepliesWidget::replyToMessage(FullMsgId itemId) {
void RepliesWidget::saveState(not_null<RepliesMemento*> memento) { void RepliesWidget::saveState(not_null<RepliesMemento*> memento) {
memento->setReplies(_replies); memento->setReplies(_replies);
memento->setReplyReturns(_replyReturns); memento->setReplyReturns(_cornerButtons.replyReturns());
_inner->saveState(memento->list()); _inner->saveState(memento->list());
} }
@ -1740,7 +1698,7 @@ void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
} else if (!_replies) { } else if (!_replies) {
refreshReplies(); refreshReplies();
} }
restoreReplyReturns(memento->replyReturns()); _cornerButtons.setReplyReturns(memento->replyReturns());
_inner->restoreState(memento->list()); _inner->restoreState(memento->list());
if (const auto highlight = memento->getHighlightId()) { if (const auto highlight = memento->getHighlightId()) {
const auto position = Data::MessagePosition{ const auto position = Data::MessagePosition{
@ -1811,7 +1769,7 @@ void RepliesWidget::updateControlsGeometry() {
_composeControls->move(0, bottom - controlsHeight); _composeControls->move(0, bottom - controlsHeight);
_composeControls->setAutocompleteBoundingRect(_scroll->geometry()); _composeControls->setAutocompleteBoundingRect(_scroll->geometry());
updateScrollDownPosition(); _cornerButtons.updatePositions();
} }
void RepliesWidget::paintEvent(QPaintEvent *e) { void RepliesWidget::paintEvent(QPaintEvent *e) {
@ -1842,7 +1800,8 @@ void RepliesWidget::updateInnerVisibleArea() {
const auto scrollTop = _scroll->scrollTop(); const auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height()); _inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
updatePinnedVisibility(); updatePinnedVisibility();
updateScrollDownVisibility(); _cornerButtons.updateJumpDownVisibility();
_cornerButtons.updateUnreadThingsVisibility();
} }
void RepliesWidget::updatePinnedVisibility() { void RepliesWidget::updatePinnedVisibility() {
@ -2024,11 +1983,16 @@ void RepliesWidget::readTill(not_null<HistoryItem*> item) {
} }
} }
void RepliesWidget::listVisibleItemsChanged(HistoryItemsList &&items) { void RepliesWidget::listMarkReadTill(not_null<HistoryItem*> item) {
const auto reversed = ranges::views::reverse(items); if (true/*doWeReadServerHistory()*/) { // #TODO forum active
const auto good = ranges::find_if(reversed, &HistoryItem::isRegular); readTill(item);
if (good != end(reversed)) { }
readTill(*good); }
void RepliesWidget::listMarkContentsRead(
const base::flat_set<not_null<HistoryItem*>> &items) {
if (true/*doWeReadMentions()*/) { // #TODO forum active
session().api().markContentsRead(items);
} }
} }

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/section_widget.h" #include "window/section_widget.h"
#include "window/section_memento.h" #include "window/section_memento.h"
#include "history/view/history_view_corner_buttons.h"
#include "history/view/history_view_list_widget.h" #include "history/view/history_view_list_widget.h"
#include "data/data_messages.h" #include "data/data_messages.h"
#include "base/timer.h" #include "base/timer.h"
@ -67,7 +68,8 @@ class StickerToast;
class RepliesWidget final class RepliesWidget final
: public Window::SectionWidget : public Window::SectionWidget
, private ListDelegate { , private ListDelegate
, private CornerButtonsDelegate {
public: public:
RepliesWidget( RepliesWidget(
QWidget *parent, QWidget *parent,
@ -128,7 +130,9 @@ public:
not_null<HistoryItem*> first, not_null<HistoryItem*> first,
not_null<HistoryItem*> second) override; not_null<HistoryItem*> second) override;
void listSelectionChanged(SelectedItems &&items) override; void listSelectionChanged(SelectedItems &&items) override;
void listVisibleItemsChanged(HistoryItemsList &&items) override; void listMarkReadTill(not_null<HistoryItem*> item) override;
void listMarkContentsRead(
const base::flat_set<not_null<HistoryItem*>> &items) override;
MessagesBarData listMessagesBar( MessagesBarData listMessagesBar(
const std::vector<not_null<Element*>> &elements) override; const std::vector<not_null<Element*>> &elements) override;
void listContentRefreshed() override; void listContentRefreshed() override;
@ -147,6 +151,15 @@ public:
->rpl::producer<Data::AllowedReactions> override; ->rpl::producer<Data::AllowedReactions> override;
void listShowPremiumToast(not_null<DocumentData*> document) override; void listShowPremiumToast(not_null<DocumentData*> document) override;
// CornerButtonsDelegate delegate.
void cornerButtonsShowAtPosition(
Data::MessagePosition position) override;
Dialogs::Entry *cornerButtonsEntry() override;
FullMsgId cornerButtonsCurrentId() override;
bool cornerButtonsIgnoreVisibility() override;
std::optional<bool> cornerButtonsDownShown() override;
bool cornerButtonsUnreadMayBeShown() override;
protected: protected:
void resizeEvent(QResizeEvent *e) override; void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
@ -181,16 +194,13 @@ private:
void setupRoot(); void setupRoot();
void setupRootView(); void setupRootView();
void setupTopicViewer(); void setupTopicViewer();
void subscribeToTopic();
void setTopic(Data::ForumTopic *topic); void setTopic(Data::ForumTopic *topic);
void setupDragArea(); void setupDragArea();
void sendReadTillRequest(); void sendReadTillRequest();
void readTill(not_null<HistoryItem*> item); void readTill(not_null<HistoryItem*> item);
void setupScrollDownButton();
void scrollDownClicked();
void scrollDownAnimationFinish(); void scrollDownAnimationFinish();
void updateScrollDownVisibility();
void updateScrollDownPosition();
void updatePinnedVisibility(); void updatePinnedVisibility();
void confirmDeleteSelected(); void confirmDeleteSelected();
@ -216,9 +226,6 @@ private:
void orderWidgets(); void orderWidgets();
void pushReplyReturn(not_null<HistoryItem*> item); void pushReplyReturn(not_null<HistoryItem*> item);
void computeCurrentReplyReturn();
void calculateNextReplyReturn();
void restoreReplyReturns(const std::vector<MsgId> &list);
void checkReplyReturns(); void checkReplyReturns();
void recountChatWidth(); void recountChatWidth();
void replyToMessage(FullMsgId itemId); void replyToMessage(FullMsgId itemId);
@ -295,12 +302,9 @@ private:
std::unique_ptr<Ui::ScrollArea> _scroll; std::unique_ptr<Ui::ScrollArea> _scroll;
std::unique_ptr<HistoryView::StickerToast> _stickerToast; std::unique_ptr<HistoryView::StickerToast> _stickerToast;
std::vector<MsgId> _replyReturns; FullMsgId _lastShownAt;
HistoryItem *_replyReturn = nullptr; HistoryView::CornerButtons _cornerButtons;
rpl::lifetime _topicLifetime;
Ui::Animations::Simple _scrollDownShown;
bool _scrollDownIsShown = false;
object_ptr<Ui::HistoryDownButton> _scrollDown;
bool _choosingAttach = false; bool _choosingAttach = false;
@ -351,10 +355,10 @@ public:
return _replies; return _replies;
} }
void setReplyReturns(const std::vector<MsgId> &list) { void setReplyReturns(const QVector<FullMsgId> &list) {
_replyReturns = list; _replyReturns = list;
} }
const std::vector<MsgId> &replyReturns() const { const QVector<FullMsgId> &replyReturns() const {
return _replyReturns; return _replyReturns;
} }
@ -373,7 +377,7 @@ private:
const MsgId _highlightId = 0; const MsgId _highlightId = 0;
ListMemento _list; ListMemento _list;
std::shared_ptr<Data::RepliesList> _replies; std::shared_ptr<Data::RepliesList> _replies;
std::vector<MsgId> _replyReturns; QVector<FullMsgId> _replyReturns;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;

View file

@ -1258,7 +1258,11 @@ void ScheduledWidget::listSelectionChanged(SelectedItems &&items) {
_topBar->showSelected(state); _topBar->showSelected(state);
} }
void ScheduledWidget::listVisibleItemsChanged(HistoryItemsList &&items) { void ScheduledWidget::listMarkReadTill(not_null<HistoryItem*> item) {
}
void ScheduledWidget::listMarkContentsRead(
const base::flat_set<not_null<HistoryItem*>> &items) {
} }
MessagesBarData ScheduledWidget::listMessagesBar( MessagesBarData ScheduledWidget::listMessagesBar(

View file

@ -112,7 +112,9 @@ public:
not_null<HistoryItem*> first, not_null<HistoryItem*> first,
not_null<HistoryItem*> second) override; not_null<HistoryItem*> second) override;
void listSelectionChanged(SelectedItems &&items) override; void listSelectionChanged(SelectedItems &&items) override;
void listVisibleItemsChanged(HistoryItemsList &&items) override; void listMarkReadTill(not_null<HistoryItem*> item) override;
void listMarkContentsRead(
const base::flat_set<not_null<HistoryItem*>> &items) override;
MessagesBarData listMessagesBar( MessagesBarData listMessagesBar(
const std::vector<not_null<Element*>> &elements) override; const std::vector<not_null<Element*>> &elements) override;
void listContentRefreshed() override; void listContentRefreshed() override;