Use only shared media code for pinned tracking.

This commit is contained in:
John Preston 2020-10-21 14:07:49 +03:00
parent 59b4c5dad9
commit cd5cad72bd
17 changed files with 162 additions and 273 deletions

View file

@ -400,8 +400,6 @@ PRIVATE
data/data_photo.h data/data_photo.h
data/data_photo_media.cpp data/data_photo_media.cpp
data/data_photo_media.h data/data_photo_media.h
data/data_pinned_messages.cpp
data/data_pinned_messages.h
data/data_poll.cpp data/data_poll.cpp
data/data_poll.h data/data_poll.h
data/data_pts_waiter.cpp data/data_pts_waiter.cpp

View file

@ -3570,6 +3570,9 @@ void ApiWrap::sharedMediaDone(
parsed.noSkipRange, parsed.noSkipRange,
parsed.fullCount parsed.fullCount
)); ));
if (type == SharedMediaType::Pinned && !parsed.messageIds.empty()) {
peer->setHasPinnedMessages(true);
}
} }
void ApiWrap::requestUserPhotos( void ApiWrap::requestUserPhotos(

View file

@ -444,7 +444,7 @@ PinMessageBox::PinMessageBox(
: _peer(peer) : _peer(peer)
, _api(&peer->session().mtp()) , _api(&peer->session().mtp())
, _msgId(msgId) , _msgId(msgId)
, _pinningOld(msgId < peer->topPinnedMessageId()) , _pinningOld(msgId < Data::ResolveTopPinnedId(peer))
, _text( , _text(
this, this,
(_pinningOld (_pinningOld

View file

@ -57,7 +57,7 @@ struct PeerUpdate {
Notifications = (1 << 4), Notifications = (1 << 4),
Migration = (1 << 5), Migration = (1 << 5),
UnavailableReason = (1 << 6), UnavailableReason = (1 << 6),
PinnedMessage = (1 << 7), PinnedMessages = (1 << 7),
IsBlocked = (1 << 8), IsBlocked = (1 << 8),
// For users // For users

View file

@ -769,9 +769,7 @@ void ApplyChannelUpdate(
} }
} }
if (const auto pinned = update.vpinned_msg_id()) { if (const auto pinned = update.vpinned_msg_id()) {
channel->setTopPinnedMessageId(pinned->v); SetTopPinnedMessageId(channel, pinned->v);
} else {
channel->clearPinnedMessages();
} }
if (channel->isMegagroup()) { if (channel->isMegagroup()) {
const auto stickerSet = update.vstickerset(); const auto stickerSet = update.vstickerset();

View file

@ -333,9 +333,7 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
return QString(); return QString();
})); }));
if (const auto pinned = update.vpinned_msg_id()) { if (const auto pinned = update.vpinned_msg_id()) {
chat->setTopPinnedMessageId(pinned->v); SetTopPinnedMessageId(chat, pinned->v);
} else {
chat->clearPinnedMessages();
} }
chat->checkFolder(update.vfolder_id().value_or_empty()); chat->checkFolder(update.vfolder_id().value_or_empty());
chat->fullUpdated(); chat->fullUpdated();

View file

@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_pinned_messages.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "base/crc32hash.h" #include "base/crc32hash.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -464,54 +463,13 @@ bool PeerData::canEditMessagesIndefinitely() const {
Unexpected("Peer type in PeerData::canEditMessagesIndefinitely."); Unexpected("Peer type in PeerData::canEditMessagesIndefinitely.");
} }
MsgId PeerData::topPinnedMessageId() const { bool PeerData::hasPinnedMessages() const {
return _pinnedMessages ? _pinnedMessages->topId() : 0; return _hasPinnedMessages;
} }
void PeerData::ensurePinnedMessagesCreated() { void PeerData::setHasPinnedMessages(bool has) {
if (!_pinnedMessages) { _hasPinnedMessages = has;
_pinnedMessages = std::make_unique<Data::PinnedMessages>(this); session().changes().peerUpdated(this, UpdateFlag::PinnedMessages);
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
}
}
void PeerData::removeEmptyPinnedMessages() {
if (_pinnedMessages && _pinnedMessages->empty()) {
_pinnedMessages = nullptr;
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
}
}
bool PeerData::messageIdTooSmall(MsgId messageId) const {
if (const auto channel = asChannel()) {
return (messageId <= channel->availableMinId());
}
return false;
}
void PeerData::setTopPinnedMessageId(MsgId messageId) {
if (messageIdTooSmall(messageId)) {
clearPinnedMessages();
return;
}
const auto hiddenId = session().settings().hiddenPinnedMessageId(id);
if (hiddenId != 0 && hiddenId != messageId) {
session().settings().setHiddenPinnedMessageId(id, 0);
session().saveSettingsDelayed();
}
ensurePinnedMessagesCreated();
_pinnedMessages->setTopId(messageId);
}
void PeerData::clearPinnedMessages() {
if (session().settings().hiddenPinnedMessageId(id) != 0) {
session().settings().setHiddenPinnedMessageId(id, 0);
session().saveSettingsDelayed();
}
session().storage().remove(Storage::SharedMediaRemoveAll(
id,
Storage::SharedMediaType::Pinned));
removeEmptyPinnedMessages();
} }
bool PeerData::canExportChatHistory() const { bool PeerData::canExportChatHistory() const {
@ -1038,4 +996,36 @@ std::optional<QString> RestrictionError(
return std::nullopt; return std::nullopt;
} }
void SetTopPinnedMessageId(not_null<PeerData*> peer, MsgId messageId) {
if (const auto channel = peer->asChannel()) {
if (messageId <= channel->availableMinId()) {
return;
}
}
auto &session = peer->session();
const auto hiddenId = session.settings().hiddenPinnedMessageId(peer->id);
if (hiddenId != 0 && hiddenId != messageId) {
session.settings().setHiddenPinnedMessageId(peer->id, 0);
session.saveSettingsDelayed();
}
session.storage().add(Storage::SharedMediaAddExisting(
peer->id,
Storage::SharedMediaType::Pinned,
messageId,
{ messageId, ServerMaxMsgId }));
peer->setHasPinnedMessages(true);
}
MsgId ResolveTopPinnedId(not_null<PeerData*> peer) {
const auto slice = peer->session().storage().snapshot(
Storage::SharedMediaQuery(
Storage::SharedMediaKey(
peer->id,
Storage::SharedMediaType::Pinned,
ServerMaxMsgId - 1),
1,
1));
return slice.messageIds.empty() ? 0 : slice.messageIds.back();
}
} // namespace Data } // namespace Data

