mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-08-31 14:38:15 +00:00
Add pinned messages section.
This commit is contained in:
@@ -383,7 +383,6 @@ void ChannelData::setUnavailableReasons(
|
||||
void ChannelData::setAvailableMinId(MsgId availableMinId) {
|
||||
if (_availableMinId != availableMinId) {
|
||||
_availableMinId = availableMinId;
|
||||
clearPinnedMessages(_availableMinId + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -38,6 +38,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/history_item.h"
|
||||
#include "storage/file_download.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "facades.h" // Ui::showPeerProfile
|
||||
#include "app.h"
|
||||
|
||||
@@ -468,14 +470,15 @@ MsgId PeerData::topPinnedMessageId() const {
|
||||
|
||||
void PeerData::ensurePinnedMessagesCreated() {
|
||||
if (!_pinnedMessages) {
|
||||
_pinnedMessages = std::make_unique<Data::PinnedMessages>(
|
||||
peerToChannel(id));
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,69 +494,24 @@ void PeerData::setTopPinnedMessageId(MsgId messageId) {
|
||||
clearPinnedMessages();
|
||||
return;
|
||||
}
|
||||
if (session().settings().hiddenPinnedMessageId(id) != messageId) {
|
||||
const auto hiddenId = session().settings().hiddenPinnedMessageId(id);
|
||||
if (hiddenId != 0 && hiddenId != messageId) {
|
||||
session().settings().setHiddenPinnedMessageId(id, 0);
|
||||
session().saveSettingsDelayed();
|
||||
}
|
||||
ensurePinnedMessagesCreated();
|
||||
_pinnedMessages->setTopId(messageId);
|
||||
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
|
||||
}
|
||||
|
||||
void PeerData::clearPinnedMessages(MsgId lessThanId) {
|
||||
if (lessThanId == ServerMaxMsgId
|
||||
&& session().settings().hiddenPinnedMessageId(id) != 0) {
|
||||
void PeerData::clearPinnedMessages() {
|
||||
if (session().settings().hiddenPinnedMessageId(id) != 0) {
|
||||
session().settings().setHiddenPinnedMessageId(id, 0);
|
||||
session().saveSettingsDelayed();
|
||||
}
|
||||
if (!_pinnedMessages) {
|
||||
return;
|
||||
}
|
||||
_pinnedMessages->clearLessThanId(lessThanId);
|
||||
session().storage().remove(Storage::SharedMediaRemoveAll(
|
||||
id,
|
||||
Storage::SharedMediaType::Pinned));
|
||||
removeEmptyPinnedMessages();
|
||||
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
|
||||
}
|
||||
|
||||
void PeerData::addPinnedMessage(MsgId messageId) {
|
||||
if (messageIdTooSmall(messageId)) {
|
||||
return;
|
||||
}
|
||||
ensurePinnedMessagesCreated();
|
||||
_pinnedMessages->add(messageId);
|
||||
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
|
||||
}
|
||||
|
||||
void PeerData::addPinnedSlice(
|
||||
std::vector<MsgId> &&ids,
|
||||
MsgRange noSkipRange,
|
||||
std::optional<int> count) {
|
||||
const auto min = [&] {
|
||||
if (const auto channel = asChannel()) {
|
||||
return channel->availableMinId();
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
ids.erase(
|
||||
ranges::remove_if(ids, [&](MsgId id) { return id <= min; }),
|
||||
end(ids));
|
||||
if (noSkipRange.from <= min) {
|
||||
noSkipRange.from = 0;
|
||||
}
|
||||
if (ids.empty() && !_pinnedMessages) {
|
||||
return;
|
||||
}
|
||||
ensurePinnedMessagesCreated();
|
||||
_pinnedMessages->add(std::move(ids), noSkipRange, count);
|
||||
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
|
||||
}
|
||||
|
||||
void PeerData::removePinnedMessage(MsgId messageId) {
|
||||
if (!_pinnedMessages) {
|
||||
return;
|
||||
}
|
||||
_pinnedMessages->remove(messageId);
|
||||
removeEmptyPinnedMessages();
|
||||
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
|
||||
}
|
||||
|
||||
bool PeerData::canExportChatHistory() const {
|
||||
|
@@ -330,13 +330,7 @@ public:
|
||||
[[nodiscard]] bool canEditMessagesIndefinitely() const;
|
||||
[[nodiscard]] MsgId topPinnedMessageId() const;
|
||||
void setTopPinnedMessageId(MsgId messageId);
|
||||
void clearPinnedMessages(MsgId lessThanId = ServerMaxMsgId);
|
||||
void addPinnedMessage(MsgId messageId);
|
||||
void addPinnedSlice(
|
||||
std::vector<MsgId> &&ids,
|
||||
MsgRange noSkipRange,
|
||||
std::optional<int> count);
|
||||
void removePinnedMessage(MsgId messageId);
|
||||
void clearPinnedMessages();
|
||||
Data::PinnedMessages *currentPinnedMessages() const {
|
||||
return _pinnedMessages.get();
|
||||
}
|
||||
|
@@ -7,94 +7,76 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_pinned_messages.h"
|
||||
|
||||
namespace Data {
|
||||
#include "data/data_peer.h"
|
||||
#include "main/main_session.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
|
||||
PinnedMessages::PinnedMessages(ChannelId channelId) : _channelId(channelId) {
|
||||
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 _list.empty();
|
||||
return _storage.empty(SharedMediaKey(_peer->id, PinnedType, 0));
|
||||
}
|
||||
|
||||
MsgId PinnedMessages::topId() const {
|
||||
const auto slice = _list.snapshot(MessagesQuery{
|
||||
.aroundId = MaxMessagePosition,
|
||||
.limitBefore = 1,
|
||||
.limitAfter = 1
|
||||
});
|
||||
return slice.messageIds.empty() ? 0 : slice.messageIds.back().fullId.msg;
|
||||
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 _list.viewer(MessagesQuery{
|
||||
.aroundId = position(aroundId),
|
||||
.limitBefore = limit,
|
||||
.limitAfter = limit
|
||||
}) | rpl::map([](const MessagesResult &result) {
|
||||
return _storage.query(
|
||||
SharedMediaQuery(
|
||||
SharedMediaKey(_peer->id, PinnedType, aroundId),
|
||||
limit,
|
||||
limit)
|
||||
) | rpl::map([](const SharedMediaResult &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;
|
||||
data.ids = result.messageIds | ranges::to_vector;
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
MessagePosition PinnedMessages::position(MsgId id) const {
|
||||
return MessagePosition{
|
||||
.fullId = FullMsgId(_channelId, id),
|
||||
};
|
||||
}
|
||||
|
||||
void PinnedMessages::add(MsgId messageId) {
|
||||
_list.addOne(position(messageId));
|
||||
}
|
||||
|
||||
void PinnedMessages::add(
|
||||
std::vector<MsgId> &&ids,
|
||||
MsgRange range,
|
||||
std::optional<int> count) {
|
||||
auto positions = ids | ranges::view::transform([&](MsgId id) {
|
||||
return position(id);
|
||||
}) | ranges::to_vector;
|
||||
|
||||
_list.addSlice(
|
||||
std::move(positions),
|
||||
MessagesRange{
|
||||
.from = range.from ? position(range.from) : MinMessagePosition,
|
||||
.till = position(range.till)
|
||||
},
|
||||
count);
|
||||
}
|
||||
|
||||
void PinnedMessages::remove(MsgId messageId) {
|
||||
_list.removeOne(position(messageId));
|
||||
}
|
||||
|
||||
void PinnedMessages::setTopId(MsgId messageId) {
|
||||
while (true) {
|
||||
auto top = topId();
|
||||
if (top > messageId) {
|
||||
remove(top);
|
||||
_storage.remove(Storage::SharedMediaRemoveOne(
|
||||
_peer->id,
|
||||
PinnedType,
|
||||
top));
|
||||
} else if (top == messageId) {
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto wrapped = position(messageId);
|
||||
_list.addSlice(
|
||||
{ wrapped },
|
||||
{ .from = wrapped, .till = MaxMessagePosition },
|
||||
std::nullopt);
|
||||
}
|
||||
|
||||
void PinnedMessages::clearLessThanId(MsgId messageId) {
|
||||
_list.removeLessThan(position(messageId));
|
||||
_storage.add(Storage::SharedMediaAddNew(
|
||||
_peer->id,
|
||||
PinnedType,
|
||||
messageId));
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
@@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_messages.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
namespace Storage {
|
||||
class Facade;
|
||||
} // namespace Storage
|
||||
|
||||
namespace Data {
|
||||
|
||||
struct PinnedAroundId {
|
||||
@@ -21,30 +25,18 @@ struct PinnedAroundId {
|
||||
|
||||
class PinnedMessages final : public base::has_weak_ptr {
|
||||
public:
|
||||
explicit PinnedMessages(ChannelId channelId);
|
||||
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 add(MsgId messageId);
|
||||
void add(
|
||||
std::vector<MsgId> &&ids,
|
||||
MsgRange range,
|
||||
std::optional<int> count);
|
||||
void remove(MsgId messageId);
|
||||
|
||||
void setTopId(MsgId messageId);
|
||||
|
||||
void clearLessThanId(MsgId messageId);
|
||||
|
||||
private:
|
||||
[[nodiscard]] MessagePosition position(MsgId id) const;
|
||||
|
||||
MessagesList _list;
|
||||
ChannelId _channelId = 0;
|
||||
const not_null<PeerData*> _peer;
|
||||
Storage::Facade &_storage;
|
||||
|
||||
};
|
||||
|
||||
|
@@ -55,6 +55,8 @@ std::optional<MTPmessages_Search> PrepareSearchRequest(
|
||||
return MTP_inputMessagesFilterUrl();
|
||||
case Type::ChatPhoto:
|
||||
return MTP_inputMessagesFilterChatPhotos();
|
||||
case Type::Pinned:
|
||||
return MTP_inputMessagesFilterPinned();
|
||||
}
|
||||
return MTP_inputMessagesFilterEmpty();
|
||||
}();
|
||||
|
@@ -123,7 +123,8 @@ rpl::producer<SparseIdsSlice> SharedMediaViewer(
|
||||
using AllRemoved = Storage::SharedMediaRemoveAll;
|
||||
session->storage().sharedMediaAllRemoved(
|
||||
) | rpl::filter([=](const AllRemoved &update) {
|
||||
return (update.peerId == key.peerId);
|
||||
return (update.peerId == key.peerId)
|
||||
&& (update.types.test(key.type));
|
||||
}) | rpl::filter([=] {
|
||||
return builder->removeAll();
|
||||
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
|
||||
|
Reference in New Issue
Block a user