mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Track and display unread count in discussions.
This commit is contained in:
parent
85e4c8527b
commit
c39024c7fd
12 changed files with 271 additions and 45 deletions
|
@ -2094,17 +2094,18 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
const auto msgId = d.vtop_msg_id().v;
|
const auto msgId = d.vtop_msg_id().v;
|
||||||
const auto readTillId = d.vread_max_id().v;
|
const auto readTillId = d.vread_max_id().v;
|
||||||
const auto item = session().data().message(channelId, msgId);
|
const auto item = session().data().message(channelId, msgId);
|
||||||
|
const auto unreadCount = std::nullopt;
|
||||||
if (item) {
|
if (item) {
|
||||||
item->setRepliesInboxReadTill(readTillId);
|
item->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||||
if (const auto post = item->lookupDiscussionPostOriginal()) {
|
if (const auto post = item->lookupDiscussionPostOriginal()) {
|
||||||
post->setRepliesInboxReadTill(readTillId);
|
post->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (const auto broadcastId = d.vbroadcast_id()) {
|
if (const auto broadcastId = d.vbroadcast_id()) {
|
||||||
if (const auto post = session().data().message(
|
if (const auto post = session().data().message(
|
||||||
broadcastId->v,
|
broadcastId->v,
|
||||||
d.vbroadcast_post()->v)) {
|
d.vbroadcast_post()->v)) {
|
||||||
post->setRepliesInboxReadTill(readTillId);
|
post->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -134,16 +134,17 @@ struct MessageUpdate {
|
||||||
enum class Flag : uint32 {
|
enum class Flag : uint32 {
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
||||||
Edited = (1U << 0),
|
Edited = (1U << 0),
|
||||||
Destroyed = (1U << 1),
|
Destroyed = (1U << 1),
|
||||||
DialogRowRepaint = (1U << 2),
|
DialogRowRepaint = (1U << 2),
|
||||||
DialogRowRefresh = (1U << 3),
|
DialogRowRefresh = (1U << 3),
|
||||||
NewAdded = (1U << 4),
|
NewAdded = (1U << 4),
|
||||||
ReplyMarkup = (1U << 5),
|
ReplyMarkup = (1U << 5),
|
||||||
BotCallbackSent = (1U << 6),
|
BotCallbackSent = (1U << 6),
|
||||||
NewMaybeAdded = (1U << 7),
|
NewMaybeAdded = (1U << 7),
|
||||||
|
RepliesUnreadCount = (1U << 8),
|
||||||
|
|
||||||
LastUsedBit = (1U << 7),
|
LastUsedBit = (1U << 7),
|
||||||
};
|
};
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||||
|
|
|
@ -101,6 +101,15 @@ rpl::producer<MessagesSlice> RepliesList::source(
|
||||||
_partLoaded.events(
|
_partLoaded.events(
|
||||||
) | rpl::start_with_next(pushDelayed, lifetime);
|
) | rpl::start_with_next(pushDelayed, lifetime);
|
||||||
|
|
||||||
|
_history->session().data().channelDifferenceTooLong(
|
||||||
|
) | rpl::filter([=](not_null<ChannelData*> channel) {
|
||||||
|
if (_history->peer != channel || !_skippedAfter.has_value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_skippedAfter = std::nullopt;
|
||||||
|
return true;
|
||||||
|
}) | rpl::start_with_next(pushDelayed, lifetime);
|
||||||
|
|
||||||
push();
|
push();
|
||||||
return lifetime;
|
return lifetime;
|
||||||
};
|
};
|
||||||
|
@ -169,6 +178,64 @@ rpl::producer<int> RepliesList::fullCount() const {
|
||||||
return _fullCount.value() | rpl::filter_optional();
|
return _fullCount.value() | rpl::filter_optional();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<int> RepliesList::fullUnreadCountAfter(
|
||||||
|
MsgId readTillId,
|
||||||
|
MsgId wasReadTillId,
|
||||||
|
std::optional<int> wasUnreadCountAfter) const {
|
||||||
|
Expects(readTillId >= wasReadTillId);
|
||||||
|
|
||||||
|
readTillId = std::max(readTillId, _rootId);
|
||||||
|
wasReadTillId = std::max(wasReadTillId, _rootId);
|
||||||
|
const auto backLoaded = (_skippedBefore == 0);
|
||||||
|
const auto frontLoaded = (_skippedAfter == 0);
|
||||||
|
const auto fullLoaded = backLoaded && frontLoaded;
|
||||||
|
const auto allUnread = (readTillId == _rootId)
|
||||||
|
|| (fullLoaded && _list.empty());
|
||||||
|
const auto countIncoming = [&](auto from, auto till) {
|
||||||
|
auto &owner = _history->owner();
|
||||||
|
const auto channelId = _history->channelId();
|
||||||
|
auto count = 0;
|
||||||
|
for (auto i = from; i != till; ++i) {
|
||||||
|
if (!owner.message(channelId, *i)->out()) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
if (allUnread && fullLoaded) {
|
||||||
|
// Should not happen too often unless the list is empty.
|
||||||
|
return countIncoming(begin(_list), end(_list));
|
||||||
|
} else if (frontLoaded && !_list.empty() && readTillId >= _list.front()) {
|
||||||
|
// Always "count by local data" if read till the end.
|
||||||
|
return 0;
|
||||||
|
} else if (wasReadTillId == readTillId) {
|
||||||
|
// Otherwise don't recount the same value over and over.
|
||||||
|
return wasUnreadCountAfter;
|
||||||
|
} else if (frontLoaded && !_list.empty() && readTillId >= _list.back()) {
|
||||||
|
// And count by local data if it is available and read-till changed.
|
||||||
|
return countIncoming(
|
||||||
|
begin(_list),
|
||||||
|
ranges::lower_bound(_list, readTillId, std::greater<>()));
|
||||||
|
} else if (_list.empty()) {
|
||||||
|
return std::nullopt;
|
||||||
|
} else if (wasUnreadCountAfter.has_value()
|
||||||
|
&& (frontLoaded || readTillId <= _list.front())
|
||||||
|
&& (backLoaded || wasReadTillId >= _list.back())) {
|
||||||
|
// Count how many were read since previous value.
|
||||||
|
const auto from = ranges::lower_bound(
|
||||||
|
_list,
|
||||||
|
readTillId,
|
||||||
|
std::greater<>());
|
||||||
|
const auto till = ranges::lower_bound(
|
||||||
|
from,
|
||||||
|
end(_list),
|
||||||
|
wasReadTillId,
|
||||||
|
std::greater<>());
|
||||||
|
return std::max(*wasUnreadCountAfter - countIncoming(from, till), 0);
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
void RepliesList::injectRootMessageAndReverse(not_null<Viewer*> viewer) {
|
void RepliesList::injectRootMessageAndReverse(not_null<Viewer*> viewer) {
|
||||||
injectRootMessage(viewer);
|
injectRootMessage(viewer);
|
||||||
ranges::reverse(viewer->slice.ids);
|
ranges::reverse(viewer->slice.ids);
|
||||||
|
|
|
@ -31,6 +31,11 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<int> fullCount() const;
|
[[nodiscard]] rpl::producer<int> fullCount() const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<int> fullUnreadCountAfter(
|
||||||
|
MsgId readTillId,
|
||||||
|
MsgId wasReadTillId,
|
||||||
|
std::optional<int> wasUnreadCountAfter) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Viewer;
|
struct Viewer;
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,9 @@ public:
|
||||||
[[nodiscard]] virtual MsgId repliesInboxReadTill() const {
|
[[nodiscard]] virtual MsgId repliesInboxReadTill() const {
|
||||||
return MsgId(0);
|
return MsgId(0);
|
||||||
}
|
}
|
||||||
virtual void setRepliesInboxReadTill(MsgId readTillId) {
|
virtual void setRepliesInboxReadTill(
|
||||||
|
MsgId readTillId,
|
||||||
|
std::optional<int> unreadCount) {
|
||||||
}
|
}
|
||||||
[[nodiscard]] virtual MsgId computeRepliesInboxReadTillFull() const {
|
[[nodiscard]] virtual MsgId computeRepliesInboxReadTillFull() const {
|
||||||
return MsgId(0);
|
return MsgId(0);
|
||||||
|
@ -316,7 +318,10 @@ public:
|
||||||
}
|
}
|
||||||
virtual void clearReplies() {
|
virtual void clearReplies() {
|
||||||
}
|
}
|
||||||
virtual void changeRepliesCount(int delta, PeerId replier) {
|
virtual void changeRepliesCount(
|
||||||
|
int delta,
|
||||||
|
PeerId replier,
|
||||||
|
std::optional<bool> unread) {
|
||||||
}
|
}
|
||||||
virtual void setReplyToTop(MsgId replyToTop) {
|
virtual void setReplyToTop(MsgId replyToTop) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ struct HistoryMessageViews : public RuntimeComponent<HistoryMessageViews, Histor
|
||||||
MsgId repliesInboxReadTillId = 0;
|
MsgId repliesInboxReadTillId = 0;
|
||||||
MsgId repliesOutboxReadTillId = 0;
|
MsgId repliesOutboxReadTillId = 0;
|
||||||
MsgId repliesMaxId = 0;
|
MsgId repliesMaxId = 0;
|
||||||
|
int repliesUnreadCount = -1; // unknown
|
||||||
ChannelId commentsMegagroupId = 0;
|
ChannelId commentsMegagroupId = 0;
|
||||||
MsgId commentsRootId = 0;
|
MsgId commentsRootId = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -816,16 +816,30 @@ MsgId HistoryMessage::repliesInboxReadTill() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryMessage::setRepliesInboxReadTill(MsgId readTillId) {
|
void HistoryMessage::setRepliesInboxReadTill(
|
||||||
|
MsgId readTillId,
|
||||||
|
std::optional<int> unreadCount) {
|
||||||
if (const auto views = Get<HistoryMessageViews>()) {
|
if (const auto views = Get<HistoryMessageViews>()) {
|
||||||
const auto newReadTillId = std::max(readTillId, 1);
|
const auto newReadTillId = std::max(readTillId, 1);
|
||||||
if (newReadTillId > views->repliesInboxReadTillId) {
|
const auto ignore = (newReadTillId < views->repliesInboxReadTillId);
|
||||||
|
if (ignore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto changed = (newReadTillId > views->repliesInboxReadTillId);
|
||||||
|
if (changed) {
|
||||||
const auto wasUnread = repliesAreComments() && areRepliesUnread();
|
const auto wasUnread = repliesAreComments() && areRepliesUnread();
|
||||||
views->repliesInboxReadTillId = newReadTillId;
|
views->repliesInboxReadTillId = newReadTillId;
|
||||||
if (wasUnread && !areRepliesUnread()) {
|
if (wasUnread && !areRepliesUnread()) {
|
||||||
history()->owner().requestItemRepaint(this);
|
history()->owner().requestItemRepaint(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const auto wasUnreadCount = (views->repliesUnreadCount >= 0)
|
||||||
|
? std::make_optional(views->repliesUnreadCount)
|
||||||
|
: std::nullopt;
|
||||||
|
if (unreadCount != wasUnreadCount
|
||||||
|
&& (changed || unreadCount.has_value())) {
|
||||||
|
setUnreadRepliesCount(views, unreadCount.value_or(-1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1808,10 +1822,27 @@ void HistoryMessage::refreshRepliesText(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryMessage::changeRepliesCount(int delta, PeerId replier) {
|
void HistoryMessage::changeRepliesCount(
|
||||||
|
int delta,
|
||||||
|
PeerId replier,
|
||||||
|
std::optional<bool> unread) {
|
||||||
const auto views = Get<HistoryMessageViews>();
|
const auto views = Get<HistoryMessageViews>();
|
||||||
const auto limit = HistoryMessageViews::kMaxRecentRepliers;
|
const auto limit = HistoryMessageViews::kMaxRecentRepliers;
|
||||||
if (!views || views->replies.count < 0) {
|
if (!views) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update unread count.
|
||||||
|
if (!unread) {
|
||||||
|
setUnreadRepliesCount(views, -1);
|
||||||
|
} else if (views->repliesUnreadCount >= 0 && *unread) {
|
||||||
|
setUnreadRepliesCount(
|
||||||
|
views,
|
||||||
|
std::max(views->repliesUnreadCount + delta, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update full count.
|
||||||
|
if (views->replies.count < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
views->replies.count = std::max(views->replies.count + delta, 0);
|
views->replies.count = std::max(views->replies.count + delta, 0);
|
||||||
|
@ -1830,6 +1861,19 @@ void HistoryMessage::changeRepliesCount(int delta, PeerId replier) {
|
||||||
refreshRepliesText(views);
|
refreshRepliesText(views);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryMessage::setUnreadRepliesCount(
|
||||||
|
not_null<HistoryMessageViews*> views,
|
||||||
|
int count) {
|
||||||
|
// Track unread count in discussion forwards, not in the channel posts.
|
||||||
|
if (views->repliesUnreadCount == count || views->commentsMegagroupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
views->repliesUnreadCount = count;
|
||||||
|
history()->session().changes().messageUpdated(
|
||||||
|
this,
|
||||||
|
Data::MessageUpdate::Flag::RepliesUnreadCount);
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryMessage::setReplyToTop(MsgId replyToTop) {
|
void HistoryMessage::setReplyToTop(MsgId replyToTop) {
|
||||||
const auto reply = Get<HistoryMessageReply>();
|
const auto reply = Get<HistoryMessageReply>();
|
||||||
if (!reply
|
if (!reply
|
||||||
|
@ -1877,19 +1921,23 @@ void HistoryMessage::changeReplyToTopCounter(
|
||||||
if (!top) {
|
if (!top) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto changeFor = [&](not_null<HistoryItem*> item) {
|
auto unread = out() ? std::make_optional(false) : std::nullopt;
|
||||||
if (const auto from = displayFrom()) {
|
|
||||||
item->changeRepliesCount(delta, from->id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
item->changeRepliesCount(delta, PeerId());
|
|
||||||
};
|
|
||||||
if (const auto views = top->Get<HistoryMessageViews>()) {
|
if (const auto views = top->Get<HistoryMessageViews>()) {
|
||||||
if (views->commentsMegagroupId) {
|
if (views->commentsMegagroupId) {
|
||||||
// This is a post in channel, we don't track its replies.
|
// This is a post in channel, we don't track its replies.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (views->repliesInboxReadTillId > 0) {
|
||||||
|
unread = !out() && (id > views->repliesInboxReadTillId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
const auto changeFor = [&](not_null<HistoryItem*> item) {
|
||||||
|
if (const auto from = displayFrom()) {
|
||||||
|
item->changeRepliesCount(delta, from->id, unread);
|
||||||
|
} else {
|
||||||
|
item->changeRepliesCount(delta, PeerId(), unread);
|
||||||
|
}
|
||||||
|
};
|
||||||
changeFor(top);
|
changeFor(top);
|
||||||
if (const auto original = top->lookupDiscussionPostOriginal()) {
|
if (const auto original = top->lookupDiscussionPostOriginal()) {
|
||||||
changeFor(original);
|
changeFor(original);
|
||||||
|
|
|
@ -133,7 +133,10 @@ public:
|
||||||
void setForwardsCount(int count) override;
|
void setForwardsCount(int count) override;
|
||||||
void setReplies(const MTPMessageReplies &data) override;
|
void setReplies(const MTPMessageReplies &data) override;
|
||||||
void clearReplies() override;
|
void clearReplies() override;
|
||||||
void changeRepliesCount(int delta, PeerId replier) override;
|
void changeRepliesCount(
|
||||||
|
int delta,
|
||||||
|
PeerId replier,
|
||||||
|
std::optional<bool> unread) override;
|
||||||
void setReplyToTop(MsgId replyToTop) override;
|
void setReplyToTop(MsgId replyToTop) override;
|
||||||
void setPostAuthor(const QString &author) override;
|
void setPostAuthor(const QString &author) override;
|
||||||
void setRealId(MsgId newId) override;
|
void setRealId(MsgId newId) override;
|
||||||
|
@ -181,7 +184,9 @@ public:
|
||||||
[[nodiscard]] bool externalReply() const override;
|
[[nodiscard]] bool externalReply() const override;
|
||||||
|
|
||||||
[[nodiscard]] MsgId repliesInboxReadTill() const override;
|
[[nodiscard]] MsgId repliesInboxReadTill() const override;
|
||||||
void setRepliesInboxReadTill(MsgId readTillId) override;
|
void setRepliesInboxReadTill(
|
||||||
|
MsgId readTillId,
|
||||||
|
std::optional<int> unreadCount) override;
|
||||||
[[nodiscard]] MsgId computeRepliesInboxReadTillFull() const override;
|
[[nodiscard]] MsgId computeRepliesInboxReadTillFull() const override;
|
||||||
[[nodiscard]] MsgId repliesOutboxReadTill() const override;
|
[[nodiscard]] MsgId repliesOutboxReadTill() const override;
|
||||||
void setRepliesOutboxReadTill(MsgId readTillId) override;
|
void setRepliesOutboxReadTill(MsgId readTillId) override;
|
||||||
|
@ -250,6 +255,9 @@ private:
|
||||||
void refreshRepliesText(
|
void refreshRepliesText(
|
||||||
not_null<HistoryMessageViews*> views,
|
not_null<HistoryMessageViews*> views,
|
||||||
bool forceResize = false);
|
bool forceResize = false);
|
||||||
|
void setUnreadRepliesCount(
|
||||||
|
not_null<HistoryMessageViews*> views,
|
||||||
|
int count);
|
||||||
|
|
||||||
static void FillForwardedInfo(
|
static void FillForwardedInfo(
|
||||||
CreateConfig &config,
|
CreateConfig &config,
|
||||||
|
|
|
@ -160,7 +160,6 @@ private:
|
||||||
bool _scrollDownIsShown = false;
|
bool _scrollDownIsShown = false;
|
||||||
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
||||||
|
|
||||||
Data::MessagesSlice _lastSlice;
|
|
||||||
int _messagesCount = -1;
|
int _messagesCount = -1;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -247,16 +247,24 @@ RepliesWidget::RepliesWidget(
|
||||||
data.progress);
|
data.progress);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
using MessageUpdateFlag = Data::MessageUpdate::Flag;
|
||||||
_history->session().changes().messageUpdates(
|
_history->session().changes().messageUpdates(
|
||||||
Data::MessageUpdate::Flag::Destroyed
|
MessageUpdateFlag::Destroyed
|
||||||
|
| MessageUpdateFlag::RepliesUnreadCount
|
||||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||||
if (update.item == _root) {
|
if (update.flags & MessageUpdateFlag::Destroyed) {
|
||||||
_root = nullptr;
|
if (update.item == _root) {
|
||||||
updatePinnedVisibility();
|
_root = nullptr;
|
||||||
controller->showBackFromStack();
|
updatePinnedVisibility();
|
||||||
}
|
controller->showBackFromStack();
|
||||||
while (update.item == _replyReturn) {
|
}
|
||||||
calculateNextReplyReturn();
|
while (update.item == _replyReturn) {
|
||||||
|
calculateNextReplyReturn();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if ((update.item == _root)
|
||||||
|
&& (update.flags & MessageUpdateFlag::RepliesUnreadCount)) {
|
||||||
|
refreshUnreadCountBadge();
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
@ -302,12 +310,15 @@ void RepliesWidget::sendReadTillRequest() {
|
||||||
_readRequestPending = false;
|
_readRequestPending = false;
|
||||||
const auto api = &_history->session().api();
|
const auto api = &_history->session().api();
|
||||||
api->request(base::take(_readRequestId)).cancel();
|
api->request(base::take(_readRequestId)).cancel();
|
||||||
|
|
||||||
_readRequestId = api->request(MTPmessages_ReadDiscussion(
|
_readRequestId = api->request(MTPmessages_ReadDiscussion(
|
||||||
_root->history()->peer->input,
|
_root->history()->peer->input,
|
||||||
MTP_int(_root->id),
|
MTP_int(_root->id),
|
||||||
MTP_int(_root->computeRepliesInboxReadTillFull())
|
MTP_int(_root->computeRepliesInboxReadTillFull())
|
||||||
)).done([=](const MTPBool &) {
|
)).done(crl::guard(this, [=](const MTPBool &) {
|
||||||
}).send();
|
_readRequestId = 0;
|
||||||
|
reloadUnreadCountIfNeeded();
|
||||||
|
})).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::setupRoot() {
|
void RepliesWidget::setupRoot() {
|
||||||
|
@ -317,6 +328,7 @@ void RepliesWidget::setupRoot() {
|
||||||
_root = lookupRoot();
|
_root = lookupRoot();
|
||||||
if (_root) {
|
if (_root) {
|
||||||
_areComments = computeAreComments();
|
_areComments = computeAreComments();
|
||||||
|
refreshUnreadCountBadge();
|
||||||
if (_readRequestPending) {
|
if (_readRequestPending) {
|
||||||
sendReadTillRequest();
|
sendReadTillRequest();
|
||||||
}
|
}
|
||||||
|
@ -370,6 +382,19 @@ bool RepliesWidget::computeAreComments() const {
|
||||||
return _root && _root->isDiscussionPost();
|
return _root && _root->isDiscussionPost();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<int> RepliesWidget::computeUnreadCount() const {
|
||||||
|
if (!_root) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const auto views = _root->Get<HistoryMessageViews>();
|
||||||
|
if (!views) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return (views->repliesUnreadCount >= 0)
|
||||||
|
? std::make_optional(views->repliesUnreadCount)
|
||||||
|
: std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
void RepliesWidget::setupComposeControls() {
|
void RepliesWidget::setupComposeControls() {
|
||||||
auto slowmodeSecondsLeft = session().changes().peerFlagsValue(
|
auto slowmodeSecondsLeft = session().changes().peerFlagsValue(
|
||||||
_history->peer,
|
_history->peer,
|
||||||
|
@ -1143,6 +1168,7 @@ void RepliesWidget::setupScrollDownButton() {
|
||||||
_scrollDown->setClickedCallback([=] {
|
_scrollDown->setClickedCallback([=] {
|
||||||
scrollDownClicked();
|
scrollDownClicked();
|
||||||
});
|
});
|
||||||
|
refreshUnreadCountBadge();
|
||||||
base::install_event_filter(_scrollDown, [=](not_null<QEvent*> event) {
|
base::install_event_filter(_scrollDown, [=](not_null<QEvent*> event) {
|
||||||
if (event->type() != QEvent::Wheel) {
|
if (event->type() != QEvent::Wheel) {
|
||||||
return base::EventFilterResult::Continue;
|
return base::EventFilterResult::Continue;
|
||||||
|
@ -1154,6 +1180,56 @@ void RepliesWidget::setupScrollDownButton() {
|
||||||
updateScrollDownVisibility();
|
updateScrollDownVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RepliesWidget::refreshUnreadCountBadge() {
|
||||||
|
if (!_root) {
|
||||||
|
return;
|
||||||
|
} else if (const auto count = computeUnreadCount()) {
|
||||||
|
_scrollDown->setUnreadCount(*count);
|
||||||
|
} else if (!_readRequestPending
|
||||||
|
&& !_readRequestTimer.isActive()
|
||||||
|
&& !_readRequestId) {
|
||||||
|
reloadUnreadCountIfNeeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesWidget::reloadUnreadCountIfNeeded() {
|
||||||
|
const auto views = _root ? _root->Get<HistoryMessageViews>() : nullptr;
|
||||||
|
if (!views || views->repliesUnreadCount >= 0) {
|
||||||
|
return;
|
||||||
|
} else if (views->repliesInboxReadTillId
|
||||||
|
< _root->computeRepliesInboxReadTillFull()) {
|
||||||
|
_readRequestTimer.callOnce(0);
|
||||||
|
} else if (!_reloadUnreadCountRequestId) {
|
||||||
|
const auto session = &_history->session();
|
||||||
|
const auto fullId = _root->fullId();
|
||||||
|
const auto apply = [session, fullId](int readTill, int unreadCount) {
|
||||||
|
if (const auto root = session->data().message(fullId)) {
|
||||||
|
root->setRepliesInboxReadTill(readTill, unreadCount);
|
||||||
|
if (const auto post = root->lookupDiscussionPostOriginal()) {
|
||||||
|
post->setRepliesInboxReadTill(readTill, unreadCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto weak = Ui::MakeWeak(this);
|
||||||
|
_reloadUnreadCountRequestId = session->api().request(
|
||||||
|
MTPmessages_GetDiscussionMessage(
|
||||||
|
_history->peer->input,
|
||||||
|
MTP_int(_rootId))
|
||||||
|
).done([=](const MTPmessages_DiscussionMessage &result) {
|
||||||
|
if (weak) {
|
||||||
|
_reloadUnreadCountRequestId = 0;
|
||||||
|
}
|
||||||
|
result.match([&](const MTPDmessages_discussionMessage &data) {
|
||||||
|
session->data().processUsers(data.vusers());
|
||||||
|
session->data().processChats(data.vchats());
|
||||||
|
apply(
|
||||||
|
data.vread_inbox_max_id().value_or_empty(),
|
||||||
|
data.vunread_count().v);
|
||||||
|
});
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RepliesWidget::scrollDownClicked() {
|
void RepliesWidget::scrollDownClicked() {
|
||||||
if (QGuiApplication::keyboardModifiers() == Qt::ControlModifier) {
|
if (QGuiApplication::keyboardModifiers() == Qt::ControlModifier) {
|
||||||
showAtEnd();
|
showAtEnd();
|
||||||
|
@ -1692,11 +1768,21 @@ void RepliesWidget::readTill(not_null<HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
const auto was = _root->computeRepliesInboxReadTillFull();
|
const auto was = _root->computeRepliesInboxReadTillFull();
|
||||||
const auto now = item->id;
|
const auto now = item->id;
|
||||||
const auto fast = item->out();
|
if (now < was) {
|
||||||
if (was < now) {
|
return;
|
||||||
_root->setRepliesInboxReadTill(now);
|
}
|
||||||
|
const auto views = _root->Get<HistoryMessageViews>();
|
||||||
|
const auto wasReadTillId = views ? views->repliesInboxReadTillId : 0;
|
||||||
|
const auto wasUnreadCount = views ? views->repliesUnreadCount : -1;
|
||||||
|
const auto unreadCount = _replies->fullUnreadCountAfter(
|
||||||
|
now,
|
||||||
|
wasReadTillId,
|
||||||
|
wasUnreadCount);
|
||||||
|
const auto fast = item->out() || !unreadCount.has_value();
|
||||||
|
if (was < now || (fast && now == was)) {
|
||||||
|
_root->setRepliesInboxReadTill(now, unreadCount);
|
||||||
if (const auto post = _root->lookupDiscussionPostOriginal()) {
|
if (const auto post = _root->lookupDiscussionPostOriginal()) {
|
||||||
post->setRepliesInboxReadTill(now);
|
post->setRepliesInboxReadTill(now, unreadCount);
|
||||||
}
|
}
|
||||||
if (!_readRequestTimer.isActive()) {
|
if (!_readRequestTimer.isActive()) {
|
||||||
_readRequestTimer.callOnce(fast ? 0 : kReadRequestTimeout);
|
_readRequestTimer.callOnce(fast ? 0 : kReadRequestTimeout);
|
||||||
|
|
|
@ -198,6 +198,7 @@ private:
|
||||||
[[nodiscard]] MsgId replyToId() const;
|
[[nodiscard]] MsgId replyToId() const;
|
||||||
[[nodiscard]] HistoryItem *lookupRoot() const;
|
[[nodiscard]] HistoryItem *lookupRoot() const;
|
||||||
[[nodiscard]] bool computeAreComments() const;
|
[[nodiscard]] bool computeAreComments() const;
|
||||||
|
[[nodiscard]] std::optional<int> computeUnreadCount() const;
|
||||||
void orderWidgets();
|
void orderWidgets();
|
||||||
|
|
||||||
void pushReplyReturn(not_null<HistoryItem*> item);
|
void pushReplyReturn(not_null<HistoryItem*> item);
|
||||||
|
@ -208,6 +209,8 @@ private:
|
||||||
void recountChatWidth();
|
void recountChatWidth();
|
||||||
void replyToMessage(FullMsgId itemId);
|
void replyToMessage(FullMsgId itemId);
|
||||||
void refreshTopBarActiveChat();
|
void refreshTopBarActiveChat();
|
||||||
|
void refreshUnreadCountBadge();
|
||||||
|
void reloadUnreadCountIfNeeded();
|
||||||
|
|
||||||
void uploadFile(const QByteArray &fileContent, SendMediaType type);
|
void uploadFile(const QByteArray &fileContent, SendMediaType type);
|
||||||
bool confirmSendingFiles(
|
bool confirmSendingFiles(
|
||||||
|
@ -276,13 +279,13 @@ private:
|
||||||
bool _scrollDownIsShown = false;
|
bool _scrollDownIsShown = false;
|
||||||
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
||||||
|
|
||||||
Data::MessagesSlice _lastSlice;
|
|
||||||
bool _choosingAttach = false;
|
bool _choosingAttach = false;
|
||||||
|
|
||||||
base::Timer _readRequestTimer;
|
base::Timer _readRequestTimer;
|
||||||
bool _readRequestPending = false;
|
bool _readRequestPending = false;
|
||||||
mtpRequestId _readRequestId = 0;
|
mtpRequestId _readRequestId = 0;
|
||||||
|
|
||||||
|
mtpRequestId _reloadUnreadCountRequestId = 0;
|
||||||
bool _loaded = false;
|
bool _loaded = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -399,7 +399,8 @@ void SessionNavigation::showRepliesForMessage(
|
||||||
item->setRepliesMaxId(maxId->v);
|
item->setRepliesMaxId(maxId->v);
|
||||||
}
|
}
|
||||||
item->setRepliesInboxReadTill(
|
item->setRepliesInboxReadTill(
|
||||||
data.vread_inbox_max_id().value_or_empty());
|
data.vread_inbox_max_id().value_or_empty(),
|
||||||
|
data.vunread_count().v);
|
||||||
item->setRepliesOutboxReadTill(
|
item->setRepliesOutboxReadTill(
|
||||||
data.vread_outbox_max_id().value_or_empty());
|
data.vread_outbox_max_id().value_or_empty());
|
||||||
const auto post = _session->data().message(channelId, rootId);
|
const auto post = _session->data().message(channelId, rootId);
|
||||||
|
@ -409,7 +410,8 @@ void SessionNavigation::showRepliesForMessage(
|
||||||
post->setRepliesMaxId(maxId->v);
|
post->setRepliesMaxId(maxId->v);
|
||||||
}
|
}
|
||||||
post->setRepliesInboxReadTill(
|
post->setRepliesInboxReadTill(
|
||||||
data.vread_inbox_max_id().value_or_empty());
|
data.vread_inbox_max_id().value_or_empty(),
|
||||||
|
data.vunread_count().v);
|
||||||
post->setRepliesOutboxReadTill(
|
post->setRepliesOutboxReadTill(
|
||||||
data.vread_outbox_max_id().value_or_empty());
|
data.vread_outbox_max_id().value_or_empty());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue