mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Add read status tracking to comments.
This commit is contained in:
parent
78d83a2c69
commit
b8424b1d89
11 changed files with 182 additions and 11 deletions
|
@ -165,7 +165,14 @@ bool RepliesList::buildFromData(not_null<Viewer*> viewer) {
|
|||
= 0;
|
||||
return true;
|
||||
}
|
||||
const auto around = viewer->around;
|
||||
const auto around = [&] {
|
||||
if (viewer->around != ShowAtUnreadMsgId) {
|
||||
return viewer->around;
|
||||
} else if (const auto item = lookupRoot()) {
|
||||
return item->repliesReadTill();
|
||||
}
|
||||
return viewer->around;
|
||||
}();
|
||||
if (_list.empty()
|
||||
|| (!around && _skippedAfter != 0)
|
||||
|| (around > _list.front() && _skippedAfter != 0)
|
||||
|
@ -251,6 +258,10 @@ Histories &RepliesList::histories() {
|
|||
return _history->owner().histories();
|
||||
}
|
||||
|
||||
HistoryItem *RepliesList::lookupRoot() {
|
||||
return _history->owner().message(_history->channelId(), _rootId);
|
||||
}
|
||||
|
||||
void RepliesList::loadAround(MsgId id) {
|
||||
if (_loadingAround && *_loadingAround == id) {
|
||||
return;
|
||||
|
|
|
@ -33,6 +33,7 @@ public:
|
|||
private:
|
||||
struct Viewer;
|
||||
|
||||
HistoryItem *lookupRoot();
|
||||
[[nodiscard]] Histories &histories();
|
||||
|
||||
[[nodiscard]] rpl::producer<MessagesSlice> sourceFromServer(
|
||||
|
|
|
@ -264,6 +264,11 @@ public:
|
|||
}
|
||||
virtual void changeRepliesCount(int delta, PeerId replier) {
|
||||
}
|
||||
virtual void setRepliesReadTill(MsgId readTillId) {
|
||||
}
|
||||
[[nodiscard]] virtual MsgId repliesReadTill() const {
|
||||
return MsgId(0);
|
||||
}
|
||||
virtual void setReplyToTop(MsgId replyToTop) {
|
||||
}
|
||||
virtual void setRealId(MsgId newId);
|
||||
|
|
|
@ -46,6 +46,7 @@ struct HistoryMessageViews : public RuntimeComponent<HistoryMessageViews, Histor
|
|||
Part replies;
|
||||
ChannelId commentsChannelId = 0;
|
||||
MsgId commentsRootId = 0;
|
||||
MsgId repliesReadTillId = 0;
|
||||
};
|
||||
|
||||
struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned, HistoryItem> {
|
||||
|
|
|
@ -1561,6 +1561,22 @@ void HistoryMessage::changeRepliesCount(int delta, PeerId replier) {
|
|||
refreshRepliesText(views);
|
||||
}
|
||||
|
||||
void HistoryMessage::setRepliesReadTill(MsgId readTillId) {
|
||||
auto views = Get<HistoryMessageViews>();
|
||||
if (!views) {
|
||||
AddComponents(HistoryMessageViews::Bit());
|
||||
views = Get<HistoryMessageViews>();
|
||||
}
|
||||
views->repliesReadTillId = std::max(readTillId, 1);
|
||||
}
|
||||
|
||||
MsgId HistoryMessage::repliesReadTill() const {
|
||||
if (const auto views = Get<HistoryMessageViews>()) {
|
||||
return views->repliesReadTillId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HistoryMessage::setReplyToTop(MsgId replyToTop) {
|
||||
const auto reply = Get<HistoryMessageReply>();
|
||||
if (!reply
|
||||
|
|
|
@ -136,6 +136,8 @@ public:
|
|||
void setForwardsCount(int count) override;
|
||||
void setReplies(const MTPMessageReplies &data) override;
|
||||
void changeRepliesCount(int delta, PeerId replier) override;
|
||||
void setRepliesReadTill(MsgId readTillId) override;
|
||||
MsgId repliesReadTill() const override;
|
||||
void setReplyToTop(MsgId replyToTop) override;
|
||||
void setRealId(MsgId newId) override;
|
||||
void incrementReplyToTopCounter() override;
|
||||
|
|
|
@ -1265,8 +1265,8 @@ void ListWidget::restoreState(not_null<ListMemento*> memento) {
|
|||
_aroundIndex = -1;
|
||||
if (const auto limit = memento->idsLimit()) {
|
||||
_idsLimit = limit;
|
||||
_scrollTopState = memento->scrollTopState();
|
||||
}
|
||||
_scrollTopState = memento->scrollTopState();
|
||||
refreshViewer();
|
||||
}
|
||||
|
||||
|
@ -2663,6 +2663,10 @@ void ListWidget::replyToMessageRequestNotify(FullMsgId item) {
|
|||
_requestedToReplyToMessage.fire(std::move(item));
|
||||
}
|
||||
|
||||
rpl::producer<FullMsgId> ListWidget::readMessageRequested() const {
|
||||
return _requestedToReadMessage.events();
|
||||
}
|
||||
|
||||
ListWidget::~ListWidget() = default;
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -188,10 +188,11 @@ public:
|
|||
QPoint tooltipPos() const override;
|
||||
bool tooltipWindowActive() const override;
|
||||
|
||||
rpl::producer<FullMsgId> editMessageRequested() const;
|
||||
[[nodiscard]] rpl::producer<FullMsgId> editMessageRequested() const;
|
||||
void editMessageRequestNotify(FullMsgId item);
|
||||
rpl::producer<FullMsgId> replyToMessageRequested() const;
|
||||
[[nodiscard]] rpl::producer<FullMsgId> replyToMessageRequested() const;
|
||||
void replyToMessageRequestNotify(FullMsgId item);
|
||||
[[nodiscard]] rpl::producer<FullMsgId> readMessageRequested() const;
|
||||
|
||||
// ElementDelegate interface.
|
||||
Context elementContext() override;
|
||||
|
@ -529,6 +530,7 @@ private:
|
|||
|
||||
rpl::event_stream<FullMsgId> _requestedToEditMessage;
|
||||
rpl::event_stream<FullMsgId> _requestedToReplyToMessage;
|
||||
rpl::event_stream<FullMsgId> _requestedToReadMessage;
|
||||
|
||||
rpl::lifetime _viewerLifetime;
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
constexpr auto kReadRequestTimeout = 3 * crl::time(1000);
|
||||
|
||||
void ShowErrorToast(const QString &text) {
|
||||
Ui::Toast::Show(Ui::Toast::Config{
|
||||
.text = { text },
|
||||
|
@ -80,6 +82,16 @@ bool CanSendFiles(not_null<const QMimeData*> data) {
|
|||
|
||||
} // namespace
|
||||
|
||||
RepliesMemento::RepliesMemento(not_null<HistoryItem*> commentsItem)
|
||||
: RepliesMemento(commentsItem->history(), commentsItem->id) {
|
||||
if (commentsItem->repliesReadTill() == MsgId(1)) {
|
||||
_list.setAroundPosition(Data::MinMessagePosition);
|
||||
_list.setScrollTopState(ListMemento::ScrollTopState{
|
||||
Data::MinMessagePosition
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
object_ptr<Window::SectionWidget> RepliesMemento::createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
|
@ -106,6 +118,7 @@ RepliesWidget::RepliesWidget(
|
|||
, _history(history)
|
||||
, _rootId(rootId)
|
||||
, _root(lookupRoot())
|
||||
, _commentsRoot(lookupCommentsRoot())
|
||||
, _areComments(computeAreComments())
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
, _topBar(this, controller)
|
||||
|
@ -115,7 +128,8 @@ RepliesWidget::RepliesWidget(
|
|||
controller,
|
||||
ComposeControls::Mode::Normal))
|
||||
, _rootShadow(this)
|
||||
, _scrollDown(_scroll, st::historyToDown) {
|
||||
, _scrollDown(_scroll, st::historyToDown)
|
||||
, _readRequestTimer([=] { sendReadTillRequest(); }) {
|
||||
setupRoot();
|
||||
|
||||
_rootHeight = st::msgReplyPadding.top()
|
||||
|
@ -178,9 +192,13 @@ RepliesWidget::RepliesWidget(
|
|||
|
||||
_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) {
|
||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||
if (update.item == _root) {
|
||||
_root = nullptr;
|
||||
}
|
||||
if (update.item == _commentsRoot) {
|
||||
_commentsRoot = nullptr;
|
||||
}
|
||||
while (update.item == _replyReturn) {
|
||||
calculateNextReplyReturn();
|
||||
}
|
||||
|
@ -190,10 +208,33 @@ RepliesWidget::RepliesWidget(
|
|||
setupComposeControls();
|
||||
}
|
||||
|
||||
RepliesWidget::~RepliesWidget() = default;
|
||||
RepliesWidget::~RepliesWidget() {
|
||||
if (_readRequestTimer.isActive()) {
|
||||
sendReadTillRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void RepliesWidget::sendReadTillRequest() {
|
||||
if (!_commentsRoot || !_root) {
|
||||
return;
|
||||
}
|
||||
if (_readRequestTimer.isActive()) {
|
||||
_readRequestTimer.cancel();
|
||||
}
|
||||
const auto api = &_history->session().api();
|
||||
api->request(base::take(_readRequestId)).cancel();
|
||||
_readRequestId = api->request(MTPmessages_ReadDiscussion(
|
||||
_commentsRoot->history()->peer->input,
|
||||
MTP_int(_commentsRoot->id),
|
||||
MTP_int(_root->repliesReadTill())
|
||||
)).done([=](const MTPBool &) {
|
||||
|
||||
}).send();
|
||||
}
|
||||
|
||||
void RepliesWidget::setupRoot() {
|
||||
if (_root) {
|
||||
setupCommentsRoot();
|
||||
refreshRootView();
|
||||
} else {
|
||||
const auto channel = _history->peer->asChannel();
|
||||
|
@ -201,6 +242,7 @@ void RepliesWidget::setupRoot() {
|
|||
_root = lookupRoot();
|
||||
if (_root) {
|
||||
_areComments = computeAreComments();
|
||||
setupCommentsRoot();
|
||||
}
|
||||
refreshRootView();
|
||||
});
|
||||
|
@ -208,6 +250,30 @@ void RepliesWidget::setupRoot() {
|
|||
}
|
||||
}
|
||||
|
||||
void RepliesWidget::setupCommentsRoot() {
|
||||
Expects(_root != nullptr);
|
||||
|
||||
const auto postChannel = _root->discussionPostOriginalSender();
|
||||
if (!postChannel) {
|
||||
return;
|
||||
} else if (_commentsRoot) {
|
||||
sendReadTillRequest();
|
||||
} else {
|
||||
const auto forwarded = _root->Get<HistoryMessageForwarded>();
|
||||
const auto messageId = forwarded->savedFromMsgId;
|
||||
const auto done = crl::guard(this, [=](ChannelData*, MsgId) {
|
||||
_commentsRoot = lookupCommentsRoot();
|
||||
if (_commentsRoot) {
|
||||
sendReadTillRequest();
|
||||
}
|
||||
});
|
||||
_history->session().api().requestMessageData(
|
||||
postChannel,
|
||||
messageId,
|
||||
done);
|
||||
}
|
||||
}
|
||||
|
||||
void RepliesWidget::refreshRootView() {
|
||||
const auto sender = (_root && _root->discussionPostOriginalSender())
|
||||
? _root->discussionPostOriginalSender()
|
||||
|
@ -240,6 +306,17 @@ HistoryItem *RepliesWidget::lookupRoot() const {
|
|||
return _history->owner().message(_history->channelId(), _rootId);
|
||||
}
|
||||
|
||||
HistoryItem *RepliesWidget::lookupCommentsRoot() const {
|
||||
if (!computeAreComments()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto forwarded = _root->Get<HistoryMessageForwarded>();
|
||||
Assert(forwarded != nullptr);
|
||||
return _history->owner().message(
|
||||
forwarded->savedFromPeer->asChannel(),
|
||||
forwarded->savedFromMsgId);
|
||||
}
|
||||
|
||||
bool RepliesWidget::computeAreComments() const {
|
||||
return _root && _root->isDiscussionPost();
|
||||
}
|
||||
|
@ -1364,11 +1441,49 @@ void RepliesWidget::listSelectionChanged(SelectedItems &&items) {
|
|||
_topBar->showSelected(state);
|
||||
}
|
||||
|
||||
void RepliesWidget::readTill(MsgId tillId) {
|
||||
if (!_root) {
|
||||
return;
|
||||
}
|
||||
const auto now = _root->repliesReadTill();
|
||||
if (now < tillId) {
|
||||
_root->setRepliesReadTill(tillId);
|
||||
if (!_readRequestTimer.isActive()) {
|
||||
_readRequestTimer.callOnce(kReadRequestTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RepliesWidget::listVisibleItemsChanged(HistoryItemsList &&items) {
|
||||
const auto reversed = ranges::view::reverse(items);
|
||||
const auto good = ranges::find_if(reversed, [](auto item) {
|
||||
return IsServerMsgId(item->id);
|
||||
});
|
||||
if (good != end(reversed)) {
|
||||
readTill((*good)->id);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int> RepliesWidget::listUnreadBarView(
|
||||
const std::vector<not_null<Element*>> &elements) {
|
||||
if (!_root) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto till = _root->repliesReadTill();
|
||||
if (till < 2) {
|
||||
return std::nullopt;
|
||||
}
|
||||
for (auto i = 0, count = int(elements.size()); i != count; ++i) {
|
||||
const auto item = elements[i]->data();
|
||||
if (item->id > till) {
|
||||
if (item->out()) {
|
||||
_root->setRepliesReadTill(item->id);
|
||||
_readRequestTimer.callOnce(kReadRequestTimeout);
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "window/section_memento.h"
|
||||
#include "history/view/history_view_list_widget.h"
|
||||
#include "data/data_messages.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
class History;
|
||||
enum class CompressConfirm;
|
||||
|
@ -148,8 +149,11 @@ private:
|
|||
void setupComposeControls();
|
||||
|
||||
void setupRoot();
|
||||
void setupCommentsRoot();
|
||||
void refreshRootView();
|
||||
void setupDragArea();
|
||||
void sendReadTillRequest();
|
||||
void readTill(MsgId id);
|
||||
|
||||
void setupScrollDownButton();
|
||||
void scrollDownClicked();
|
||||
|
@ -172,6 +176,7 @@ private:
|
|||
[[nodiscard]] SendMenu::Type sendMenuType() const;
|
||||
[[nodiscard]] MsgId replyToId() const;
|
||||
[[nodiscard]] HistoryItem *lookupRoot() const;
|
||||
[[nodiscard]] HistoryItem *lookupCommentsRoot() const;
|
||||
[[nodiscard]] bool computeAreComments() const;
|
||||
|
||||
void pushReplyReturn(not_null<HistoryItem*> item);
|
||||
|
@ -224,6 +229,7 @@ private:
|
|||
const not_null<History*> _history;
|
||||
const MsgId _rootId = 0;
|
||||
HistoryItem *_root = nullptr;
|
||||
HistoryItem *_commentsRoot = nullptr;
|
||||
std::shared_ptr<Data::RepliesList> _replies;
|
||||
rpl::variable<bool> _areComments = false;
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
|
@ -248,14 +254,19 @@ private:
|
|||
Data::MessagesSlice _lastSlice;
|
||||
bool _choosingAttach = false;
|
||||
|
||||
base::Timer _readRequestTimer;
|
||||
mtpRequestId _readRequestId = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class RepliesMemento : public Window::SectionMemento {
|
||||
public:
|
||||
RepliesMemento(not_null<History*> history, MsgId rootId)
|
||||
: _history(history)
|
||||
, _rootId(rootId) {
|
||||
}
|
||||
explicit RepliesMemento(not_null<HistoryItem*> commentsItem);
|
||||
|
||||
object_ptr<Window::SectionWidget> createWidget(
|
||||
QWidget *parent,
|
||||
|
|
|
@ -110,7 +110,7 @@ void SessionNavigation::showRepliesForMessage(
|
|||
} else if (const auto id = item->commentsItemId()) {
|
||||
if (const auto item = _session->data().message(id)) {
|
||||
showSection(
|
||||
HistoryView::RepliesMemento(item->history(), item->id));
|
||||
HistoryView::RepliesMemento(item));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -148,8 +148,11 @@ void SessionNavigation::showRepliesForMessage(
|
|||
if (post) {
|
||||
post->setCommentsItemId(item->fullId());
|
||||
}
|
||||
if (const auto readTill = data.vread_max_id()) {
|
||||
item->setRepliesReadTill(readTill->v);
|
||||
}
|
||||
showSection(
|
||||
HistoryView::RepliesMemento(item->history(), item->id));
|
||||
HistoryView::RepliesMemento(item));
|
||||
}
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
|
|
Loading…
Add table
Reference in a new issue