mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Add navigation through reply stack.
This commit is contained in:
parent
c563df7d9d
commit
f22a804220
4 changed files with 142 additions and 18 deletions
|
@ -415,6 +415,10 @@ void ListWidget::animatedScrollTo(
|
||||||
transition);
|
transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ListWidget::animatedScrolling() const {
|
||||||
|
return _scrollToAnimation.animating();
|
||||||
|
}
|
||||||
|
|
||||||
void ListWidget::scrollToAnimationCallback(
|
void ListWidget::scrollToAnimationCallback(
|
||||||
FullMsgId attachToId,
|
FullMsgId attachToId,
|
||||||
int relativeTo) {
|
int relativeTo) {
|
||||||
|
@ -495,9 +499,11 @@ void ListWidget::saveScrollState() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::restoreScrollState() {
|
void ListWidget::restoreScrollState() {
|
||||||
if (_items.empty()
|
if (_items.empty()) {
|
||||||
|| (_overrideInitialScroll
|
return;
|
||||||
&& base::take(_overrideInitialScroll)())) {
|
} else if (_overrideInitialScroll
|
||||||
|
&& base::take(_overrideInitialScroll)()) {
|
||||||
|
_scrollTopState = ScrollTopState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!_scrollTopState.item) {
|
if (!_scrollTopState.item) {
|
||||||
|
|
|
@ -161,6 +161,7 @@ public:
|
||||||
Data::MessagePosition attachPosition,
|
Data::MessagePosition attachPosition,
|
||||||
int delta,
|
int delta,
|
||||||
AnimatedScroll type);
|
AnimatedScroll type);
|
||||||
|
[[nodiscard]] bool animatedScrolling() const;
|
||||||
bool isAbovePosition(Data::MessagePosition position) const;
|
bool isAbovePosition(Data::MessagePosition position) const;
|
||||||
bool isBelowPosition(Data::MessagePosition position) const;
|
bool isBelowPosition(Data::MessagePosition position) const;
|
||||||
void highlightMessage(FullMsgId itemId);
|
void highlightMessage(FullMsgId itemId);
|
||||||
|
|
|
@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_replies_list.h"
|
#include "data/data_replies_list.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
#include "storage/storage_media_prepare.h"
|
#include "storage/storage_media_prepare.h"
|
||||||
#include "storage/storage_account.h"
|
#include "storage/storage_account.h"
|
||||||
#include "inline_bots/inline_bot_result.h"
|
#include "inline_bots/inline_bot_result.h"
|
||||||
|
@ -53,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
|
||||||
#include <QtCore/QMimeData>
|
#include <QtCore/QMimeData>
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -156,6 +158,16 @@ RepliesWidget::RepliesWidget(
|
||||||
}
|
}
|
||||||
}, _inner->lifetime());
|
}, _inner->lifetime());
|
||||||
|
|
||||||
|
_history->session().changes().messageUpdates(
|
||||||
|
Data::MessageUpdate::Flag::Destroyed
|
||||||
|
) | rpl::filter([=](const Data::MessageUpdate &update) {
|
||||||
|
return (update.item == _replyReturn);
|
||||||
|
}) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||||
|
while (update.item == _replyReturn) {
|
||||||
|
calculateNextReplyReturn();
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
setupScrollDownButton();
|
setupScrollDownButton();
|
||||||
setupComposeControls();
|
setupComposeControls();
|
||||||
}
|
}
|
||||||
|
@ -468,6 +480,57 @@ void RepliesWidget::uploadFilesAfterConfirmation(
|
||||||
action);
|
action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RepliesWidget::pushReplyReturn(not_null<HistoryItem*> item) {
|
||||||
|
if (item->history() == _history && item->replyToTop() == _rootId) {
|
||||||
|
_replyReturns.push_back(item->id);
|
||||||
|
} 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->channelId(),
|
||||||
|
_replyReturns.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesWidget::calculateNextReplyReturn() {
|
||||||
|
_replyReturn = nullptr;
|
||||||
|
while (!_replyReturns.empty() && !_replyReturn) {
|
||||||
|
_replyReturns.pop_back();
|
||||||
|
computeCurrentReplyReturn();
|
||||||
|
}
|
||||||
|
if (!_replyReturn) {
|
||||||
|
updateScrollDownVisibility();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesWidget::checkReplyReturns() {
|
||||||
|
const auto currentTop = _scroll->scrollTop();
|
||||||
|
for (; _replyReturn != nullptr; calculateNextReplyReturn()) {
|
||||||
|
const auto position = _replyReturn->position();
|
||||||
|
const auto scrollTop = _inner->scrollTopForPosition(position);
|
||||||
|
const auto scrolledBelow = scrollTop
|
||||||
|
? (currentTop >= std::min(*scrollTop, _scroll->scrollTopMax()))
|
||||||
|
: _inner->isBelowPosition(position);
|
||||||
|
if (!scrolledBelow) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RepliesWidget::uploadFile(
|
void RepliesWidget::uploadFile(
|
||||||
const QByteArray &fileContent,
|
const QByteArray &fileContent,
|
||||||
SendMediaType type) {
|
SendMediaType type) {
|
||||||
|
@ -776,19 +839,32 @@ void RepliesWidget::setupScrollDownButton() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::scrollDownClicked() {
|
void RepliesWidget::scrollDownClicked() {
|
||||||
showAtPosition(Data::MaxMessagePosition);
|
if (QGuiApplication::keyboardModifiers() == Qt::ControlModifier) {
|
||||||
|
showAtPosition(Data::MaxMessagePosition);
|
||||||
|
} else if (_replyReturn) {
|
||||||
|
showAtPosition(_replyReturn->position());
|
||||||
|
} else {
|
||||||
|
showAtPosition(Data::MaxMessagePosition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::showAtPosition(Data::MessagePosition position) {
|
void RepliesWidget::showAtPosition(
|
||||||
if (!showAtPositionNow(position)) {
|
Data::MessagePosition position,
|
||||||
|
HistoryItem *originItem) {
|
||||||
|
if (!showAtPositionNow(position, originItem)) {
|
||||||
_inner->showAroundPosition(position, [=] {
|
_inner->showAroundPosition(position, [=] {
|
||||||
return showAtPositionNow(position);
|
return showAtPositionNow(position, originItem);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RepliesWidget::showAtPositionNow(Data::MessagePosition position) {
|
bool RepliesWidget::showAtPositionNow(
|
||||||
|
Data::MessagePosition position,
|
||||||
|
HistoryItem *originItem) {
|
||||||
if (const auto scrollTop = _inner->scrollTopForPosition(position)) {
|
if (const auto scrollTop = _inner->scrollTopForPosition(position)) {
|
||||||
|
while (_replyReturn && position.fullId.msg == _replyReturn->id) {
|
||||||
|
calculateNextReplyReturn();
|
||||||
|
}
|
||||||
const auto currentScrollTop = _scroll->scrollTop();
|
const auto currentScrollTop = _scroll->scrollTop();
|
||||||
const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax());
|
const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax());
|
||||||
const auto fullDelta = (wanted - currentScrollTop);
|
const auto fullDelta = (wanted - currentScrollTop);
|
||||||
|
@ -801,8 +877,12 @@ bool RepliesWidget::showAtPositionNow(Data::MessagePosition position) {
|
||||||
(std::abs(fullDelta) > limit
|
(std::abs(fullDelta) > limit
|
||||||
? HistoryView::ListWidget::AnimatedScroll::Part
|
? HistoryView::ListWidget::AnimatedScroll::Part
|
||||||
: HistoryView::ListWidget::AnimatedScroll::Full));
|
: HistoryView::ListWidget::AnimatedScroll::Full));
|
||||||
if (const auto highlight = base::take(_highlightMessageId)) {
|
if (position != Data::MaxMessagePosition
|
||||||
_inner->highlightMessage(highlight);
|
&& position != Data::UnreadMessagePosition) {
|
||||||
|
_inner->highlightMessage(position.fullId);
|
||||||
|
}
|
||||||
|
if (originItem) {
|
||||||
|
pushReplyReturn(originItem);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -816,10 +896,9 @@ void RepliesWidget::updateScrollDownVisibility() {
|
||||||
|
|
||||||
const auto scrollDownIsVisible = [&]() -> std::optional<bool> {
|
const auto scrollDownIsVisible = [&]() -> std::optional<bool> {
|
||||||
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
|
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
|
||||||
if (top < _scroll->scrollTopMax()) {
|
if (top < _scroll->scrollTopMax() || _replyReturn) {
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if (_inner->loadedAtBottomKnown()) {
|
||||||
if (_inner->loadedAtBottomKnown()) {
|
|
||||||
return !_inner->loadedAtBottom();
|
return !_inner->loadedAtBottom();
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -939,13 +1018,27 @@ bool RepliesWidget::showMessage(
|
||||||
if (!message || message->replyToTop() != _rootId) {
|
if (!message || message->replyToTop() != _rootId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_highlightMessageId = id;
|
|
||||||
showAtPosition(Data::MessagePosition(message->date(), id));
|
const auto originItem = [&]() -> HistoryItem* {
|
||||||
|
using OriginMessage = Window::SectionShow::OriginMessage;
|
||||||
|
if (const auto origin = std::get_if<OriginMessage>(¶ms.origin)) {
|
||||||
|
if (const auto returnTo = session().data().message(origin->id)) {
|
||||||
|
if (returnTo->history() == _history
|
||||||
|
&& returnTo->replyToTop() == _rootId
|
||||||
|
&& _replyReturn != returnTo) {
|
||||||
|
return returnTo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}();
|
||||||
|
showAtPosition(Data::MessagePosition(message->date(), id), originItem);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::saveState(not_null<RepliesMemento*> memento) {
|
void RepliesWidget::saveState(not_null<RepliesMemento*> memento) {
|
||||||
memento->setReplies(_replies);
|
memento->setReplies(_replies);
|
||||||
|
memento->setReplyReturns(_replyReturns);
|
||||||
_inner->saveState(memento->list());
|
_inner->saveState(memento->list());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,6 +1065,7 @@ void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
|
||||||
} else if (!_replies) {
|
} else if (!_replies) {
|
||||||
setReplies(std::make_shared<Data::RepliesList>(_history, _rootId));
|
setReplies(std::make_shared<Data::RepliesList>(_history, _rootId));
|
||||||
}
|
}
|
||||||
|
restoreReplyReturns(memento->replyReturns());
|
||||||
_inner->restoreState(memento->list());
|
_inner->restoreState(memento->list());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1039,6 +1133,9 @@ void RepliesWidget::onScroll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::updateInnerVisibleArea() {
|
void RepliesWidget::updateInnerVisibleArea() {
|
||||||
|
if (!_inner->animatedScrolling()) {
|
||||||
|
checkReplyReturns();
|
||||||
|
}
|
||||||
const auto scrollTop = _scroll->scrollTop();
|
const auto scrollTop = _scroll->scrollTop();
|
||||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||||
updateScrollDownVisibility();
|
updateScrollDownVisibility();
|
||||||
|
@ -1144,6 +1241,7 @@ std::optional<int> RepliesWidget::listUnreadBarView(
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::listContentRefreshed() {
|
void RepliesWidget::listContentRefreshed() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClickHandlerPtr RepliesWidget::listDateLink(not_null<Element*> view) {
|
ClickHandlerPtr RepliesWidget::listDateLink(not_null<Element*> view) {
|
||||||
|
|
|
@ -137,8 +137,12 @@ private:
|
||||||
void updateAdaptiveLayout();
|
void updateAdaptiveLayout();
|
||||||
void saveState(not_null<RepliesMemento*> memento);
|
void saveState(not_null<RepliesMemento*> memento);
|
||||||
void restoreState(not_null<RepliesMemento*> memento);
|
void restoreState(not_null<RepliesMemento*> memento);
|
||||||
void showAtPosition(Data::MessagePosition position);
|
void showAtPosition(
|
||||||
bool showAtPositionNow(Data::MessagePosition position);
|
Data::MessagePosition position,
|
||||||
|
HistoryItem *originItem = nullptr);
|
||||||
|
bool showAtPositionNow(
|
||||||
|
Data::MessagePosition position,
|
||||||
|
HistoryItem *originItem);
|
||||||
|
|
||||||
void setupComposeControls();
|
void setupComposeControls();
|
||||||
|
|
||||||
|
@ -163,6 +167,12 @@ private:
|
||||||
void chooseAttach();
|
void chooseAttach();
|
||||||
[[nodiscard]] SendMenu::Type sendMenuType() const;
|
[[nodiscard]] SendMenu::Type sendMenuType() const;
|
||||||
|
|
||||||
|
void pushReplyReturn(not_null<HistoryItem*> item);
|
||||||
|
void computeCurrentReplyReturn();
|
||||||
|
void calculateNextReplyReturn();
|
||||||
|
void restoreReplyReturns(const std::vector<MsgId> &list);
|
||||||
|
void checkReplyReturns();
|
||||||
|
|
||||||
void uploadFile(const QByteArray &fileContent, SendMediaType type);
|
void uploadFile(const QByteArray &fileContent, SendMediaType type);
|
||||||
bool confirmSendingFiles(
|
bool confirmSendingFiles(
|
||||||
QImage &&image,
|
QImage &&image,
|
||||||
|
@ -212,7 +222,8 @@ private:
|
||||||
std::unique_ptr<ComposeControls> _composeControls;
|
std::unique_ptr<ComposeControls> _composeControls;
|
||||||
bool _skipScrollEvent = false;
|
bool _skipScrollEvent = false;
|
||||||
|
|
||||||
FullMsgId _highlightMessageId;
|
std::vector<MsgId> _replyReturns;
|
||||||
|
HistoryItem *_replyReturn = nullptr;
|
||||||
|
|
||||||
Ui::Animations::Simple _scrollDownShown;
|
Ui::Animations::Simple _scrollDownShown;
|
||||||
bool _scrollDownIsShown = false;
|
bool _scrollDownIsShown = false;
|
||||||
|
@ -250,6 +261,13 @@ public:
|
||||||
return _replies;
|
return _replies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setReplyReturns(const std::vector<MsgId> &list) {
|
||||||
|
_replyReturns = list;
|
||||||
|
}
|
||||||
|
const std::vector<MsgId> &replyReturns() const {
|
||||||
|
return _replyReturns;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] not_null<ListMemento*> list() {
|
[[nodiscard]] not_null<ListMemento*> list() {
|
||||||
return &_list;
|
return &_list;
|
||||||
}
|
}
|
||||||
|
@ -259,6 +277,7 @@ private:
|
||||||
const MsgId _rootId = 0;
|
const MsgId _rootId = 0;
|
||||||
ListMemento _list;
|
ListMemento _list;
|
||||||
std::shared_ptr<Data::RepliesList> _replies;
|
std::shared_ptr<Data::RepliesList> _replies;
|
||||||
|
std::vector<MsgId> _replyReturns;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue