Merge pinned list with migrated legacy group.

This commit is contained in:
John Preston 2020-10-22 15:40:33 +03:00
parent 18d218044c
commit 68041d2ffb
13 changed files with 248 additions and 104 deletions

View file

@ -69,6 +69,21 @@ TextParseOptions kMarkedTextBoxOptions = {
Qt::LayoutDirectionAuto, // dir Qt::LayoutDirectionAuto, // dir
}; };
[[nodiscard]] bool IsOldForPin(MsgId id, not_null<PeerData*> peer) {
const auto normal = peer->migrateToOrMe();
const auto migrated = normal->migrateFrom();
const auto top = Data::ResolveTopPinnedId(normal, migrated);
if (!top) {
return false;
} else if (peer == migrated) {
return top.channel || (id < top.msg);
} else if (migrated) {
return top.channel && (id < top.msg);
} else {
return (id < top.msg);
}
}
} // namespace } // namespace
ConfirmBox::ConfirmBox( ConfirmBox::ConfirmBox(
@ -444,7 +459,7 @@ PinMessageBox::PinMessageBox(
: _peer(peer) : _peer(peer)
, _api(&peer->session().mtp()) , _api(&peer->session().mtp())
, _msgId(msgId) , _msgId(msgId)
, _pinningOld(msgId < Data::ResolveTopPinnedId(peer)) , _pinningOld(IsOldForPin(msgId, peer))
, _text( , _text(
this, this,
(_pinningOld (_pinningOld

View file

@ -1017,7 +1017,9 @@ void SetTopPinnedMessageId(not_null<PeerData*> peer, MsgId messageId) {
peer->setHasPinnedMessages(true); peer->setHasPinnedMessages(true);
} }
MsgId ResolveTopPinnedId(not_null<PeerData*> peer) { FullMsgId ResolveTopPinnedId(
not_null<PeerData*> peer,
PeerData *migrated) {
const auto slice = peer->session().storage().snapshot( const auto slice = peer->session().storage().snapshot(
Storage::SharedMediaQuery( Storage::SharedMediaQuery(
Storage::SharedMediaKey( Storage::SharedMediaKey(
@ -1026,10 +1028,32 @@ MsgId ResolveTopPinnedId(not_null<PeerData*> peer) {
ServerMaxMsgId - 1), ServerMaxMsgId - 1),
1, 1,
1)); 1));
return slice.messageIds.empty() ? 0 : slice.messageIds.back(); const auto old = migrated
? migrated->session().storage().snapshot(
Storage::SharedMediaQuery(
Storage::SharedMediaKey(
migrated->id,
Storage::SharedMediaType::Pinned,
ServerMaxMsgId - 1),
1,
1))
: Storage::SharedMediaResult{
.count = 0,
.skippedBefore = 0,
.skippedAfter = 0,
};
if (!slice.messageIds.empty()) {
return FullMsgId(peerToChannel(peer->id), slice.messageIds.back());
} else if (!migrated || slice.count != 0 || old.messageIds.empty()) {
return FullMsgId();
} else {
return FullMsgId(0, old.messageIds.back());
}
} }
std::optional<int> ResolvePinnedCount(not_null<PeerData*> peer) { std::optional<int> ResolvePinnedCount(
not_null<PeerData*> peer,
PeerData *migrated) {
const auto slice = peer->session().storage().snapshot( const auto slice = peer->session().storage().snapshot(
Storage::SharedMediaQuery( Storage::SharedMediaQuery(
Storage::SharedMediaKey( Storage::SharedMediaKey(
@ -1038,7 +1062,23 @@ std::optional<int> ResolvePinnedCount(not_null<PeerData*> peer) {
0), 0),
0, 0,
0)); 0));
return slice.count; const auto old = migrated
? migrated->session().storage().snapshot(
Storage::SharedMediaQuery(
Storage::SharedMediaKey(
migrated->id,
Storage::SharedMediaType::Pinned,
0),
0,
0))
: Storage::SharedMediaResult{
.count = 0,
.skippedBefore = 0,
.skippedAfter = 0,
};
return (slice.count.has_value() && old.count.has_value())
? std::make_optional(*slice.count + *old.count)
: std::nullopt;
} }
} // namespace Data } // namespace Data

View file

@ -443,7 +443,11 @@ std::optional<QString> RestrictionError(
ChatRestriction restriction); ChatRestriction restriction);
void SetTopPinnedMessageId(not_null<PeerData*> peer, MsgId messageId); void SetTopPinnedMessageId(not_null<PeerData*> peer, MsgId messageId);
[[nodiscard]] MsgId ResolveTopPinnedId(not_null<PeerData*> peer); [[nodiscard]] FullMsgId ResolveTopPinnedId(
[[nodiscard]] std::optional<int> ResolvePinnedCount(not_null<PeerData*> peer); not_null<PeerData*> peer,
PeerData *migrated);
[[nodiscard]] std::optional<int> ResolvePinnedCount(
not_null<PeerData*> peer,
PeerData *migrated);
} // namespace Data } // namespace Data

View file

@ -2998,6 +2998,31 @@ int HistoryInner::itemTop(const Element *view) const {
return (top < 0) ? top : (top + view->y() + view->block()->y()); return (top < 0) ? top : (top + view->y() + view->block()->y());
} }
auto HistoryInner::findViewForPinnedTracking(int top) const
-> std::pair<Element*, int> {
const auto normalTop = historyTop();
const auto oldTop = migratedTop();
const auto fromHistory = [&](not_null<History*> history, int historyTop)
-> std::pair<Element*, int> {
auto [view, offset] = history->findItemAndOffset(top - historyTop);
while (view && !IsServerMsgId(view->data()->id)) {
offset -= view->height();
view = view->nextInBlocks();
}
return { view, offset };
};
if (normalTop >= 0 && (oldTop < 0 || top >= normalTop)) {
return fromHistory(_history, normalTop);
} else if (oldTop >= 0) {
auto [view, offset] = fromHistory(_migrated, oldTop);
if (!view && normalTop >= 0) {
return fromHistory(_history, normalTop);
}
return { view, offset };
}
return { nullptr, 0 };
}
void HistoryInner::notifyIsBotChanged() { void HistoryInner::notifyIsBotChanged() {
const auto newinfo = _peer->isUser() const auto newinfo = _peer->isUser()
? _peer->asUser()->botInfo.get() ? _peer->asUser()->botInfo.get()

View file

@ -111,6 +111,10 @@ public:
int itemTop(const HistoryItem *item) const; int itemTop(const HistoryItem *item) const;
int itemTop(const Element *view) const; int itemTop(const Element *view) const;
// Returns (view, offset-from-top).
[[nodiscard]] std::pair<Element*, int> findViewForPinnedTracking(
int top) const;
void notifyIsBotChanged(); void notifyIsBotChanged();
void notifyMigrateUpdated(); void notifyMigrateUpdated();

View file

@ -569,6 +569,12 @@ HistoryWidget::HistoryWidget(
| UpdateFlag::BotStartToken | UpdateFlag::BotStartToken
| UpdateFlag::PinnedMessages | UpdateFlag::PinnedMessages
) | rpl::filter([=](const Data::PeerUpdate &update) { ) | rpl::filter([=](const Data::PeerUpdate &update) {
if (_migrated && update.peer.get() == _migrated->peer) {
if (_pinnedTracker
&& (update.flags & UpdateFlag::PinnedMessages)) {
checkPinnedBarState();
}
}
return (update.peer.get() == _peer); return (update.peer.get() == _peer);
}) | rpl::map([](const Data::PeerUpdate &update) { }) | rpl::map([](const Data::PeerUpdate &update) {
return update.flags; return update.flags;
@ -3398,6 +3404,7 @@ void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) {
if (_replyForwardPressed) { if (_replyForwardPressed) {
_replyForwardPressed = false; _replyForwardPressed = false;
update(0, _field->y() - st::historySendPadding - st::historyReplyHeight, width(), st::historyReplyHeight); update(0, _field->y() - st::historySendPadding - st::historyReplyHeight, width(), st::historyReplyHeight);
setupPinnedTracker(); AssertIsDebug();
} }
if (_recording) { if (_recording) {
stopRecording(_peer && _inField); stopRecording(_peer && _inField);
@ -5021,6 +5028,7 @@ void HistoryWidget::handlePeerMigration() {
} else { } else {
_migrated = _history->migrateFrom(); _migrated = _history->migrateFrom();
_list->notifyMigrateUpdated(); _list->notifyMigrateUpdated();
setupPinnedTracker();
updateHistoryGeometry(); updateHistoryGeometry();
} }
const auto from = chat->owner().historyLoaded(chat); const auto from = chat->owner().historyLoaded(chat);
@ -5168,23 +5176,14 @@ void HistoryWidget::updatePinnedViewer() {
|| !_historyInited) { || !_historyInited) {
return; return;
} }
const auto [item, offset] = [&] { const auto visibleTop = _scroll->scrollTop();
auto visibleTop = _scroll->scrollTop(); const auto add = (st::historyReplyHeight - _pinnedBarHeight);
if (_migrated auto [view, offset] = _list->findViewForPinnedTracking(visibleTop + add);
&& _history->loadedAtBottom() const auto lessThanId = !view
&& _migrated->loadedAtTop()) { ? (ServerMaxMsgId - 1)
visibleTop -= _migrated->height(); : (view->data()->history() != _history)
} ? (view->data()->id + (offset > 0 ? 1 : 0) - ServerMaxMsgId)
auto [item, offset] = _history->findItemAndOffset(visibleTop); : (view->data()->id + (offset > 0 ? 1 : 0));
while (item && !IsServerMsgId(item->data()->id)) {
offset -= item->height();
item = item->nextInBlocks();
}
return std::pair(item, offset);
}();
const auto lessThanId = item
? (item->data()->id + (offset > 0 ? 1 : 0))
: (ServerMaxMsgId - 1);
_pinnedTracker->trackAround(lessThanId); _pinnedTracker->trackAround(lessThanId);
} }
@ -5202,8 +5201,15 @@ 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 = Data::ResolveTopPinnedId(_peer); const auto currentPinnedId = Data::ResolveTopPinnedId(
if (currentPinnedId == hiddenId) { _peer,
_migrated ? _migrated->peer.get() : nullptr);
const auto universalPinnedId = !currentPinnedId
? int32(0)
: (_migrated && !currentPinnedId.channel)
? (currentPinnedId.msg - ServerMaxMsgId)
: currentPinnedId.msg;
if (universalPinnedId == hiddenId) {
if (_pinnedBar) { if (_pinnedBar) {
_pinnedTracker->reset(); _pinnedTracker->reset();
auto qobject = base::unique_qptr{ auto qobject = base::unique_qptr{
@ -5221,21 +5227,13 @@ void HistoryWidget::checkPinnedBarState() {
} }
return; return;
} }
if (_pinnedBar || !currentPinnedId) { if (_pinnedBar || !universalPinnedId) {
return; return;
} }
auto shown = _pinnedTracker->shownMessageId(
) | rpl::map([=](HistoryView::PinnedId messageId) {
return HistoryView::PinnedBarId{
FullMsgId{ peerToChannel(_peer->id), messageId.message },
messageId.index,
messageId.count
};
});
auto barContent = HistoryView::PinnedBarContent( auto barContent = HistoryView::PinnedBarContent(
&session(), &session(),
std::move(shown)); _pinnedTracker->shownMessageId());
_pinnedBar = std::make_unique<Ui::PinnedBar>( _pinnedBar = std::make_unique<Ui::PinnedBar>(
this, this,
std::move(barContent)); std::move(barContent));
@ -5268,8 +5266,8 @@ void HistoryWidget::checkPinnedBarState() {
_pinnedBar->barClicks( _pinnedBar->barClicks(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
const auto id = _pinnedTracker->currentMessageId(); const auto id = _pinnedTracker->currentMessageId();
if (id.message) { if (const auto item = session().data().message(id.message)) {
Ui::showPeerHistory(_peer, id.message); Ui::showPeerHistory(item->history()->peer, item->id);
} }
}, _pinnedBar->lifetime()); }, _pinnedBar->lifetime());
@ -5303,7 +5301,11 @@ void HistoryWidget::refreshPinnedBarButton(bool many) {
const auto id = _pinnedTracker->currentMessageId(); const auto id = _pinnedTracker->currentMessageId();
if (id.message) { if (id.message) {
controller()->showSection( controller()->showSection(
HistoryView::PinnedMemento(_history, id.message)); HistoryView::PinnedMemento(
_history,
((!_migrated || id.message.channel)
? id.message.msg
: (id.message.msg - ServerMaxMsgId))));
} }
} }
}, button->lifetime()); }, button->lifetime());
@ -5560,10 +5562,7 @@ void HistoryWidget::hidePinnedMessage() {
return; return;
} }
if (_peer->canPinMessages()) { if (_peer->canPinMessages()) {
Window::ToggleMessagePinned( Window::ToggleMessagePinned(controller(), id.message, false);
controller(),
{ peerToChannel(_peer->id), id.message },
false);
} else { } else {
const auto callback = [=] { const auto callback = [=] {
if (_pinnedTracker) { if (_pinnedTracker) {
@ -5877,7 +5876,9 @@ void HistoryWidget::handlePeerUpdate() {
updateHistoryGeometry(); updateHistoryGeometry();
if (_peer->isChat() && _peer->asChat()->noParticipantInfo()) { if (_peer->isChat() && _peer->asChat()->noParticipantInfo()) {
session().api().requestFullPeer(_peer); session().api().requestFullPeer(_peer);
} else if (_peer->isUser() && (_peer->asUser()->blockStatus() == UserData::BlockStatus::Unknown || _peer->asUser()->callsStatus() == UserData::CallsStatus::Unknown)) { } else if (_peer->isUser()
&& (_peer->asUser()->blockStatus() == UserData::BlockStatus::Unknown
|| _peer->asUser()->callsStatus() == UserData::CallsStatus::Unknown)) {
session().api().requestFullPeer(_peer); session().api().requestFullPeer(_peer);
} else if (auto channel = _peer->asMegagroup()) { } else if (auto channel = _peer->asMegagroup()) {
if (!channel->mgInfo->botStatus) { if (!channel->mgInfo->botStatus) {

View file

@ -117,7 +117,7 @@ namespace {
})); }));
} }
auto WithPinnedTitle(not_null<Main::Session*> session, PinnedBarId id) { auto WithPinnedTitle(not_null<Main::Session*> session, PinnedId id) {
return [=](Ui::MessageBarContent &&content) { return [=](Ui::MessageBarContent &&content) {
const auto item = session->data().message(id.message); const auto item = session->data().message(id.message);
if (!item) { if (!item) {
@ -144,11 +144,11 @@ rpl::producer<Ui::MessageBarContent> MessageBarContentByItemId(
rpl::producer<Ui::MessageBarContent> PinnedBarContent( rpl::producer<Ui::MessageBarContent> PinnedBarContent(
not_null<Main::Session*> session, not_null<Main::Session*> session,
rpl::producer<PinnedBarId> id) { rpl::producer<PinnedId> id) {
return std::move( return std::move(
id id
) | rpl::distinct_until_changed( ) | rpl::distinct_until_changed(
) | rpl::map([=](PinnedBarId id) { ) | rpl::map([=](PinnedId id) {
return ContentByItemId( return ContentByItemId(
session, session,
id.message id.message

View file

@ -28,25 +28,25 @@ namespace HistoryView {
FullMsgId id); FullMsgId id);
enum class PinnedIdType; enum class PinnedIdType;
struct PinnedBarId { struct PinnedId {
FullMsgId message; FullMsgId message;
int index = 0; int index = 0;
int count = 1; int count = 1;
bool operator<(const PinnedBarId &other) const { bool operator<(const PinnedId &other) const {
return std::tie(message, index, count) return std::tie(message, index, count)
< std::tie(other.message, other.index, other.count); < std::tie(other.message, other.index, other.count);
} }
bool operator==(const PinnedBarId &other) const { bool operator==(const PinnedId &other) const {
return std::tie(message, index, count) return std::tie(message, index, count)
== std::tie(other.message, other.index, other.count); == std::tie(other.message, other.index, other.count);
} }
bool operator!=(const PinnedBarId &other) const { bool operator!=(const PinnedId &other) const {
return !(*this == other); return !(*this == other);
} }
}; };
[[nodiscard]] rpl::producer<Ui::MessageBarContent> PinnedBarContent( [[nodiscard]] rpl::producer<Ui::MessageBarContent> PinnedBarContent(
not_null<Main::Session*> session, not_null<Main::Session*> session,
rpl::producer<PinnedBarId> id); rpl::producer<PinnedId> id);
} // namespace HistoryView } // namespace HistoryView

View file

@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_components.h" #include "history/history_item_components.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "data/data_peer_values.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
@ -34,10 +33,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_chat.h"
#include "data/data_channel.h" #include "data/data_channel.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"
#include "data/data_peer_values.h"
#include "storage/storage_account.h" #include "storage/storage_account.h"
#include "platform/platform_specific.h" #include "platform/platform_specific.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -90,7 +91,8 @@ PinnedWidget::PinnedWidget(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<History*> history) not_null<History*> history)
: Window::SectionWidget(parent, controller) : Window::SectionWidget(parent, controller)
, _history(history) , _history(history->migrateToOrMe())
, _migratedPeer(_history->peer->migrateFrom())
, _topBar(this, controller) , _topBar(this, controller)
, _topBarShadow(this) , _topBarShadow(this)
, _scroll(std::make_unique<Ui::ScrollArea>(this, st::historyScroll, false)) , _scroll(std::make_unique<Ui::ScrollArea>(this, st::historyScroll, false))
@ -321,7 +323,8 @@ bool PinnedWidget::showInternal(
not_null<Window::SectionMemento*> memento, not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) { const Window::SectionShow &params) {
if (auto logMemento = dynamic_cast<PinnedMemento*>(memento.get())) { if (auto logMemento = dynamic_cast<PinnedMemento*>(memento.get())) {
if (logMemento->getHistory() == history()) { if (logMemento->getHistory() == history()
|| logMemento->getHistory()->migrateToOrMe() == history()) {
restoreState(logMemento); restoreState(logMemento);
return true; return true;
} }
@ -358,7 +361,9 @@ void PinnedWidget::restoreState(not_null<PinnedMemento*> memento) {
_inner->restoreState(memento->list()); _inner->restoreState(memento->list());
if (const auto highlight = memento->getHighlightId()) { if (const auto highlight = memento->getHighlightId()) {
const auto position = Data::MessagePosition{ const auto position = Data::MessagePosition{
.fullId = FullMsgId(_history->channelId(), highlight), .fullId = ((highlight > 0 || !_migratedPeer)
? FullMsgId(_history->channelId(), highlight)
: FullMsgId(0, -highlight)),
.date = TimeId(0), .date = TimeId(0),
}; };
_inner->showAroundPosition(position, [=] { _inner->showAroundPosition(position, [=] {
@ -508,6 +513,28 @@ void PinnedWidget::listDeleteRequest() {
confirmDeleteSelected(); confirmDeleteSelected();
} }
rpl::producer<int> SharedMediaCountValue(
not_null<PeerData*> peer,
PeerData *migrated,
Storage::SharedMediaType type) {
auto aroundId = 0;
auto limit = 0;
auto updated = SharedMediaMergedViewer(
&peer->session(),
SharedMediaMergedKey(
SparseIdsMergedSlice::Key(
peer->id,
migrated ? migrated->id : 0,
aroundId),
type),
limit,
limit
) | rpl::map([](const SparseIdsMergedSlice &slice) {
return slice.fullCount();
}) | rpl::filter_optional();
return rpl::single(0) | rpl::then(std::move(updated));
}
rpl::producer<Data::MessagesSlice> PinnedWidget::listSource( rpl::producer<Data::MessagesSlice> PinnedWidget::listSource(
Data::MessagePosition aroundId, Data::MessagePosition aroundId,
int limitBefore, int limitBefore,
@ -516,15 +543,18 @@ rpl::producer<Data::MessagesSlice> PinnedWidget::listSource(
const auto messageId = aroundId.fullId.msg const auto messageId = aroundId.fullId.msg
? aroundId.fullId.msg ? aroundId.fullId.msg
: (ServerMaxMsgId - 1); : (ServerMaxMsgId - 1);
return SharedMediaViewer(
return SharedMediaMergedViewer(
&_history->session(), &_history->session(),
Storage::SharedMediaKey( SharedMediaMergedKey(
_history->peer->id, SparseIdsMergedSlice::Key(
Storage::SharedMediaType::Pinned, _history->peer->id,
messageId), _migratedPeer ? _migratedPeer->id : 0,
messageId),
Storage::SharedMediaType::Pinned),
limitBefore, limitBefore,
limitAfter limitAfter
) | rpl::filter([=](const SparseIdsSlice &slice) { ) | rpl::filter([=](const SparseIdsMergedSlice &slice) {
const auto count = slice.fullCount(); const auto count = slice.fullCount();
if (!count.has_value()) { if (!count.has_value()) {
return true; return true;
@ -535,7 +565,7 @@ rpl::producer<Data::MessagesSlice> PinnedWidget::listSource(
controller()->showBackFromStack(); controller()->showBackFromStack();
return false; return false;
} }
}) | rpl::map([=](SparseIdsSlice &&slice) { }) | rpl::map([=](SparseIdsMergedSlice &&slice) {
auto result = Data::MessagesSlice(); auto result = Data::MessagesSlice();
result.fullCount = slice.fullCount(); result.fullCount = slice.fullCount();
result.skippedAfter = slice.skippedAfter(); result.skippedAfter = slice.skippedAfter();
@ -543,10 +573,10 @@ rpl::producer<Data::MessagesSlice> PinnedWidget::listSource(
const auto count = slice.size(); const auto count = slice.size();
result.ids.reserve(count); result.ids.reserve(count);
if (const auto msgId = slice.nearest(messageId)) { if (const auto msgId = slice.nearest(messageId)) {
result.nearestToAround = FullMsgId(channelId, *msgId); result.nearestToAround = *msgId;
} }
for (auto i = 0; i != count; ++i) { for (auto i = 0; i != count; ++i) {
result.ids.emplace_back(channelId, slice[i]); result.ids.push_back(slice[i]);
} }
return result; return result;
}); });

View file

@ -136,6 +136,7 @@ private:
void refreshClearButtonText(); void refreshClearButtonText();
const not_null<History*> _history; const not_null<History*> _history;
PeerData *_migratedPeer = nullptr;
QPointer<ListWidget> _inner; QPointer<ListWidget> _inner;
object_ptr<TopBarWidget> _topBar; object_ptr<TopBarWidget> _topBar;
object_ptr<Ui::PlainShadow> _topBarShadow; object_ptr<Ui::PlainShadow> _topBarShadow;
@ -155,9 +156,11 @@ private:
class PinnedMemento : public Window::SectionMemento { class PinnedMemento : public Window::SectionMemento {
public: public:
using UniversalMsgId = int32;
explicit PinnedMemento( explicit PinnedMemento(
not_null<History*> history, not_null<History*> history,
MsgId highlightId = 0); UniversalMsgId highlightId = 0);
object_ptr<Window::SectionWidget> createWidget( object_ptr<Window::SectionWidget> createWidget(
QWidget *parent, QWidget *parent,
@ -172,13 +175,13 @@ public:
[[nodiscard]] not_null<ListMemento*> list() { [[nodiscard]] not_null<ListMemento*> list() {
return &_list; return &_list;
} }
[[nodiscard]] MsgId getHighlightId() const { [[nodiscard]] UniversalMsgId getHighlightId() const {
return _highlightId; return _highlightId;
} }
private: private:
const not_null<History*> _history; const not_null<History*> _history;
const MsgId _highlightId = 0; const UniversalMsgId _highlightId = 0;
ListMemento _list; ListMemento _list;
}; };

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_chat.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_shared_media.h" #include "data/data_shared_media.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -26,13 +27,25 @@ constexpr auto kChangeViewerLimit = 2;
} // namespace } // namespace
PinnedTracker::PinnedTracker(not_null<History*> history) : _history(history) { PinnedTracker::PinnedTracker(not_null<History*> history)
_history->session().changes().peerFlagsValue( : _history(history->migrateToOrMe())
_history->peer, , _migratedPeer(_history->peer->migrateFrom()) {
Data::PeerUpdate::Flag::PinnedMessages using namespace rpl::mappers;
) | rpl::map([=] { const auto has = [&](PeerData *peer) -> rpl::producer<bool> {
return _history->peer->hasPinnedMessages(); auto &changes = _history->session().changes();
}) | rpl::distinct_until_changed( const auto flag = Data::PeerUpdate::Flag::PinnedMessages;
if (!peer) {
return rpl::single(false);
}
return changes.peerFlagsValue(peer, flag) | rpl::map([=] {
return peer->hasPinnedMessages();
});
};
rpl::combine(
has(_history->peer),
has(_migratedPeer),
_1 || _2
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool has) { ) | rpl::start_with_next([=](bool has) {
if (has) { if (has) {
refreshViewer(); refreshViewer();
@ -62,15 +75,17 @@ void PinnedTracker::refreshViewer() {
} }
_dataLifetime.destroy(); _dataLifetime.destroy();
_viewerAroundId = _aroundId; _viewerAroundId = _aroundId;
SharedMediaViewer( SharedMediaMergedViewer(
&_history->peer->session(), &_history->peer->session(),
Storage::SharedMediaKey( SharedMediaMergedKey(
_history->peer->id, SparseIdsMergedSlice::Key(
Storage::SharedMediaType::Pinned, _history->peer->id,
_viewerAroundId), _migratedPeer ? _migratedPeer->id : 0,
_viewerAroundId),
Storage::SharedMediaType::Pinned),
kLoadedLimit, kLoadedLimit,
kLoadedLimit kLoadedLimit
) | rpl::start_with_next([=](const SparseIdsSlice &result) { ) | rpl::start_with_next([=](const SparseIdsMergedSlice &result) {
_slice.fullCount = result.fullCount(); _slice.fullCount = result.fullCount();
_slice.skippedBefore = result.skippedBefore(); _slice.skippedBefore = result.skippedBefore();
_slice.skippedAfter = result.skippedAfter(); _slice.skippedAfter = result.skippedAfter();
@ -83,12 +98,23 @@ void PinnedTracker::refreshViewer() {
refreshCurrentFromSlice(); refreshCurrentFromSlice();
if (_slice.fullCount == 0) { if (_slice.fullCount == 0) {
_history->peer->setHasPinnedMessages(false); _history->peer->setHasPinnedMessages(false);
if (_migratedPeer) {
_migratedPeer->setHasPinnedMessages(false);
}
} }
}, _dataLifetime); }, _dataLifetime);
} }
void PinnedTracker::refreshCurrentFromSlice() { void PinnedTracker::refreshCurrentFromSlice() {
const auto i = ranges::lower_bound(_slice.ids, _aroundId); const auto proj1 = [](FullMsgId id) {
return id.channel ? id.msg : (id.msg - ServerMaxMsgId);
};
const auto proj2 = [](FullMsgId id) {
return id.msg;
};
const auto i = _migratedPeer
? ranges::lower_bound(_slice.ids, _aroundId, ranges::less(), proj1)
: ranges::lower_bound(_slice.ids, _aroundId, ranges::less(), proj2);
const auto empty = _slice.ids.empty(); const auto empty = _slice.ids.empty();
const auto before = int(i - begin(_slice.ids)); const auto before = int(i - begin(_slice.ids));
const auto after = int(end(_slice.ids) - i); const auto after = int(end(_slice.ids) - i);

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "history/view/history_view_pinned_bar.h"
class History; class History;
namespace Data { namespace Data {
@ -15,29 +17,16 @@ enum class LoadDirection : char;
namespace HistoryView { namespace HistoryView {
struct PinnedId {
MsgId message = 0;
int index = 0;
int count = 1;
bool operator<(const PinnedId &other) const {
return std::tie(message, index, count)
< std::tie(other.message, other.index, other.count);
}
bool operator==(const PinnedId &other) const {
return std::tie(message, index, count)
== std::tie(other.message, other.index, other.count);
}
};
class PinnedTracker final { class PinnedTracker final {
public: public:
using UniversalMsgId = int32;
explicit PinnedTracker(not_null<History*> history); explicit PinnedTracker(not_null<History*> history);
~PinnedTracker(); ~PinnedTracker();
[[nodiscard]] rpl::producer<PinnedId> shownMessageId() const; [[nodiscard]] rpl::producer<PinnedId> shownMessageId() const;
[[nodiscard]] PinnedId currentMessageId() const; [[nodiscard]] PinnedId currentMessageId() const;
void trackAround(MsgId messageId); void trackAround(UniversalMsgId messageId);
void reset(); void reset();
[[nodiscard]] rpl::lifetime &lifetime() { [[nodiscard]] rpl::lifetime &lifetime() {
@ -46,7 +35,7 @@ public:
private: private:
struct Slice { struct Slice {
std::vector<MsgId> ids; std::vector<FullMsgId> ids;
std::optional<int> fullCount; std::optional<int> fullCount;
std::optional<int> skippedBefore; std::optional<int> skippedBefore;
std::optional<int> skippedAfter; std::optional<int> skippedAfter;
@ -56,12 +45,13 @@ private:
void refreshCurrentFromSlice(); void refreshCurrentFromSlice();
const not_null<History*> _history; const not_null<History*> _history;
PeerData *_migratedPeer = nullptr;
rpl::variable<PinnedId> _current; rpl::variable<PinnedId> _current;
rpl::lifetime _dataLifetime; rpl::lifetime _dataLifetime;
MsgId _aroundId = 0; UniversalMsgId _aroundId = 0;
MsgId _viewerAroundId = 0; UniversalMsgId _viewerAroundId = 0;
Slice _slice; Slice _slice;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;

View file

@ -1157,9 +1157,15 @@ void HidePinnedBar(
Ui::show(Box<ConfirmBox>(tr::lng_pinned_hide_all_sure(tr::now), tr::lng_pinned_hide_all_hide(tr::now), crl::guard(navigation, [=] { Ui::show(Box<ConfirmBox>(tr::lng_pinned_hide_all_sure(tr::now), tr::lng_pinned_hide_all_hide(tr::now), crl::guard(navigation, [=] {
Ui::hideLayer(); Ui::hideLayer();
auto &session = peer->session(); auto &session = peer->session();
const auto top = Data::ResolveTopPinnedId(peer); const auto migrated = peer->migrateFrom();
if (top) { const auto top = Data::ResolveTopPinnedId(peer, migrated);
session.settings().setHiddenPinnedMessageId(peer->id, top); const auto universal = !top
? int32(0)
: (migrated && !top.channel)
? (top.msg - ServerMaxMsgId)
: top.msg;
if (universal) {
session.settings().setHiddenPinnedMessageId(peer->id, universal);
session.saveSettingsDelayed(); session.saveSettingsDelayed();
if (onHidden) { if (onHidden) {
onHidden(); onHidden();