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_media.cpp
data/data_photo_media.h
data/data_pinned_messages.cpp
data/data_pinned_messages.h
data/data_poll.cpp
data/data_poll.h
data/data_pts_waiter.cpp

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "data/data_histories.h"
#include "data/data_pinned_messages.h"
#include "base/unixtime.h"
#include "base/crc32hash.h"
#include "lang/lang_keys.h"
@ -464,54 +463,13 @@ bool PeerData::canEditMessagesIndefinitely() const {
Unexpected("Peer type in PeerData::canEditMessagesIndefinitely.");
}
MsgId PeerData::topPinnedMessageId() const {
return _pinnedMessages ? _pinnedMessages->topId() : 0;
bool PeerData::hasPinnedMessages() const {
return _hasPinnedMessages;
}
void PeerData::ensurePinnedMessagesCreated() {
if (!_pinnedMessages) {
_pinnedMessages = std::make_unique<Data::PinnedMessages>(this);
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();
void PeerData::setHasPinnedMessages(bool has) {
_hasPinnedMessages = has;
session().changes().peerUpdated(this, UpdateFlag::PinnedMessages);
}
bool PeerData::canExportChatHistory() const {
@ -1038,4 +996,36 @@ std::optional<QString> RestrictionError(
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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/data_channel.h"
#include "data/data_pinned_messages.h"
#include "data/data_changes.h"
#include "data/data_sparse_ids.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 "data/data_changes.h"
#include "data/data_pinned_messages.h"
#include "data/data_peer.h"
#include "data/data_channel.h"
#include "data/data_shared_media.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "storage/storage_facade.h"
@ -21,16 +21,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView {
namespace{
constexpr auto kLoadedLimit = 4;
constexpr auto kLoadedLimit = 5;
constexpr auto kChangeViewerLimit = 2;
} // namespace
PinnedTracker::PinnedTracker(not_null<History*> history) : _history(history) {
_history->session().changes().peerFlagsValue(
_history->peer,
Data::PeerUpdate::Flag::PinnedMessage
) | rpl::start_with_next([=] {
refreshData();
Data::PeerUpdate::Flag::PinnedMessages
) | rpl::map([=] {
return _history->peer->hasPinnedMessages();
}) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool has) {
if (has) {
refreshViewer();
} else {
clear();
}
}, _lifetime);
}
@ -48,60 +56,85 @@ PinnedId PinnedTracker::currentMessageId() const {
return _current.current();
}
void PinnedTracker::refreshData() {
const auto now = _history->peer->currentPinnedMessages();
if (!now) {
_dataLifetime.destroy();
_current = PinnedId();
} else if (_data.get() != now) {
_dataLifetime.destroy();
_data = now;
if (_aroundId) {
setupViewer(now);
void PinnedTracker::refreshViewer() {
if (_viewerAroundId == _aroundId) {
return;
}
_dataLifetime.destroy();
_viewerAroundId = _aroundId;
SharedMediaViewer(
&_history->peer->session(),
Storage::SharedMediaKey(
_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) {
if (_aroundId == messageId) {
return;
}
_dataLifetime.destroy();
_aroundId = messageId;
if (!_aroundId) {
_current = PinnedId();
} else if (const auto now = _data.get()) {
setupViewer(now);
clear();
} else {
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

View File

@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History;
namespace Data {
class PinnedMessages;
enum class LoadDirection : char;
} // namespace Data
@ -46,16 +45,24 @@ public:
}
private:
void refreshData();
void setupViewer(not_null<Data::PinnedMessages*> data);
struct Slice {
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;
base::weak_ptr<Data::PinnedMessages> _data;
rpl::variable<PinnedId> _current;
rpl::lifetime _dataLifetime;
MsgId _aroundId = 0;
MsgId _viewerAroundId = 0;
Slice _slice;
rpl::lifetime _lifetime;