mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-08-31 14:38:15 +00:00
Track and display unread count in discussions.
This commit is contained in:
@@ -134,16 +134,17 @@ struct MessageUpdate {
|
||||
enum class Flag : uint32 {
|
||||
None = 0,
|
||||
|
||||
Edited = (1U << 0),
|
||||
Destroyed = (1U << 1),
|
||||
DialogRowRepaint = (1U << 2),
|
||||
DialogRowRefresh = (1U << 3),
|
||||
NewAdded = (1U << 4),
|
||||
ReplyMarkup = (1U << 5),
|
||||
BotCallbackSent = (1U << 6),
|
||||
NewMaybeAdded = (1U << 7),
|
||||
Edited = (1U << 0),
|
||||
Destroyed = (1U << 1),
|
||||
DialogRowRepaint = (1U << 2),
|
||||
DialogRowRefresh = (1U << 3),
|
||||
NewAdded = (1U << 4),
|
||||
ReplyMarkup = (1U << 5),
|
||||
BotCallbackSent = (1U << 6),
|
||||
NewMaybeAdded = (1U << 7),
|
||||
RepliesUnreadCount = (1U << 8),
|
||||
|
||||
LastUsedBit = (1U << 7),
|
||||
LastUsedBit = (1U << 7),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
@@ -101,6 +101,15 @@ rpl::producer<MessagesSlice> RepliesList::source(
|
||||
_partLoaded.events(
|
||||
) | 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();
|
||||
return lifetime;
|
||||
};
|
||||
@@ -169,6 +178,64 @@ rpl::producer<int> RepliesList::fullCount() const {
|
||||
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) {
|
||||
injectRootMessage(viewer);
|
||||
ranges::reverse(viewer->slice.ids);
|
||||
|
@@ -31,6 +31,11 @@ public:
|
||||
|
||||
[[nodiscard]] rpl::producer<int> fullCount() const;
|
||||
|
||||
[[nodiscard]] std::optional<int> fullUnreadCountAfter(
|
||||
MsgId readTillId,
|
||||
MsgId wasReadTillId,
|
||||
std::optional<int> wasUnreadCountAfter) const;
|
||||
|
||||
private:
|
||||
struct Viewer;
|
||||
|
||||
|
Reference in New Issue
Block a user