View file

@ -29,8 +29,6 @@ class Session;
namespace Data { namespace Data {
class Session; class Session;
class PinnedMessages;
struct PinnedAroundId;
int PeerColorIndex(PeerId peerId); int PeerColorIndex(PeerId peerId);
int PeerColorIndex(int32 bareId); int PeerColorIndex(int32 bareId);
@ -328,12 +326,9 @@ public:
[[nodiscard]] bool canPinMessages() const; [[nodiscard]] bool canPinMessages() const;
[[nodiscard]] bool canEditMessagesIndefinitely() const; [[nodiscard]] bool canEditMessagesIndefinitely() const;
[[nodiscard]] MsgId topPinnedMessageId() const;
void setTopPinnedMessageId(MsgId messageId); [[nodiscard]] bool hasPinnedMessages() const;
void clearPinnedMessages(); void setHasPinnedMessages(bool has);
Data::PinnedMessages *currentPinnedMessages() const {
return _pinnedMessages.get();
}
[[nodiscard]] bool canExportChatHistory() const; [[nodiscard]] bool canExportChatHistory() const;
@ -412,10 +407,6 @@ private:
-> const std::vector<Data::UnavailableReason> &; -> const std::vector<Data::UnavailableReason> &;
void setUserpicChecked(PhotoId photoId, const ImageLocation &location); void setUserpicChecked(PhotoId photoId, const ImageLocation &location);
void ensurePinnedMessagesCreated();
void removeEmptyPinnedMessages();
[[nodiscard]] bool messageIdTooSmall(MsgId messageId) const;
static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL); static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL);
@ -433,7 +424,7 @@ private:
base::flat_set<QChar> _nameFirstLetters; base::flat_set<QChar> _nameFirstLetters;
crl::time _lastFullUpdate = 0; crl::time _lastFullUpdate = 0;
std::unique_ptr<Data::PinnedMessages> _pinnedMessages; bool _hasPinnedMessages = false;
Settings _settings = { kSettingsUnknown }; Settings _settings = { kSettingsUnknown };
BlockStatus _blockStatus = BlockStatus::Unknown; BlockStatus _blockStatus = BlockStatus::Unknown;
@ -451,4 +442,7 @@ std::optional<QString> RestrictionError(
not_null<PeerData*> peer, not_null<PeerData*> peer,
ChatRestriction restriction); ChatRestriction restriction);
void SetTopPinnedMessageId(not_null<PeerData*> peer, MsgId messageId);
[[nodiscard]] MsgId ResolveTopPinnedId(not_null<PeerData*> peer);
} // namespace Data } // namespace Data

