Switch between pinned messages in chat.

This commit is contained in:
John Preston 2020-10-09 19:23:53 +03:00
parent ec35e3f081
commit b9f40e35cd
12 changed files with 466 additions and 69 deletions

View File

@ -564,6 +564,8 @@ PRIVATE
history/view/history_view_message.cpp
history/view/history_view_message.h
history/view/history_view_object.h
history/view/history_view_pinned_tracker.cpp
history/view/history_view_pinned_tracker.h
history/view/history_view_replies_section.cpp
history/view/history_view_replies_section.h
history/view/history_view_schedule_box.cpp

View File

@ -161,6 +161,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_deleted" = "Deleted Account";
"lng_deleted_message" = "Deleted message";
"lng_pinned_message" = "Pinned message";
"lng_pinned_previous" = "Previous message";
"lng_pinned_poll" = "Pinned poll";
"lng_pinned_quiz" = "Pinned quiz";
"lng_pinned_unpin_sure" = "Would you like to unpin this message?";

View File

@ -469,12 +469,14 @@ void PeerData::ensurePinnedMessagesCreated() {
if (!_pinnedMessages) {
_pinnedMessages = std::make_unique<Data::PinnedMessages>(
peerToChannel(id));
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
}
}
void PeerData::removeEmptyPinnedMessages() {
if (_pinnedMessages && _pinnedMessages->empty()) {
_pinnedMessages = nullptr;
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
}
}
@ -511,8 +513,8 @@ void PeerData::addPinnedMessage(MsgId messageId) {
void PeerData::addPinnedSlice(
std::vector<MsgId> &&ids,
MsgId from,
MsgId till) {
MsgRange noSkipRange,
std::optional<int> count) {
const auto min = [&] {
if (const auto channel = asChannel()) {
return channel->availableMinId();
@ -522,14 +524,14 @@ void PeerData::addPinnedSlice(
ids.erase(
ranges::remove_if(ids, [&](MsgId id) { return id <= min; }),
end(ids));
if (from <= min) {
from = 0;
if (noSkipRange.from <= min) {
noSkipRange.from = 0;
}
if (ids.empty() && !_pinnedMessages) {
return;
}
ensurePinnedMessagesCreated();
_pinnedMessages->add(std::move(ids), from, till, std::nullopt);
_pinnedMessages->add(std::move(ids), noSkipRange, std::nullopt);
}
void PeerData::removePinnedMessage(MsgId messageId) {

View File

@ -30,6 +30,7 @@ namespace Data {
class Session;
class PinnedMessages;
struct PinnedAroundId;
int PeerColorIndex(PeerId peerId);
int PeerColorIndex(int32 bareId);
@ -331,8 +332,14 @@ public:
void setTopPinnedMessageId(MsgId messageId);
void clearPinnedMessages(MsgId lessThanId = ServerMaxMsgId);
void addPinnedMessage(MsgId messageId);
void addPinnedSlice(std::vector<MsgId> &&ids, MsgId from, MsgId till);
void addPinnedSlice(
std::vector<MsgId> &&ids,
MsgRange noSkipRange,
std::optional<int> count);
void removePinnedMessage(MsgId messageId);
Data::PinnedMessages *currentPinnedMessages() const {
return _pinnedMessages.get();
}
[[nodiscard]] bool canExportChatHistory() const;

View File

@ -25,6 +25,25 @@ MsgId PinnedMessages::topId() const {
return slice.messageIds.empty() ? 0 : slice.messageIds.back().fullId.msg;
}
rpl::producer<PinnedAroundId> PinnedMessages::viewer(
MsgId aroundId,
int limit) const {
return _list.viewer(MessagesQuery{
.aroundId = position(aroundId),
.limitBefore = limit,
.limitAfter = limit
}) | rpl::map([](const MessagesResult &result) {
auto data = PinnedAroundId();
data.fullCount = result.count;
data.skippedBefore = result.skippedBefore;
data.skippedAfter = result.skippedAfter;
data.ids = result.messageIds | ranges::view::transform(
[](MessagePosition position) { return position.fullId.msg; }
) | ranges::to_vector;
return data;
});
}
MessagePosition PinnedMessages::position(MsgId id) const {
return MessagePosition{
.fullId = FullMsgId(_channelId, id),
@ -37,8 +56,7 @@ void PinnedMessages::add(MsgId messageId) {
void PinnedMessages::add(
std::vector<MsgId> &&ids,
MsgId from,
MsgId till,
MsgRange range,
std::optional<int> count) {
auto positions = ids | ranges::view::transform([&](MsgId id) {
return position(id);
@ -47,16 +65,14 @@ void PinnedMessages::add(
_list.addSlice(
std::move(positions),
MessagesRange{
.from = from ? position(from) : MinMessagePosition,
.till = position(till)
.from = range.from ? position(range.from) : MinMessagePosition,
.till = position(range.till)
},
count);
}
void PinnedMessages::remove(MsgId messageId) {
_list.removeOne(MessagePosition{
.fullId = FullMsgId(0, messageId),
});
_list.removeOne(position(messageId));
}
void PinnedMessages::setTopId(MsgId messageId) {
@ -70,19 +86,15 @@ void PinnedMessages::setTopId(MsgId messageId) {
break;
}
}
const auto position = MessagePosition{
.fullId = FullMsgId(0, messageId),
};
const auto wrapped = position(messageId);
_list.addSlice(
{ position },
{ .from = position, .till = MaxMessagePosition },
{ wrapped },
{ .from = wrapped, .till = MaxMessagePosition },
std::nullopt);
}
void PinnedMessages::clearLessThanId(MsgId messageId) {
_list.removeLessThan(MessagePosition{
.fullId = FullMsgId(0, messageId),
});
_list.removeLessThan(position(messageId));
}
} // namespace Data

View File

@ -8,21 +8,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "data/data_messages.h"
#include "base/weak_ptr.h"
namespace Data {
class PinnedMessages final {
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(ChannelId channelId);
[[nodiscard]] bool empty() const;
[[nodiscard]] MsgId topId() const;
[[nodiscard]] rpl::producer<PinnedAroundId> viewer(
MsgId aroundId,
int limit) const;
void add(MsgId messageId);
void add(
std::vector<MsgId> &&ids,
MsgId from,
MsgId till,
MsgRange range,
std::optional<int> count);
void remove(MsgId messageId);

View File

@ -1161,7 +1161,7 @@ void History::addEdgesToSharedMedia() {
{},
{ from, till }));
}
peer->addPinnedSlice({}, from, till);
peer->addPinnedSlice({}, { from, till }, std::nullopt);
}
void History::addOlderSlice(const QVector<MTPMessage> &slice) {
@ -1358,7 +1358,7 @@ void History::addToSharedMedia(
{ from, till }));
}
}
peer->addPinnedSlice(std::move(pinned), from, till);
peer->addPinnedSlice(std::move(pinned), { from, till }, std::nullopt);
}
void History::calculateFirstUnreadMessage() {
@ -1785,16 +1785,19 @@ TimeId History::adjustedChatListTimeId() const {
}
void History::countScrollState(int top) {
countScrollTopItem(top);
if (scrollTopItem) {
scrollTopOffset = (top - scrollTopItem->block()->y() - scrollTopItem->y());
}
std::tie(scrollTopItem, scrollTopOffset) = findItemAndOffset(top);
}
void History::countScrollTopItem(int top) {
auto History::findItemAndOffset(int top) const -> std::pair<Element*, int> {
if (const auto element = findScrollTopItem(top)) {
return { element, (top - element->block()->y() - element->y()) };
}
return {};
}
auto History::findScrollTopItem(int top) const -> Element* {
if (isEmpty()) {
forgetScrollState();
return;
return nullptr;
}
auto itemIndex = 0;
@ -1813,8 +1816,7 @@ void History::countScrollTopItem(int top) {
const auto view = block->messages[itemIndex].get();
itemTop = block->y() + view->y();
if (itemTop <= top) {
scrollTopItem = view;
return;
return view;
}
}
if (--blockIndex >= 0) {
@ -1824,27 +1826,24 @@ void History::countScrollTopItem(int top) {
}
} while (true);
scrollTopItem = blocks.front()->messages.front().get();
} else {
// go forward through history while we don't find the last item that starts above
for (auto blocksCount = int(blocks.size()); blockIndex < blocksCount; ++blockIndex) {
const auto &block = blocks[blockIndex];
for (auto itemsCount = int(block->messages.size()); itemIndex < itemsCount; ++itemIndex) {
itemTop = block->y() + block->messages[itemIndex]->y();
if (itemTop > top) {
Assert(itemIndex > 0 || blockIndex > 0);
if (itemIndex > 0) {
scrollTopItem = block->messages[itemIndex - 1].get();
} else {
scrollTopItem = blocks[blockIndex - 1]->messages.back().get();
}
return;
}
}
itemIndex = 0;
}
scrollTopItem = blocks.back()->messages.back().get();
return blocks.front()->messages.front().get();
}
// go forward through history while we don't find the last item that starts above
for (auto blocksCount = int(blocks.size()); blockIndex < blocksCount; ++blockIndex) {
const auto &block = blocks[blockIndex];
for (auto itemsCount = int(block->messages.size()); itemIndex < itemsCount; ++itemIndex) {
itemTop = block->y() + block->messages[itemIndex]->y();
if (itemTop > top) {
Assert(itemIndex > 0 || blockIndex > 0);
if (itemIndex > 0) {
return block->messages[itemIndex - 1].get();
}
return blocks[blockIndex - 1]->messages.back().get();
}
}
itemIndex = 0;
}
return blocks.back()->messages.back().get();
}
void History::getNextScrollTopItem(HistoryBlock *block, int32 i) {

View File

@ -366,6 +366,8 @@ public:
// of the displayed window relative to the history start coordinate
void countScrollState(int top);
[[nodiscard]] std::pair<Element*, int> findItemAndOffset(int top) const;
MsgId nextNonHistoryEntryId();
bool folderKnown() const override;
@ -422,7 +424,7 @@ private:
void getNextScrollTopItem(HistoryBlock *block, int32 i);
// helper method for countScrollState(int top)
void countScrollTopItem(int top);
[[nodiscard]] Element *findScrollTopItem(int top) const;
// this method just removes a block from the blocks list
// when the last item from this block was detached and

View File

@ -88,6 +88,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "history/view/history_view_top_bar_widget.h"
#include "history/view/history_view_contact_status.h"
#include "history/view/history_view_pinned_tracker.h"
#include "base/qthelp_regex.h"
#include "ui/widgets/popup_menu.h"
#include "ui/text_options.h"
@ -1697,7 +1698,8 @@ void HistoryWidget::showHistory(
_history->showAtMsgId = _showAtMsgId;
destroyUnreadBarOnClose();
destroyPinnedBar();
showPinnedMessage(FullMsgId());
_pinnedTracker = nullptr;
_membersDropdown.destroy();
_scrollToAnimation.stop();
@ -1811,6 +1813,7 @@ void HistoryWidget::showHistory(
_updateHistoryItems.stop();
setupPinnedTracker();
pinnedMsgVisibilityUpdated();
if (_history->scrollTopItem
|| (_migrated && _migrated->scrollTopItem)
@ -2732,6 +2735,7 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) {
void HistoryWidget::onScroll() {
preloadHistoryIfNeeded();
visibleAreaUpdated();
updatePinnedViewer();
if (!_synteticScrollEvent) {
_lastUserScrolled = crl::now();
}
@ -3211,6 +3215,7 @@ void HistoryWidget::doneShow() {
handlePendingHistoryUpdate();
}
preloadHistoryIfNeeded();
updatePinnedViewer();
checkHistoryActivation();
App::wnd()->setInnerFocus();
}
@ -5226,14 +5231,66 @@ void HistoryWidget::updatePinnedBar(bool force) {
}
}
void HistoryWidget::updatePinnedViewer() {
if (_firstLoadRequest
|| _delayedShowAtRequest
|| _scroll->isHidden()
|| !_history
|| !_historyInited) {
return;
}
const auto [item, offset] = [&] {
auto visibleTop = _scroll->scrollTop();
if (_migrated
&& _history->loadedAtBottom()
&& _migrated->loadedAtTop()) {
visibleTop -= _migrated->height();
}
auto [item, offset] = _history->findItemAndOffset(visibleTop);
while (item && !IsServerMsgId(item->data()->id)) {
offset -= item->height();
item = item->nextInBlocks();
}
return std::pair(item, offset);
}();
const auto last = _history->peer->topPinnedMessageId();
const auto lessThanId = item
? (item->data()->id + (offset > 0 ? 1 : 0))
: (last + 1);
_pinnedTracker->trackAround(lessThanId);
}
void HistoryWidget::setupPinnedTracker() {
Expects(_history != nullptr);
_pinnedTracker = std::make_unique<HistoryView::PinnedTracker>(_history);
_pinnedTracker->shownMessageId(
) | rpl::start_with_next([=](MsgId messageId) {
showPinnedMessage({ peerToChannel(_peer->id), messageId });
}, _list->lifetime());
}
void HistoryWidget::showPinnedMessage(FullMsgId id) {
if (_pinnedId == id) {
return;
}
_pinnedId = id;
if (pinnedMsgVisibilityUpdated()) {
updateHistoryGeometry();
updateControlsVisibility();
updateControlsGeometry();
this->update();
}
}
bool HistoryWidget::pinnedMsgVisibilityUpdated() {
auto result = false;
auto pinnedId = _peer->topPinnedMessageId();
auto pinnedId = _pinnedId;
if (pinnedId && !_peer->canPinMessages()) {
const auto hiddenId = session().settings().hiddenPinnedMessageId(
_peer->id);
if (hiddenId == pinnedId) {
pinnedId = 0;
if (hiddenId == pinnedId.msg) {
pinnedId = FullMsgId();
} else if (hiddenId) {
session().settings().setHiddenPinnedMessageId(_peer->id, 0);
session().saveSettings();
@ -5241,7 +5298,7 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() {
}
if (pinnedId) {
if (!_pinnedBar) {
_pinnedBar = std::make_unique<PinnedBar>(pinnedId, this);
_pinnedBar = std::make_unique<PinnedBar>(pinnedId.msg, this);
if (_a_show.animating()) {
_pinnedBar->cancel->hide();
_pinnedBar->shadow->hide();
@ -5261,8 +5318,8 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() {
if (!barTop || _scroll->scrollTop() != *barTop) {
synteticScrollToY(_scroll->scrollTop() + st::historyReplyHeight);
}
} else if (_pinnedBar->msgId != pinnedId) {
_pinnedBar->msgId = pinnedId;
} else if (_pinnedBar->msgId != pinnedId.msg) {
_pinnedBar->msgId = pinnedId.msg;
_pinnedBar->msg = nullptr;
_pinnedBar->text.clear();
updatePinnedBar();
@ -5565,7 +5622,7 @@ void HistoryWidget::UnpinMessage(not_null<PeerData*> peer, MsgId msgId) {
}
void HistoryWidget::hidePinnedMessage() {
const auto pinnedId = _peer ? _peer->topPinnedMessageId() : MsgId(0);
const auto pinnedId = _pinnedId;
if (!pinnedId) {
if (pinnedMsgVisibilityUpdated()) {
updateControlsGeometry();
@ -5575,11 +5632,9 @@ void HistoryWidget::hidePinnedMessage() {
}
if (_peer->canPinMessages()) {
unpinMessage(FullMsgId(
_peer->isChannel() ? peerToChannel(_peer->id) : NoChannel,
pinnedId));
unpinMessage(pinnedId);
} else {
session().settings().setHiddenPinnedMessageId(_peer->id, pinnedId);
session().settings().setHiddenPinnedMessageId(_peer->id, pinnedId.msg);
session().saveSettings();
if (pinnedMsgVisibilityUpdated()) {
updateControlsGeometry();
@ -6389,7 +6444,9 @@ void HistoryWidget::drawPinnedBar(Painter &p) {
p.setPen(st::historyReplyNameFg);
p.setFont(st::msgServiceNameFont);
const auto poll = media ? media->poll() : nullptr;
const auto pinnedHeader = !poll
const auto pinnedHeader = (_pinnedBar->msgId < _peer->topPinnedMessageId())
? tr::lng_pinned_previous(tr::now)
: !poll
? tr::lng_pinned_message(tr::now)
: poll->quiz()
? tr::lng_pinned_quiz(tr::now)

View File

@ -93,6 +93,7 @@ namespace HistoryView {
class TopBarWidget;
class ContactStatus;
class Element;
class PinnedTracker;
} // namespace HistoryView
class DragArea;
@ -491,9 +492,12 @@ private:
void updateReplyEditTexts(bool force = false);
void updateReplyEditText(not_null<HistoryItem*> item);
void showPinnedMessage(FullMsgId id);
void updatePinnedBar(bool force = false);
bool pinnedMsgVisibilityUpdated();
void destroyPinnedBar();
void updatePinnedViewer();
void setupPinnedTracker();
void sendInlineResult(
not_null<InlineBots::Result*> result,
@ -610,7 +614,9 @@ private:
object_ptr<Ui::IconButton> _fieldBarCancel;
FullMsgId _pinnedId;
std::unique_ptr<PinnedBar> _pinnedBar;
std::unique_ptr<HistoryView::PinnedTracker> _pinnedTracker;
mtpRequestId _saveEditMsgRequestId = 0;

View File

@ -0,0 +1,247 @@
/*
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 "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_session.h"
#include "data/data_histories.h"
#include "main/main_session.h"
#include "history/history.h"
#include "history/history_item.h"
#include "apiwrap.h"
namespace HistoryView {
namespace {
constexpr auto kLoadedLimit = 4;
constexpr auto kPerPage = 40;
} // namespace
PinnedTracker::PinnedTracker(not_null<History*> history) : _history(history) {
_history->session().changes().peerFlagsValue(
_history->peer,
Data::PeerUpdate::Flag::PinnedMessage
) | rpl::start_with_next([=] {
refreshData();
}, _lifetime);
}
PinnedTracker::~PinnedTracker() {
_history->owner().histories().cancelRequest(_beforeRequestId);
_history->owner().histories().cancelRequest(_afterRequestId);
}
rpl::producer<MsgId> PinnedTracker::shownMessageId() const {
return _current.value();
}
void PinnedTracker::refreshData() {
const auto now = _history->peer->currentPinnedMessages();
if (!now) {
_dataLifetime.destroy();
_current = MsgId(0);
} else if (_data.get() != now) {
_dataLifetime.destroy();
_data = now;
if (_aroundId) {
setupViewer(now);
}
}
}
void PinnedTracker::trackAround(MsgId messageId) {
if (_aroundId == messageId) {
return;
}
_dataLifetime.destroy();
_aroundId = messageId;
if (!_aroundId) {
_current = MsgId(0);
} else if (const auto now = _data.get()) {
setupViewer(now);
}
}
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 = (i - begin(snapshot.ids));
const auto after = (end(snapshot.ids) - i);
if (before < kLoadedLimit && !snapshot.skippedBefore) {
load(
Data::LoadDirection::Before,
empty ? _aroundId : snapshot.ids.front());
}
if (after < kLoadedLimit && !snapshot.skippedAfter) {
load(
Data::LoadDirection::After,
empty ? _aroundId : snapshot.ids.back());
}
if (i != begin(snapshot.ids)) {
_current = *(i - 1);
} else if (snapshot.skippedBefore == 0) {
_current = 0;
}
}, _dataLifetime);
}
void PinnedTracker::load(Data::LoadDirection direction, MsgId id) {
const auto requestId = (direction == Data::LoadDirection::Before)
? &_beforeRequestId
: &_afterRequestId;
const auto aroundId = (direction == Data::LoadDirection::Before)
? &_beforeId
: &_afterId;
if (*requestId) {
if (*aroundId == id) {
return;
}
_history->owner().histories().cancelRequest(*requestId);
}
*aroundId = id;
const auto send = [=](Fn<void()> finish) {
const auto offsetId = [&] {
switch (direction) {
case Data::LoadDirection::Before: return id;
case Data::LoadDirection::After: return id + 1;
}
Unexpected("Direction in PinnedTracker::load");
}();
const auto addOffset = [&] {
switch (direction) {
case Data::LoadDirection::Before: return 0;
case Data::LoadDirection::After: return -kPerPage;
}
Unexpected("Direction in PinnedTracker::load");
}();
return _history->session().api().request(MTPmessages_Search(
MTP_flags(0),
_history->peer->input,
MTP_string(QString()),
MTP_inputPeerEmpty(),
MTPint(), // top_msg_id
MTP_inputMessagesFilterPinned(),
MTP_int(0),
MTP_int(0),
MTP_int(offsetId),
MTP_int(addOffset),
MTP_int(kPerPage),
MTP_int(0), // max_id
MTP_int(0), // min_id
MTP_int(0) // hash
)).done([=](const MTPmessages_Messages &result) {
*aroundId = 0;
*requestId = 0;
finish();
apply(direction, id, result);
}).fail([=](const RPCError &error) {
*aroundId = 0;
*requestId = 0;
finish();
}).send();
};
_beforeRequestId = _history->owner().histories().sendRequest(
_history,
Data::Histories::RequestType::History,
send);
}
void PinnedTracker::apply(
Data::LoadDirection direction,
MsgId aroundId,
const MTPmessages_Messages &result) {
auto noSkipRange = MsgRange{ aroundId, aroundId };
auto fullCount = std::optional<int>();
auto messages = [&] {
switch (result.type()) {
case mtpc_messages_messages: {
auto &d = result.c_messages_messages();
_history->owner().processUsers(d.vusers());
_history->owner().processChats(d.vchats());
fullCount = d.vmessages().v.size();
return &d.vmessages().v;
} break;
case mtpc_messages_messagesSlice: {
auto &d = result.c_messages_messagesSlice();
_history->owner().processUsers(d.vusers());
_history->owner().processChats(d.vchats());
fullCount = d.vcount().v;
return &d.vmessages().v;
} break;
case mtpc_messages_channelMessages: {
auto &d = result.c_messages_channelMessages();
if (auto channel = _history->peer->asChannel()) {
channel->ptsReceived(d.vpts().v);
} else {
LOG(("API Error: received messages.channelMessages when "
"no channel was passed! (PinnedTracker::apply)"));
}
_history->owner().processUsers(d.vusers());
_history->owner().processChats(d.vchats());
fullCount = d.vcount().v;
return &d.vmessages().v;
} break;
case mtpc_messages_messagesNotModified: {
LOG(("API Error: received messages.messagesNotModified! "
"(PinnedTracker::apply)"));
return (const QVector<MTPMessage>*)nullptr;
} break;
}
Unexpected("messages.Messages type in PinnedTracker::apply.");
}();
if (!messages) {
return;
}
const auto addType = NewMessageType::Existing;
auto list = std::vector<MsgId>();
list.reserve(messages->size());
for (const auto &message : *messages) {
const auto item = _history->owner().addNewMessage(
message,
MTPDmessage_ClientFlags(),
addType);
if (item) {
const auto itemId = item->id;
if (item->isPinned()) {
list.push_back(itemId);
}
accumulate_min(noSkipRange.from, itemId);
accumulate_max(noSkipRange.till, itemId);
}
}
if (aroundId && list.empty()) {
noSkipRange = [&]() -> MsgRange {
switch (direction) {
case Data::LoadDirection::Before: // All old loaded.
return { 0, noSkipRange.till };
case Data::LoadDirection::Around: // All loaded.
return { 0, ServerMaxMsgId };
case Data::LoadDirection::After: // All new loaded.
return { noSkipRange.from, ServerMaxMsgId };
}
Unexpected("Direction in PinnedTracker::apply.");
}();
}
_history->peer->addPinnedSlice(std::move(list), noSkipRange, fullCount);
}
} // namespace HistoryView

View File

@ -0,0 +1,52 @@
/*
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
class History;
namespace Data {
class PinnedMessages;
enum class LoadDirection : char;
} // namespace Data
namespace HistoryView {
class PinnedTracker final {
public:
explicit PinnedTracker(not_null<History*> history);
~PinnedTracker();
[[nodiscard]] rpl::producer<MsgId> shownMessageId() const;
void trackAround(MsgId messageId);
private:
void refreshData();
void setupViewer(not_null<Data::PinnedMessages*> data);
void load(Data::LoadDirection direction, MsgId id);
void apply(
Data::LoadDirection direction,
MsgId aroundId,
const MTPmessages_Messages &result);
const not_null<History*> _history;
base::weak_ptr<Data::PinnedMessages> _data;
rpl::variable<MsgId> _current = MsgId();
rpl::lifetime _dataLifetime;
MsgId _aroundId = 0;
MsgId _beforeId = 0;
MsgId _afterId = 0;
MsgId _beforeRequestId = 0;
MsgId _afterRequestId = 0;
rpl::lifetime _lifetime;
};
} // namespace HistoryView