View file

@ -1,88 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_pinned_messages.h"
#include "data/data_peer.h"
#include "main/main_session.h"
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
#include "data/data_shared_media.h"
#include "data/data_sparse_ids.h"
namespace Data {
namespace {
constexpr auto PinnedType = Storage::SharedMediaType::Pinned;
using Storage::SharedMediaQuery;
using Storage::SharedMediaKey;
using Storage::SharedMediaResult;
} // namespace
PinnedMessages::PinnedMessages(not_null<PeerData*> peer)
: _peer(peer)
, _storage(_peer->session().storage()) {
}
bool PinnedMessages::empty() const {
return _storage.empty(SharedMediaKey(_peer->id, PinnedType, 0));
}
MsgId PinnedMessages::topId() const {
const auto slice = _storage.snapshot(
SharedMediaQuery(
SharedMediaKey(_peer->id, PinnedType, ServerMaxMsgId),
1,
1));
return slice.messageIds.empty() ? 0 : slice.messageIds.back();
}
rpl::producer<PinnedAroundId> PinnedMessages::viewer(
MsgId aroundId,
int limit) const {
return SharedMediaViewer(
&_peer->session(),
SharedMediaKey(_peer->id, PinnedType, aroundId),
limit,
limit
) | rpl::map([](const SparseIdsSlice &result) {
auto data = PinnedAroundId();
data.fullCount = result.fullCount();
data.skippedBefore = result.skippedBefore();
data.skippedAfter = result.skippedAfter();
const auto count = result.size();
data.ids.reserve(count);
for (auto i = 0; i != count; ++i) {
data.ids.push_back(result[i]);
}
return data;
});
}
void PinnedMessages::setTopId(MsgId messageId) {
while (true) {
auto top = topId();
if (top > messageId) {
_storage.remove(Storage::SharedMediaRemoveOne(
_peer->id,
PinnedType,
top));
} else if (top == messageId) {
return;
} else {
break;
}
}
_storage.add(Storage::SharedMediaAddNew(
_peer->id,
PinnedType,
messageId));
}
} // namespace Data

View file

@ -1,43 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_messages.h"
#include "base/weak_ptr.h"
namespace Storage {
class Facade;
} // namespace Storage
namespace Data {
struct PinnedAroundId {
std::vector<MsgId> ids;
std::optional<int> skippedBefore;
std::optional<int> skippedAfter;
std::optional<int> fullCount;
};
class PinnedMessages final : public base::has_weak_ptr {
public:
explicit PinnedMessages(not_null<PeerData*> peer);
[[nodiscard]] bool empty() const;
[[nodiscard]] MsgId topId() const;
[[nodiscard]] rpl::producer<PinnedAroundId> viewer(
MsgId aroundId,
int limit) const;
void setTopId(MsgId messageId);
private:
const not_null<PeerData*> _peer;
Storage::Facade &_storage;
};
} // namespace Data

View file

@ -262,9 +262,7 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
user->setBotInfoVersion(-1); user->setBotInfoVersion(-1);
} }
if (const auto pinned = update.vpinned_msg_id()) { if (const auto pinned = update.vpinned_msg_id()) {
user->setTopPinnedMessageId(pinned->v); SetTopPinnedMessageId(user, pinned->v);
} else {
user->clearPinnedMessages();
} }
user->setFullFlags(update.vflags().v); user->setFullFlags(update.vflags().v);
user->setIsBlocked(update.is_blocked()); user->setIsBlocked(update.is_blocked());

View file

@ -705,6 +705,9 @@ not_null<HistoryItem*> History::addNewToBack(
sharedMediaTypes, sharedMediaTypes,
item->id, item->id,
{ from, till })); { from, till }));
if (sharedMediaTypes.test(Storage::SharedMediaType::Pinned)) {
peer->setHasPinnedMessages(true);
}
} }
} }
if (item->from()->id) { if (item->from()->id) {
@ -1008,6 +1011,7 @@ void History::applyServiceChanges(
Storage::SharedMediaType::Pinned, Storage::SharedMediaType::Pinned,
{ id }, { id },
{ id, ServerMaxMsgId })); { id, ServerMaxMsgId }));
peer->setHasPinnedMessages(true);
} }
}); });
} }
@ -1348,6 +1352,9 @@ void History::addToSharedMedia(
type, type,
std::move(medias[i]), std::move(medias[i]),
{ from, till })); { from, till }));
if (type == Storage::SharedMediaType::Pinned) {
peer->setHasPinnedMessages(true);
}
} }
} }
} }

View file

@ -355,6 +355,7 @@ void HistoryItem::setIsPinned(bool pinned) {
Storage::SharedMediaType::Pinned, Storage::SharedMediaType::Pinned,
id, id,
{ id, id })); { id, id }));
history()->peer->setHasPinnedMessages(true);
} else { } else {
_flags &= ~MTPDmessage::Flag::f_pinned; _flags &= ~MTPDmessage::Flag::f_pinned;
history()->session().storage().remove(Storage::SharedMediaRemoveOne( history()->session().storage().remove(Storage::SharedMediaRemoveOne(
@ -489,12 +490,12 @@ void HistoryItem::indexAsNewItem() {
addToUnreadMentions(UnreadMentionType::New); addToUnreadMentions(UnreadMentionType::New);
if (const auto types = sharedMediaTypes()) { if (const auto types = sharedMediaTypes()) {
_history->session().storage().add(Storage::SharedMediaAddNew( _history->session().storage().add(Storage::SharedMediaAddNew(
history()->peer->id, _history->peer->id,
types, types,
id)); id));
} if (types.test(Storage::SharedMediaType::Pinned)) {
if (isPinned()) { _history->peer->setHasPinnedMessages(true);
_history->peer->setTopPinnedMessageId(id); }
} }
//if (const auto channel = history()->peer->asChannel()) { // #feed //if (const auto channel = history()->peer->asChannel()) { // #feed
// if (const auto feed = channel->feed()) { // if (const auto feed = channel->feed()) {

View file

@ -567,7 +567,7 @@ HistoryWidget::HistoryWidget(
| UpdateFlag::ChannelLinkedChat | UpdateFlag::ChannelLinkedChat
| UpdateFlag::Slowmode | UpdateFlag::Slowmode
| UpdateFlag::BotStartToken | UpdateFlag::BotStartToken
| UpdateFlag::PinnedMessage | UpdateFlag::PinnedMessages
) | rpl::filter([=](const Data::PeerUpdate &update) { ) | rpl::filter([=](const Data::PeerUpdate &update) {
return (update.peer.get() == _peer); return (update.peer.get() == _peer);
}) | rpl::map([](const Data::PeerUpdate &update) { }) | rpl::map([](const Data::PeerUpdate &update) {
@ -608,7 +608,7 @@ HistoryWidget::HistoryWidget(
| UpdateFlag::ChannelLinkedChat)) { | UpdateFlag::ChannelLinkedChat)) {
handlePeerUpdate(); handlePeerUpdate();
} }
if (_pinnedTracker && (flags & UpdateFlag::PinnedMessage)) { if (_pinnedTracker && (flags & UpdateFlag::PinnedMessages)) {
checkPinnedBarState(); checkPinnedBarState();
} }
}, lifetime()); }, lifetime());
@ -5184,7 +5184,7 @@ void HistoryWidget::updatePinnedViewer() {
}(); }();
const auto lessThanId = item const auto lessThanId = item
? (item->data()->id + (offset > 0 ? 1 : 0)) ? (item->data()->id + (offset > 0 ? 1 : 0))
: (_history->peer->topPinnedMessageId() + 1); : (ServerMaxMsgId - 1);
_pinnedTracker->trackAround(lessThanId); _pinnedTracker->trackAround(lessThanId);
} }
@ -5193,13 +5193,7 @@ void HistoryWidget::setupPinnedTracker() {
_pinnedTracker = std::make_unique<HistoryView::PinnedTracker>(_history); _pinnedTracker = std::make_unique<HistoryView::PinnedTracker>(_history);
_pinnedBar = nullptr; _pinnedBar = nullptr;
Info::Profile::SharedMediaCountValue( checkPinnedBarState();
_history->peer,
_migrated ? _migrated->peer.get() : nullptr,
Storage::SharedMediaType::Pinned
) | rpl::start_with_next([=] {
checkPinnedBarState();
}, _pinnedTracker->lifetime());
} }
void HistoryWidget::checkPinnedBarState() { void HistoryWidget::checkPinnedBarState() {
@ -5208,7 +5202,7 @@ void HistoryWidget::checkPinnedBarState() {
const auto hiddenId = _peer->canPinMessages() const auto hiddenId = _peer->canPinMessages()
? MsgId(0) ? MsgId(0)
: session().settings().hiddenPinnedMessageId(_peer->id); : session().settings().hiddenPinnedMessageId(_peer->id);
const auto currentPinnedId = _peer->topPinnedMessageId(); const auto currentPinnedId = Data::ResolveTopPinnedId(_peer);
if (currentPinnedId == hiddenId) { if (currentPinnedId == hiddenId) {
if (_pinnedBar) { if (_pinnedBar) {
_pinnedTracker->reset(); _pinnedTracker->reset();
@ -5601,7 +5595,7 @@ void HistoryWidget::hidePinnedMessage() {
if (_peer->canPinMessages()) { if (_peer->canPinMessages()) {
unpinMessage({ peerToChannel(_peer->id), id.message }); unpinMessage({ peerToChannel(_peer->id), id.message });
} else { } else {
const auto top = _peer->topPinnedMessageId(); const auto top = Data::ResolveTopPinnedId(_peer);
if (top) { if (top) {
session().settings().setHiddenPinnedMessageId(_peer->id, top); session().settings().setHiddenPinnedMessageId(_peer->id, top);
session().saveSettingsDelayed(); session().saveSettingsDelayed();

View file

@ -34,7 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_pinned_messages.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_sparse_ids.h" #include "data/data_sparse_ids.h"
#include "data/data_shared_media.h" #include "data/data_shared_media.h"

View file

@ -8,9 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_pinned_tracker.h" #include "history/view/history_view_pinned_tracker.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_pinned_messages.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_shared_media.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "storage/storage_facade.h" #include "storage/storage_facade.h"
@ -21,16 +21,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView { namespace HistoryView {
namespace{ namespace{
constexpr auto kLoadedLimit = 4; constexpr auto kLoadedLimit = 5;
constexpr auto kChangeViewerLimit = 2;
} // namespace } // namespace
PinnedTracker::PinnedTracker(not_null<History*> history) : _history(history) { PinnedTracker::PinnedTracker(not_null<History*> history) : _history(history) {
_history->session().changes().peerFlagsValue( _history->session().changes().peerFlagsValue(
_history->peer, _history->peer,
Data::PeerUpdate::Flag::PinnedMessage Data::PeerUpdate::Flag::PinnedMessages
) | rpl::start_with_next([=] { ) | rpl::map([=] {
refreshData(); return _history->peer->hasPinnedMessages();
}) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool has) {
if (has) {
refreshViewer();
} else {
clear();
}
}, _lifetime); }, _lifetime);
} }
@ -48,60 +56,85 @@ PinnedId PinnedTracker::currentMessageId() const {
return _current.current(); return _current.current();
} }
void PinnedTracker::refreshData() { void PinnedTracker::refreshViewer() {
const auto now = _history->peer->currentPinnedMessages(); if (_viewerAroundId == _aroundId) {
if (!now) { return;
_dataLifetime.destroy(); }
_current = PinnedId(); _dataLifetime.destroy();
} else if (_data.get() != now) { _viewerAroundId = _aroundId;
_dataLifetime.destroy(); SharedMediaViewer(
_data = now; &_history->peer->session(),
if (_aroundId) { Storage::SharedMediaKey(
setupViewer(now); _history->peer->id,
Storage::SharedMediaType::Pinned,
_viewerAroundId),
kLoadedLimit,
kLoadedLimit
) | rpl::start_with_next([=](const SparseIdsSlice &result) {
_slice.fullCount = result.fullCount();
_slice.skippedBefore = result.skippedBefore();
_slice.skippedAfter = result.skippedAfter();
_slice.ids.clear();
const auto count = result.size();
_slice.ids.reserve(count);
for (auto i = 0; i != count; ++i) {
_slice.ids.push_back(result[i]);
}
refreshCurrentFromSlice();
if (_slice.fullCount == 0) {
_history->peer->setHasPinnedMessages(false);
}
}, _dataLifetime);
}
void PinnedTracker::refreshCurrentFromSlice() {
const auto i = ranges::lower_bound(_slice.ids, _aroundId);
const auto empty = _slice.ids.empty();
const auto before = int(i - begin(_slice.ids));
const auto after = int(end(_slice.ids) - i);
const auto haveValidData = (before > 0 || _slice.skippedBefore == 0)
&& (after > 0 || _slice.skippedAfter == 0);
const auto nearEnd = !haveValidData
|| (before <= kChangeViewerLimit && _slice.skippedBefore != 0)
|| (after <= kChangeViewerLimit && _slice.skippedAfter != 0);
if (haveValidData) {
const auto count = std::max(
_slice.fullCount.value_or(1),
int(_slice.ids.size()));
const auto index = _slice.skippedBefore.has_value()
? (*_slice.skippedBefore + before)
: _slice.skippedAfter.has_value()
? (count - *_slice.skippedAfter - after)
: 1;
if (i != begin(_slice.ids)) {
_current = PinnedId{ *(i - 1), index - 1, count };
} else if (!_slice.ids.empty()) {
_current = PinnedId{ _slice.ids.front(), 0, count };
} else {
_current = PinnedId();
} }
} }
if (nearEnd) {
refreshViewer();
}
}
void PinnedTracker::clear() {
_dataLifetime.destroy();
_viewerAroundId = 0;
_current = PinnedId();
} }
void PinnedTracker::trackAround(MsgId messageId) { void PinnedTracker::trackAround(MsgId messageId) {
if (_aroundId == messageId) { if (_aroundId == messageId) {
return; return;
} }
_dataLifetime.destroy();
_aroundId = messageId; _aroundId = messageId;
if (!_aroundId) { if (!_aroundId) {
_current = PinnedId(); clear();
} else if (const auto now = _data.get()) { } else {
setupViewer(now); refreshCurrentFromSlice();
} }
} }
void PinnedTracker::setupViewer(not_null<Data::PinnedMessages*> data) {
data->viewer(
_aroundId,
kLoadedLimit + 2
) | rpl::start_with_next([=](const Data::PinnedAroundId &snapshot) {
const auto i = ranges::lower_bound(snapshot.ids, _aroundId);
const auto empty = snapshot.ids.empty();
const auto before = int(i - begin(snapshot.ids));
const auto after = int(end(snapshot.ids) - i);
if (snapshot.ids.empty()) {
_current = PinnedId();
return;
}
const auto count = std::max(
snapshot.fullCount.value_or(1),
int(snapshot.ids.size()));
const auto index = snapshot.skippedBefore.has_value()
? (*snapshot.skippedBefore + before)
: snapshot.skippedAfter.has_value()
? (count - *snapshot.skippedAfter - after)
: 1;
if (i != begin(snapshot.ids)) {
_current = PinnedId{ *(i - 1), index - 1, count };
} else if (snapshot.skippedBefore == 0) {
_current = PinnedId{ snapshot.ids.front(), 0, count };
}
}, _dataLifetime);
}
} // namespace HistoryView } // namespace HistoryView

View file

@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History; class History;
namespace Data { namespace Data {
class PinnedMessages;
enum class LoadDirection : char; enum class LoadDirection : char;
} // namespace Data } // namespace Data
@ -46,16 +45,24 @@ public:
} }
private: private:
void refreshData(); struct Slice {
void setupViewer(not_null<Data::PinnedMessages*> data); std::vector<MsgId> ids;
std::optional<int> fullCount;
std::optional<int> skippedBefore;
std::optional<int> skippedAfter;
};
void clear();
void refreshViewer();
void refreshCurrentFromSlice();
const not_null<History*> _history; const not_null<History*> _history;
base::weak_ptr<Data::PinnedMessages> _data;
rpl::variable<PinnedId> _current; rpl::variable<PinnedId> _current;
rpl::lifetime _dataLifetime; rpl::lifetime _dataLifetime;
MsgId _aroundId = 0; MsgId _aroundId = 0;
MsgId _viewerAroundId = 0;
Slice _slice;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;