mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Track multiple pinned messages in MessagesList.
This commit is contained in:
parent
399b03beb2
commit
ec35e3f081
19 changed files with 435 additions and 107 deletions
|
@ -435,6 +435,8 @@ 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
|
||||||
|
|
|
@ -1083,6 +1083,17 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||||
_session->data().updateEditedMessage(d.vmessage());
|
_session->data().updateEditedMessage(d.vmessage());
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case mtpc_updatePinnedChannelMessages: {
|
||||||
|
const auto &d = update.c_updatePinnedChannelMessages();
|
||||||
|
const auto channelId = d.vchannel_id().v;
|
||||||
|
for (const auto &msgId : d.vmessages().v) {
|
||||||
|
const auto item = session().data().message(channelId, msgId.v);
|
||||||
|
if (item) {
|
||||||
|
item->setIsPinned(d.is_pinned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case mtpc_updateEditMessage: {
|
case mtpc_updateEditMessage: {
|
||||||
auto &d = update.c_updateEditMessage();
|
auto &d = update.c_updateEditMessage();
|
||||||
_session->data().updateEditedMessage(d.vmessage());
|
_session->data().updateEditedMessage(d.vmessage());
|
||||||
|
@ -1098,6 +1109,17 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||||
_session->data().processMessagesDeleted(d.vchannel_id().v, d.vmessages().v);
|
_session->data().processMessagesDeleted(d.vchannel_id().v, d.vmessages().v);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case mtpc_updatePinnedMessages: {
|
||||||
|
const auto &d = update.c_updatePinnedMessages();
|
||||||
|
const auto peerId = peerFromMTP(d.vpeer());
|
||||||
|
for (const auto &msgId : d.vmessages().v) {
|
||||||
|
const auto item = session().data().message(0, msgId.v);
|
||||||
|
if (item) {
|
||||||
|
item->setIsPinned(d.is_pinned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
default: Unexpected("Type in applyUpdateNoPtsCheck()");
|
default: Unexpected("Type in applyUpdateNoPtsCheck()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1393,6 +1415,21 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case mtpc_updatePinnedChannelMessages: {
|
||||||
|
auto &d = update.c_updatePinnedChannelMessages();
|
||||||
|
auto channel = session().data().channelLoaded(d.vchannel_id().v);
|
||||||
|
|
||||||
|
if (channel && !_handlingChannelDifference) {
|
||||||
|
if (channel->ptsRequesting()) { // skip global updates while getting channel difference
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
channel->ptsUpdateAndApply(d.vpts().v, d.vpts_count().v, update);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
applyUpdateNoPtsCheck(update);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
// Messages being read.
|
// Messages being read.
|
||||||
case mtpc_updateReadHistoryInbox: {
|
case mtpc_updateReadHistoryInbox: {
|
||||||
auto &d = update.c_updateReadHistoryInbox();
|
auto &d = update.c_updateReadHistoryInbox();
|
||||||
|
@ -1998,6 +2035,12 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
// Pinned message.
|
||||||
|
case mtpc_updatePinnedMessages: {
|
||||||
|
const auto &d = update.c_updatePinnedMessages();
|
||||||
|
updateAndApply(d.vpts().v, d.vpts_count().v, update);
|
||||||
|
} break;
|
||||||
|
|
||||||
////// Cloud sticker sets
|
////// Cloud sticker sets
|
||||||
case mtpc_updateNewStickerSet: {
|
case mtpc_updateNewStickerSet: {
|
||||||
const auto &d = update.c_updateNewStickerSet();
|
const auto &d = update.c_updateNewStickerSet();
|
||||||
|
|
|
@ -444,14 +444,20 @@ PinMessageBox::PinMessageBox(
|
||||||
: _peer(peer)
|
: _peer(peer)
|
||||||
, _api(&peer->session().mtp())
|
, _api(&peer->session().mtp())
|
||||||
, _msgId(msgId)
|
, _msgId(msgId)
|
||||||
, _text(this, tr::lng_pinned_pin_sure(tr::now), st::boxLabel) {
|
, _pinningOld(msgId < peer->topPinnedMessageId())
|
||||||
|
, _text(
|
||||||
|
this,
|
||||||
|
(_pinningOld
|
||||||
|
? "Do you want to pin an older message while leaving a more recent one pinned?" // #TODO pinned
|
||||||
|
: tr::lng_pinned_pin_sure(tr::now)),
|
||||||
|
st::boxLabel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PinMessageBox::prepare() {
|
void PinMessageBox::prepare() {
|
||||||
addButton(tr::lng_pinned_pin(), [this] { pinMessage(); });
|
addButton(tr::lng_pinned_pin(), [this] { pinMessage(); });
|
||||||
addButton(tr::lng_cancel(), [this] { closeBox(); });
|
addButton(tr::lng_cancel(), [this] { closeBox(); });
|
||||||
|
|
||||||
if (_peer->isChat() || _peer->isMegagroup()) {
|
if (!_pinningOld && (_peer->isChat() || _peer->isMegagroup())) {
|
||||||
_notify.create(this, tr::lng_pinned_notify(tr::now), true, st::defaultBoxCheckbox);
|
_notify.create(this, tr::lng_pinned_notify(tr::now), true, st::defaultBoxCheckbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -183,7 +183,8 @@ private:
|
||||||
|
|
||||||
const not_null<PeerData*> _peer;
|
const not_null<PeerData*> _peer;
|
||||||
MTP::Sender _api;
|
MTP::Sender _api;
|
||||||
MsgId _msgId;
|
MsgId _msgId = 0;
|
||||||
|
bool _pinningOld = false;
|
||||||
|
|
||||||
object_ptr<Ui::FlatLabel> _text;
|
object_ptr<Ui::FlatLabel> _text;
|
||||||
object_ptr<Ui::Checkbox> _notify = { nullptr };
|
object_ptr<Ui::Checkbox> _notify = { nullptr };
|
||||||
|
|
|
@ -383,9 +383,7 @@ void ChannelData::setUnavailableReasons(
|
||||||
void ChannelData::setAvailableMinId(MsgId availableMinId) {
|
void ChannelData::setAvailableMinId(MsgId availableMinId) {
|
||||||
if (_availableMinId != availableMinId) {
|
if (_availableMinId != availableMinId) {
|
||||||
_availableMinId = availableMinId;
|
_availableMinId = availableMinId;
|
||||||
if (pinnedMessageId() <= _availableMinId) {
|
clearPinnedMessages(_availableMinId + 1);
|
||||||
clearPinnedMessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,9 +770,9 @@ void ApplyChannelUpdate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (const auto pinned = update.vpinned_msg_id()) {
|
if (const auto pinned = update.vpinned_msg_id()) {
|
||||||
channel->setPinnedMessageId(pinned->v);
|
channel->setTopPinnedMessageId(pinned->v);
|
||||||
} else {
|
} else {
|
||||||
channel->clearPinnedMessage();
|
channel->clearPinnedMessages();
|
||||||
}
|
}
|
||||||
if (channel->isMegagroup()) {
|
if (channel->isMegagroup()) {
|
||||||
const auto stickerSet = update.vstickerset();
|
const auto stickerSet = update.vstickerset();
|
||||||
|
|
|
@ -333,9 +333,9 @@ 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->setPinnedMessageId(pinned->v);
|
chat->setTopPinnedMessageId(pinned->v);
|
||||||
} else {
|
} else {
|
||||||
chat->clearPinnedMessage();
|
chat->clearPinnedMessages();
|
||||||
}
|
}
|
||||||
chat->checkFolder(update.vfolder_id().value_or_empty());
|
chat->checkFolder(update.vfolder_id().value_or_empty());
|
||||||
chat->fullUpdated();
|
chat->fullUpdated();
|
||||||
|
|
|
@ -117,6 +117,11 @@ void MessagesList::addRange(
|
||||||
_sliceUpdated.fire(std::move(update));
|
_sliceUpdated.fire(std::move(update));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MessagesList::addOne(MessagePosition messageId) {
|
||||||
|
auto range = { messageId };
|
||||||
|
addRange(range, { messageId, messageId }, std::nullopt, true);
|
||||||
|
}
|
||||||
|
|
||||||
void MessagesList::addNew(MessagePosition messageId) {
|
void MessagesList::addNew(MessagePosition messageId) {
|
||||||
auto range = { messageId };
|
auto range = { messageId };
|
||||||
addRange(range, { messageId, MaxMessagePosition }, std::nullopt, true);
|
addRange(range, { messageId, MaxMessagePosition }, std::nullopt, true);
|
||||||
|
@ -165,6 +170,31 @@ void MessagesList::removeAll(ChannelId channelId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MessagesList::removeLessThan(MessagePosition messageId) {
|
||||||
|
auto removed = 0;
|
||||||
|
for (auto i = begin(_slices); i != end(_slices);) {
|
||||||
|
if (i->range.till <= messageId) {
|
||||||
|
removed += i->messages.size();
|
||||||
|
i = _slices.erase(i);
|
||||||
|
continue;
|
||||||
|
} else if (i->range.from <= messageId) {
|
||||||
|
_slices.modify(i, [&](Slice &slice) {
|
||||||
|
slice.range.from = MinMessagePosition;
|
||||||
|
auto from = begin(slice.messages);
|
||||||
|
auto till = ranges::lower_bound(slice.messages, messageId);
|
||||||
|
if (from != till) {
|
||||||
|
removed += till - from;
|
||||||
|
slice.messages.erase(from, till);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removed && _count) {
|
||||||
|
*_count -= removed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MessagesList::invalidate() {
|
void MessagesList::invalidate() {
|
||||||
_slices.clear();
|
_slices.clear();
|
||||||
_count = std::nullopt;
|
_count = std::nullopt;
|
||||||
|
@ -184,19 +214,26 @@ void MessagesList::invalidateBottom() {
|
||||||
_count = std::nullopt;
|
_count = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessagesResult MessagesList::queryCurrent(const MessagesQuery &query) const {
|
||||||
|
if (!query.aroundId) {
|
||||||
|
return MessagesResult();
|
||||||
|
}
|
||||||
|
const auto slice = ranges::lower_bound(
|
||||||
|
_slices,
|
||||||
|
query.aroundId,
|
||||||
|
std::less<>(),
|
||||||
|
[](const Slice &slice) { return slice.range.till; });
|
||||||
|
return (slice != _slices.end() && slice->range.from <= query.aroundId)
|
||||||
|
? queryFromSlice(query, *slice)
|
||||||
|
: MessagesResult();
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<MessagesResult> MessagesList::query(
|
rpl::producer<MessagesResult> MessagesList::query(
|
||||||
MessagesQuery &&query) const {
|
MessagesQuery &&query) const {
|
||||||
return [this, query = std::move(query)](auto consumer) {
|
return [this, query = std::move(query)](auto consumer) {
|
||||||
auto slice = query.aroundId
|
auto current = queryCurrent(query);
|
||||||
? ranges::lower_bound(
|
if (current.count.has_value() || !current.messageIds.empty()) {
|
||||||
_slices,
|
consumer.put_next(std::move(current));
|
||||||
query.aroundId,
|
|
||||||
std::less<>(),
|
|
||||||
[](const Slice &slice) { return slice.range.till; })
|
|
||||||
: _slices.end();
|
|
||||||
if (slice != _slices.end()
|
|
||||||
&& slice->range.from <= query.aroundId) {
|
|
||||||
consumer.put_next(queryFromSlice(query, *slice));
|
|
||||||
}
|
}
|
||||||
consumer.put_done();
|
consumer.put_done();
|
||||||
return rpl::lifetime();
|
return rpl::lifetime();
|
||||||
|
@ -207,6 +244,31 @@ rpl::producer<MessagesSliceUpdate> MessagesList::sliceUpdated() const {
|
||||||
return _sliceUpdated.events();
|
return _sliceUpdated.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessagesResult MessagesList::snapshot(MessagesQuery &&query) const {
|
||||||
|
return queryCurrent(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessagesList::empty() const {
|
||||||
|
for (const auto &slice : _slices) {
|
||||||
|
if (!slice.messages.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<MessagesResult> MessagesList::viewer(
|
||||||
|
MessagesQuery &&query) const {
|
||||||
|
auto copy = query;
|
||||||
|
return rpl::single(
|
||||||
|
queryCurrent(query)
|
||||||
|
) | rpl::then(sliceUpdated() | rpl::map([=] {
|
||||||
|
return queryCurrent(query);
|
||||||
|
})) | rpl::filter([=](const MessagesResult &value) {
|
||||||
|
return value.count.has_value() || !value.messageIds.empty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
MessagesResult MessagesList::queryFromSlice(
|
MessagesResult MessagesList::queryFromSlice(
|
||||||
const MessagesQuery &query,
|
const MessagesQuery &query,
|
||||||
const Slice &slice) const {
|
const Slice &slice) const {
|
||||||
|
@ -372,7 +434,7 @@ void MessagesSliceBuilder::mergeSliceData(
|
||||||
if (count) {
|
if (count) {
|
||||||
_fullCount = count;
|
_fullCount = count;
|
||||||
}
|
}
|
||||||
const auto impossible = MessagePosition(-1, FullMsgId());
|
const auto impossible = MessagePosition{ .fullId = {}, .date = -1 };
|
||||||
auto wasMinId = _ids.empty() ? impossible : _ids.front();
|
auto wasMinId = _ids.empty() ? impossible : _ids.front();
|
||||||
auto wasMaxId = _ids.empty() ? impossible : _ids.back();
|
auto wasMaxId = _ids.empty() ? impossible : _ids.back();
|
||||||
_ids.merge(messageIds.begin(), messageIds.end());
|
_ids.merge(messageIds.begin(), messageIds.end());
|
||||||
|
|
|
@ -16,11 +16,8 @@ enum class LoadDirection : char {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MessagePosition {
|
struct MessagePosition {
|
||||||
constexpr MessagePosition() = default;
|
FullMsgId fullId;
|
||||||
constexpr MessagePosition(TimeId date, FullMsgId fullId)
|
TimeId date = 0;
|
||||||
: fullId(fullId)
|
|
||||||
, date(date) {
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const {
|
explicit operator bool() const {
|
||||||
return (fullId.msg != 0);
|
return (fullId.msg != 0);
|
||||||
|
@ -50,18 +47,11 @@ struct MessagePosition {
|
||||||
inline constexpr bool operator!=(const MessagePosition &other) const {
|
inline constexpr bool operator!=(const MessagePosition &other) const {
|
||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
}
|
}
|
||||||
|
|
||||||
FullMsgId fullId;
|
|
||||||
TimeId date = 0;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MessagesRange {
|
struct MessagesRange {
|
||||||
constexpr MessagesRange() = default;
|
MessagePosition from;
|
||||||
constexpr MessagesRange(MessagePosition from, MessagePosition till)
|
MessagePosition till;
|
||||||
: from(from)
|
|
||||||
, till(till) {
|
|
||||||
}
|
|
||||||
|
|
||||||
inline constexpr bool operator==(const MessagesRange &other) const {
|
inline constexpr bool operator==(const MessagesRange &other) const {
|
||||||
return (from == other.from)
|
return (from == other.from)
|
||||||
|
@ -70,26 +60,26 @@ struct MessagesRange {
|
||||||
inline constexpr bool operator!=(const MessagesRange &other) const {
|
inline constexpr bool operator!=(const MessagesRange &other) const {
|
||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
}
|
}
|
||||||
|
|
||||||
MessagePosition from;
|
|
||||||
MessagePosition till;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr auto MinDate = TimeId(0);
|
constexpr auto MinDate = TimeId(0);
|
||||||
constexpr auto MaxDate = std::numeric_limits<TimeId>::max();
|
constexpr auto MaxDate = std::numeric_limits<TimeId>::max();
|
||||||
constexpr auto MinMessagePosition = MessagePosition(
|
constexpr auto MinMessagePosition = MessagePosition{
|
||||||
MinDate,
|
.fullId = FullMsgId(NoChannel, 1),
|
||||||
FullMsgId(NoChannel, 1));
|
.date = MinDate,
|
||||||
constexpr auto MaxMessagePosition = MessagePosition(
|
};
|
||||||
MaxDate,
|
constexpr auto MaxMessagePosition = MessagePosition{
|
||||||
FullMsgId(NoChannel, ServerMaxMsgId - 1));
|
.fullId = FullMsgId(NoChannel, ServerMaxMsgId - 1),
|
||||||
constexpr auto FullMessagesRange = MessagesRange(
|
.date = MaxDate,
|
||||||
MinMessagePosition,
|
};
|
||||||
MaxMessagePosition);
|
constexpr auto FullMessagesRange = MessagesRange{
|
||||||
constexpr auto UnreadMessagePosition = MessagePosition(
|
.from = MinMessagePosition,
|
||||||
MinDate,
|
.till = MaxMessagePosition,
|
||||||
FullMsgId(NoChannel, ShowAtUnreadMsgId));
|
};
|
||||||
|
constexpr auto UnreadMessagePosition = MessagePosition{
|
||||||
|
.fullId = FullMsgId(NoChannel, ShowAtUnreadMsgId),
|
||||||
|
.date = MinDate,
|
||||||
|
};
|
||||||
|
|
||||||
struct MessagesSlice {
|
struct MessagesSlice {
|
||||||
std::vector<FullMsgId> ids;
|
std::vector<FullMsgId> ids;
|
||||||
|
@ -100,15 +90,6 @@ struct MessagesSlice {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MessagesQuery {
|
struct MessagesQuery {
|
||||||
MessagesQuery(
|
|
||||||
MessagePosition aroundId,
|
|
||||||
int limitBefore,
|
|
||||||
int limitAfter)
|
|
||||||
: aroundId(aroundId)
|
|
||||||
, limitBefore(limitBefore)
|
|
||||||
, limitAfter(limitAfter) {
|
|
||||||
}
|
|
||||||
|
|
||||||
MessagePosition aroundId;
|
MessagePosition aroundId;
|
||||||
int limitBefore = 0;
|
int limitBefore = 0;
|
||||||
int limitAfter = 0;
|
int limitAfter = 0;
|
||||||
|
@ -129,6 +110,7 @@ struct MessagesSliceUpdate {
|
||||||
|
|
||||||
class MessagesList {
|
class MessagesList {
|
||||||
public:
|
public:
|
||||||
|
void addOne(MessagePosition messageId);
|
||||||
void addNew(MessagePosition messageId);
|
void addNew(MessagePosition messageId);
|
||||||
void addSlice(
|
void addSlice(
|
||||||
std::vector<MessagePosition> &&messageIds,
|
std::vector<MessagePosition> &&messageIds,
|
||||||
|
@ -136,10 +118,18 @@ public:
|
||||||
std::optional<int> count);
|
std::optional<int> count);
|
||||||
void removeOne(MessagePosition messageId);
|
void removeOne(MessagePosition messageId);
|
||||||
void removeAll(ChannelId channelId);
|
void removeAll(ChannelId channelId);
|
||||||
|
void removeLessThan(MessagePosition messageId);
|
||||||
void invalidate();
|
void invalidate();
|
||||||
void invalidateBottom();
|
void invalidateBottom();
|
||||||
rpl::producer<MessagesResult> query(MessagesQuery &&query) const;
|
[[nodiscard]] rpl::producer<MessagesResult> query(
|
||||||
rpl::producer<MessagesSliceUpdate> sliceUpdated() const;
|
MessagesQuery &&query) const;
|
||||||
|
[[nodiscard]] rpl::producer<MessagesSliceUpdate> sliceUpdated() const;
|
||||||
|
|
||||||
|
[[nodiscard]] MessagesResult snapshot(MessagesQuery &&query) const;
|
||||||
|
[[nodiscard]] rpl::producer<MessagesResult> viewer(
|
||||||
|
MessagesQuery &&query) const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Slice {
|
struct Slice {
|
||||||
|
@ -183,6 +173,7 @@ private:
|
||||||
MessagesResult queryFromSlice(
|
MessagesResult queryFromSlice(
|
||||||
const MessagesQuery &query,
|
const MessagesQuery &query,
|
||||||
const Slice &slice) const;
|
const Slice &slice) const;
|
||||||
|
MessagesResult queryCurrent(const MessagesQuery &query) const;
|
||||||
|
|
||||||
std::optional<int> _count;
|
std::optional<int> _count;
|
||||||
base::flat_set<Slice> _slices;
|
base::flat_set<Slice> _slices;
|
||||||
|
|
|
@ -16,6 +16,7 @@ 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"
|
||||||
|
@ -460,18 +461,83 @@ bool PeerData::canEditMessagesIndefinitely() const {
|
||||||
Unexpected("Peer type in PeerData::canEditMessagesIndefinitely.");
|
Unexpected("Peer type in PeerData::canEditMessagesIndefinitely.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerData::setPinnedMessageId(MsgId messageId) {
|
MsgId PeerData::topPinnedMessageId() const {
|
||||||
|
return _pinnedMessages ? _pinnedMessages->topId() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerData::ensurePinnedMessagesCreated() {
|
||||||
|
if (!_pinnedMessages) {
|
||||||
|
_pinnedMessages = std::make_unique<Data::PinnedMessages>(
|
||||||
|
peerToChannel(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerData::removeEmptyPinnedMessages() {
|
||||||
|
if (_pinnedMessages && _pinnedMessages->empty()) {
|
||||||
|
_pinnedMessages = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ensurePinnedMessagesCreated();
|
||||||
|
_pinnedMessages->setTopId(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerData::clearPinnedMessages(MsgId lessThanId) {
|
||||||
|
if (!_pinnedMessages) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pinnedMessages->clearLessThanId(lessThanId);
|
||||||
|
removeEmptyPinnedMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerData::addPinnedMessage(MsgId messageId) {
|
||||||
|
if (messageIdTooSmall(messageId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ensurePinnedMessagesCreated();
|
||||||
|
_pinnedMessages->add(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerData::addPinnedSlice(
|
||||||
|
std::vector<MsgId> &&ids,
|
||||||
|
MsgId from,
|
||||||
|
MsgId till) {
|
||||||
const auto min = [&] {
|
const auto min = [&] {
|
||||||
if (const auto channel = asChannel()) {
|
if (const auto channel = asChannel()) {
|
||||||
return channel->availableMinId();
|
return channel->availableMinId();
|
||||||
}
|
}
|
||||||
return MsgId(0);
|
return 0;
|
||||||
}();
|
}();
|
||||||
messageId = (messageId > min) ? messageId : MsgId(0);
|
ids.erase(
|
||||||
if (_pinnedMessageId != messageId) {
|
ranges::remove_if(ids, [&](MsgId id) { return id <= min; }),
|
||||||
_pinnedMessageId = messageId;
|
end(ids));
|
||||||
session().changes().peerUpdated(this, UpdateFlag::PinnedMessage);
|
if (from <= min) {
|
||||||
|
from = 0;
|
||||||
}
|
}
|
||||||
|
if (ids.empty() && !_pinnedMessages) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ensurePinnedMessagesCreated();
|
||||||
|
_pinnedMessages->add(std::move(ids), from, till, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerData::removePinnedMessage(MsgId messageId) {
|
||||||
|
if (!_pinnedMessages) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pinnedMessages->remove(messageId);
|
||||||
|
removeEmptyPinnedMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeerData::canExportChatHistory() const {
|
bool PeerData::canExportChatHistory() const {
|
||||||
|
|
|
@ -29,6 +29,7 @@ class Session;
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
class Session;
|
class Session;
|
||||||
|
class PinnedMessages;
|
||||||
|
|
||||||
int PeerColorIndex(PeerId peerId);
|
int PeerColorIndex(PeerId peerId);
|
||||||
int PeerColorIndex(int32 bareId);
|
int PeerColorIndex(int32 bareId);
|
||||||
|
@ -326,13 +327,12 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool canPinMessages() const;
|
[[nodiscard]] bool canPinMessages() const;
|
||||||
[[nodiscard]] bool canEditMessagesIndefinitely() const;
|
[[nodiscard]] bool canEditMessagesIndefinitely() const;
|
||||||
[[nodiscard]] MsgId pinnedMessageId() const {
|
[[nodiscard]] MsgId topPinnedMessageId() const;
|
||||||
return _pinnedMessageId;
|
void setTopPinnedMessageId(MsgId messageId);
|
||||||
}
|
void clearPinnedMessages(MsgId lessThanId = ServerMaxMsgId);
|
||||||
void setPinnedMessageId(MsgId messageId);
|
void addPinnedMessage(MsgId messageId);
|
||||||
void clearPinnedMessage() {
|
void addPinnedSlice(std::vector<MsgId> &&ids, MsgId from, MsgId till);
|
||||||
setPinnedMessageId(0);
|
void removePinnedMessage(MsgId messageId);
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool canExportChatHistory() const;
|
[[nodiscard]] bool canExportChatHistory() const;
|
||||||
|
|
||||||
|
@ -411,6 +411,10 @@ 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);
|
||||||
|
|
||||||
|
@ -428,7 +432,7 @@ private:
|
||||||
base::flat_set<QChar> _nameFirstLetters;
|
base::flat_set<QChar> _nameFirstLetters;
|
||||||
|
|
||||||
crl::time _lastFullUpdate = 0;
|
crl::time _lastFullUpdate = 0;
|
||||||
MsgId _pinnedMessageId = 0;
|
std::unique_ptr<Data::PinnedMessages> _pinnedMessages;
|
||||||
|
|
||||||
Settings _settings = { kSettingsUnknown };
|
Settings _settings = { kSettingsUnknown };
|
||||||
BlockStatus _blockStatus = BlockStatus::Unknown;
|
BlockStatus _blockStatus = BlockStatus::Unknown;
|
||||||
|
|
88
Telegram/SourceFiles/data/data_pinned_messages.cpp
Normal file
88
Telegram/SourceFiles/data/data_pinned_messages.cpp
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
PinnedMessages::PinnedMessages(ChannelId channelId) : _channelId(channelId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PinnedMessages::empty() const {
|
||||||
|
return _list.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
MsgId from,
|
||||||
|
MsgId till,
|
||||||
|
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 = from ? position(from) : MinMessagePosition,
|
||||||
|
.till = position(till)
|
||||||
|
},
|
||||||
|
count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinnedMessages::remove(MsgId messageId) {
|
||||||
|
_list.removeOne(MessagePosition{
|
||||||
|
.fullId = FullMsgId(0, messageId),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinnedMessages::setTopId(MsgId messageId) {
|
||||||
|
while (true) {
|
||||||
|
auto top = topId();
|
||||||
|
if (top > messageId) {
|
||||||
|
remove(top);
|
||||||
|
} else if (top == messageId) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto position = MessagePosition{
|
||||||
|
.fullId = FullMsgId(0, messageId),
|
||||||
|
};
|
||||||
|
_list.addSlice(
|
||||||
|
{ position },
|
||||||
|
{ .from = position, .till = MaxMessagePosition },
|
||||||
|
std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PinnedMessages::clearLessThanId(MsgId messageId) {
|
||||||
|
_list.removeLessThan(MessagePosition{
|
||||||
|
.fullId = FullMsgId(0, messageId),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Data
|
41
Telegram/SourceFiles/data/data_pinned_messages.h
Normal file
41
Telegram/SourceFiles/data/data_pinned_messages.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
class PinnedMessages final {
|
||||||
|
public:
|
||||||
|
explicit PinnedMessages(ChannelId channelId);
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const;
|
||||||
|
[[nodiscard]] MsgId topId() const;
|
||||||
|
|
||||||
|
void add(MsgId messageId);
|
||||||
|
void add(
|
||||||
|
std::vector<MsgId> &&ids,
|
||||||
|
MsgId from,
|
||||||
|
MsgId till,
|
||||||
|
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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Data
|
|
@ -262,9 +262,9 @@ 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->setPinnedMessageId(pinned->v);
|
user->setTopPinnedMessageId(pinned->v);
|
||||||
} else {
|
} else {
|
||||||
user->clearPinnedMessage();
|
user->clearPinnedMessages();
|
||||||
}
|
}
|
||||||
user->setFullFlags(update.vflags().v);
|
user->setFullFlags(update.vflags().v);
|
||||||
user->setIsBlocked(update.is_blocked());
|
user->setIsBlocked(update.is_blocked());
|
||||||
|
|
|
@ -177,9 +177,7 @@ void History::itemVanished(not_null<HistoryItem*> item) {
|
||||||
&& unreadCount() > 0) {
|
&& unreadCount() > 0) {
|
||||||
setUnreadCount(unreadCount() - 1);
|
setUnreadCount(unreadCount() - 1);
|
||||||
}
|
}
|
||||||
if (peer->pinnedMessageId() == item->id) {
|
peer->removePinnedMessage(item->id);
|
||||||
peer->clearPinnedMessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::setLocalDraft(std::unique_ptr<Data::Draft> &&draft) {
|
void History::setLocalDraft(std::unique_ptr<Data::Draft> &&draft) {
|
||||||
|
@ -709,6 +707,9 @@ not_null<HistoryItem*> History::addNewToBack(
|
||||||
item->id,
|
item->id,
|
||||||
{ from, till }));
|
{ from, till }));
|
||||||
}
|
}
|
||||||
|
if (item->isPinned()) {
|
||||||
|
item->history()->peer->addPinnedMessage(item->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (item->from()->id) {
|
if (item->from()->id) {
|
||||||
if (auto user = item->from()->asUser()) {
|
if (auto user = item->from()->asUser()) {
|
||||||
|
@ -1005,8 +1006,8 @@ void History::applyServiceChanges(
|
||||||
if (const auto replyTo = data.vreply_to()) {
|
if (const auto replyTo = data.vreply_to()) {
|
||||||
replyTo->match([&](const MTPDmessageReplyHeader &data) {
|
replyTo->match([&](const MTPDmessageReplyHeader &data) {
|
||||||
if (item) {
|
if (item) {
|
||||||
item->history()->peer->setPinnedMessageId(
|
//item->history()->peer->setTopPinnedMessageId( // #TODO pinned
|
||||||
data.vreply_to_msg_id().v);
|
// data.vreply_to_msg_id().v);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1160,6 +1161,7 @@ void History::addEdgesToSharedMedia() {
|
||||||
{},
|
{},
|
||||||
{ from, till }));
|
{ from, till }));
|
||||||
}
|
}
|
||||||
|
peer->addPinnedSlice({}, from, till);
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::addOlderSlice(const QVector<MTPMessage> &slice) {
|
void History::addOlderSlice(const QVector<MTPMessage> &slice) {
|
||||||
|
@ -1323,6 +1325,7 @@ void History::checkAddAllToUnreadMentions() {
|
||||||
|
|
||||||
void History::addToSharedMedia(
|
void History::addToSharedMedia(
|
||||||
const std::vector<not_null<HistoryItem*>> &items) {
|
const std::vector<not_null<HistoryItem*>> &items) {
|
||||||
|
auto pinned = std::vector<MsgId>();
|
||||||
std::vector<MsgId> medias[Storage::kSharedMediaTypeCount];
|
std::vector<MsgId> medias[Storage::kSharedMediaTypeCount];
|
||||||
for (const auto item : items) {
|
for (const auto item : items) {
|
||||||
if (const auto types = item->sharedMediaTypes()) {
|
if (const auto types = item->sharedMediaTypes()) {
|
||||||
|
@ -1336,6 +1339,12 @@ void History::addToSharedMedia(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (item->isPinned()) {
|
||||||
|
if (pinned.empty()) {
|
||||||
|
pinned.reserve(items.size());
|
||||||
|
}
|
||||||
|
pinned.push_back(item->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const auto from = loadedAtTop() ? 0 : minMsgId();
|
const auto from = loadedAtTop() ? 0 : minMsgId();
|
||||||
const auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
|
const auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
|
||||||
|
@ -1349,6 +1358,7 @@ void History::addToSharedMedia(
|
||||||
{ from, till }));
|
{ from, till }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
peer->addPinnedSlice(std::move(pinned), from, till);
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::calculateFirstUnreadMessage() {
|
void History::calculateFirstUnreadMessage() {
|
||||||
|
@ -3017,7 +3027,7 @@ void History::clear(ClearType type) {
|
||||||
clearSharedMedia();
|
clearSharedMedia();
|
||||||
clearLastKeyboard();
|
clearLastKeyboard();
|
||||||
if (const auto channel = peer->asChannel()) {
|
if (const auto channel = peer->asChannel()) {
|
||||||
channel->clearPinnedMessage();
|
channel->clearPinnedMessages();
|
||||||
//if (const auto feed = channel->feed()) { // #feed
|
//if (const auto feed = channel->feed()) { // #feed
|
||||||
// // Should be after resetting the _lastMessage.
|
// // Should be after resetting the _lastMessage.
|
||||||
// feed->historyCleared(this);
|
// feed->historyCleared(this);
|
||||||
|
|
|
@ -347,6 +347,16 @@ void HistoryItem::markMediaRead() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryItem::setIsPinned(bool pinned) {
|
||||||
|
if (pinned) {
|
||||||
|
_flags |= MTPDmessage::Flag::f_pinned;
|
||||||
|
history()->peer->addPinnedMessage(id);
|
||||||
|
} else {
|
||||||
|
_flags &= ~MTPDmessage::Flag::f_pinned;
|
||||||
|
history()->peer->removePinnedMessage(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool HistoryItem::definesReplyKeyboard() const {
|
bool HistoryItem::definesReplyKeyboard() const {
|
||||||
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
||||||
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
|
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
|
||||||
|
@ -476,6 +486,9 @@ void HistoryItem::indexAsNewItem() {
|
||||||
types,
|
types,
|
||||||
id));
|
id));
|
||||||
}
|
}
|
||||||
|
if (isPinned()) {
|
||||||
|
_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()) {
|
||||||
// _history->session().storage().add(Storage::FeedMessagesAddNew(
|
// _history->session().storage().add(Storage::FeedMessagesAddNew(
|
||||||
|
@ -509,10 +522,6 @@ void HistoryItem::setRealId(MsgId newId) {
|
||||||
_history->owner().requestItemRepaint(this);
|
_history->owner().requestItemRepaint(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryItem::isPinned() const {
|
|
||||||
return (_history->peer->pinnedMessageId() == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HistoryItem::canPin() const {
|
bool HistoryItem::canPin() const {
|
||||||
if (id < 0 || !toHistoryMessage()) {
|
if (id < 0 || !toHistoryMessage()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -656,7 +665,7 @@ ChannelId HistoryItem::channelId() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::MessagePosition HistoryItem::position() const {
|
Data::MessagePosition HistoryItem::position() const {
|
||||||
return Data::MessagePosition(date(), fullId());
|
return { .fullId = fullId(), .date = date() };
|
||||||
}
|
}
|
||||||
|
|
||||||
MsgId HistoryItem::replyToId() const {
|
MsgId HistoryItem::replyToId() const {
|
||||||
|
|
|
@ -113,6 +113,9 @@ public:
|
||||||
[[nodiscard]] bool out() const {
|
[[nodiscard]] bool out() const {
|
||||||
return _flags & MTPDmessage::Flag::f_out;
|
return _flags & MTPDmessage::Flag::f_out;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] bool isPinned() const {
|
||||||
|
return _flags & MTPDmessage::Flag::f_pinned;
|
||||||
|
}
|
||||||
[[nodiscard]] bool unread() const;
|
[[nodiscard]] bool unread() const;
|
||||||
[[nodiscard]] bool showNotification() const;
|
[[nodiscard]] bool showNotification() const;
|
||||||
void markClientSideAsRead();
|
void markClientSideAsRead();
|
||||||
|
@ -121,6 +124,7 @@ public:
|
||||||
[[nodiscard]] bool isUnreadMedia() const;
|
[[nodiscard]] bool isUnreadMedia() const;
|
||||||
[[nodiscard]] bool hasUnreadMediaFlag() const;
|
[[nodiscard]] bool hasUnreadMediaFlag() const;
|
||||||
void markMediaRead();
|
void markMediaRead();
|
||||||
|
void setIsPinned(bool isPinned);
|
||||||
|
|
||||||
// For edit media in history_message.
|
// For edit media in history_message.
|
||||||
virtual void returnSavedMedia() {};
|
virtual void returnSavedMedia() {};
|
||||||
|
@ -312,7 +316,6 @@ public:
|
||||||
return _text.isEmpty();
|
return _text.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool isPinned() const;
|
|
||||||
[[nodiscard]] bool canPin() const;
|
[[nodiscard]] bool canPin() const;
|
||||||
[[nodiscard]] bool canStopPoll() const;
|
[[nodiscard]] bool canStopPoll() const;
|
||||||
[[nodiscard]] virtual bool allowsSendNow() const;
|
[[nodiscard]] virtual bool allowsSendNow() const;
|
||||||
|
|
|
@ -5200,6 +5200,7 @@ void HistoryWidget::updatePinnedBar(bool force) {
|
||||||
if (!_pinnedBar) {
|
if (!_pinnedBar) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto messageId = _pinnedBar->msgId;
|
||||||
if (!force) {
|
if (!force) {
|
||||||
if (_pinnedBar->msg) {
|
if (_pinnedBar->msg) {
|
||||||
return;
|
return;
|
||||||
|
@ -5208,7 +5209,9 @@ void HistoryWidget::updatePinnedBar(bool force) {
|
||||||
|
|
||||||
Assert(_history != nullptr);
|
Assert(_history != nullptr);
|
||||||
if (!_pinnedBar->msg) {
|
if (!_pinnedBar->msg) {
|
||||||
_pinnedBar->msg = session().data().message(_history->channelId(), _pinnedBar->msgId);
|
_pinnedBar->msg = session().data().message(
|
||||||
|
_history->channelId(),
|
||||||
|
messageId);
|
||||||
}
|
}
|
||||||
if (_pinnedBar->msg) {
|
if (_pinnedBar->msg) {
|
||||||
_pinnedBar->text.setText(
|
_pinnedBar->text.setText(
|
||||||
|
@ -5217,17 +5220,15 @@ void HistoryWidget::updatePinnedBar(bool force) {
|
||||||
Ui::DialogTextOptions());
|
Ui::DialogTextOptions());
|
||||||
update();
|
update();
|
||||||
} else if (force) {
|
} else if (force) {
|
||||||
if (auto channel = _peer ? _peer->asChannel() : nullptr) {
|
|
||||||
channel->clearPinnedMessage();
|
|
||||||
}
|
|
||||||
destroyPinnedBar();
|
destroyPinnedBar();
|
||||||
|
_history->peer->removePinnedMessage(messageId);
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryWidget::pinnedMsgVisibilityUpdated() {
|
bool HistoryWidget::pinnedMsgVisibilityUpdated() {
|
||||||
auto result = false;
|
auto result = false;
|
||||||
auto pinnedId = _peer->pinnedMessageId();
|
auto pinnedId = _peer->topPinnedMessageId();
|
||||||
if (pinnedId && !_peer->canPinMessages()) {
|
if (pinnedId && !_peer->canPinMessages()) {
|
||||||
const auto hiddenId = session().settings().hiddenPinnedMessageId(
|
const auto hiddenId = session().settings().hiddenPinnedMessageId(
|
||||||
_peer->id);
|
_peer->id);
|
||||||
|
@ -5540,23 +5541,23 @@ void HistoryWidget::unpinMessage(FullMsgId itemId) {
|
||||||
if (!_peer) {
|
if (!_peer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UnpinMessage(_peer);
|
UnpinMessage(_peer, itemId.msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::UnpinMessage(not_null<PeerData*> peer) {
|
void HistoryWidget::UnpinMessage(not_null<PeerData*> peer, MsgId msgId) {
|
||||||
if (!peer) {
|
if (!peer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto session = &peer->session();
|
const auto session = &peer->session();
|
||||||
Ui::show(Box<ConfirmBox>(tr::lng_pinned_unpin_sure(tr::now), tr::lng_pinned_unpin(tr::now), crl::guard(session, [=] {
|
Ui::show(Box<ConfirmBox>(tr::lng_pinned_unpin_sure(tr::now), tr::lng_pinned_unpin(tr::now), crl::guard(session, [=] {
|
||||||
peer->clearPinnedMessage();
|
peer->removePinnedMessage(msgId);
|
||||||
|
|
||||||
Ui::hideLayer();
|
Ui::hideLayer();
|
||||||
session->api().request(MTPmessages_UpdatePinnedMessage(
|
session->api().request(MTPmessages_UpdatePinnedMessage(
|
||||||
MTP_flags(0),
|
MTP_flags(MTPmessages_UpdatePinnedMessage::Flag::f_unpin),
|
||||||
peer->input,
|
peer->input,
|
||||||
MTP_int(0)
|
MTP_int(msgId)
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
session->api().applyUpdates(result);
|
session->api().applyUpdates(result);
|
||||||
}).send();
|
}).send();
|
||||||
|
@ -5564,7 +5565,7 @@ void HistoryWidget::UnpinMessage(not_null<PeerData*> peer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::hidePinnedMessage() {
|
void HistoryWidget::hidePinnedMessage() {
|
||||||
const auto pinnedId = _peer ? _peer->pinnedMessageId() : MsgId(0);
|
const auto pinnedId = _peer ? _peer->topPinnedMessageId() : MsgId(0);
|
||||||
if (!pinnedId) {
|
if (!pinnedId) {
|
||||||
if (pinnedMsgVisibilityUpdated()) {
|
if (pinnedMsgVisibilityUpdated()) {
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
|
|
|
@ -530,7 +530,7 @@ private:
|
||||||
void addMessagesToFront(PeerData *peer, const QVector<MTPMessage> &messages);
|
void addMessagesToFront(PeerData *peer, const QVector<MTPMessage> &messages);
|
||||||
void addMessagesToBack(PeerData *peer, const QVector<MTPMessage> &messages);
|
void addMessagesToBack(PeerData *peer, const QVector<MTPMessage> &messages);
|
||||||
|
|
||||||
static void UnpinMessage(not_null<PeerData*> peer);
|
static void UnpinMessage(not_null<PeerData*> peer, MsgId msgId);
|
||||||
|
|
||||||
void updateHistoryGeometry(bool initial = false, bool loadedDown = false, const ScrollChange &change = { ScrollChangeNone, 0 });
|
void updateHistoryGeometry(bool initial = false, bool loadedDown = false, const ScrollChange &change = { ScrollChangeNone, 0 });
|
||||||
void updateListSize();
|
void updateListSize();
|
||||||
|
|
|
@ -85,8 +85,9 @@ RepliesMemento::RepliesMemento(
|
||||||
: RepliesMemento(commentsItem->history(), commentsItem->id, commentId) {
|
: RepliesMemento(commentsItem->history(), commentsItem->id, commentId) {
|
||||||
if (commentId) {
|
if (commentId) {
|
||||||
_list.setAroundPosition({
|
_list.setAroundPosition({
|
||||||
TimeId(0),
|
.fullId = FullMsgId(commentsItem->history()->channelId(), commentId),
|
||||||
FullMsgId(commentsItem->history()->channelId(), commentId)
|
.date = TimeId(0),
|
||||||
|
|
||||||
});
|
});
|
||||||
} else if (commentsItem->computeRepliesInboxReadTillFull() == MsgId(1)) {
|
} else if (commentsItem->computeRepliesInboxReadTillFull() == MsgId(1)) {
|
||||||
_list.setAroundPosition(Data::MinMessagePosition);
|
_list.setAroundPosition(Data::MinMessagePosition);
|
||||||
|
@ -1430,7 +1431,9 @@ bool RepliesWidget::showMessage(
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}();
|
}();
|
||||||
showAtPosition(Data::MessagePosition(message->date(), id), originItem);
|
showAtPosition(
|
||||||
|
Data::MessagePosition{ .fullId = id, .date = message->date() },
|
||||||
|
originItem);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1479,8 +1482,8 @@ void RepliesWidget::restoreState(not_null<RepliesMemento*> 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{
|
||||||
TimeId(0),
|
.fullId = FullMsgId(_history->channelId(), highlight),
|
||||||
FullMsgId(_history->channelId(), highlight)
|
.date = TimeId(0),
|
||||||
};
|
};
|
||||||
_inner->showAroundPosition(position, [=] {
|
_inner->showAroundPosition(position, [=] {
|
||||||
return showAtPositionNow(position, nullptr);
|
return showAtPositionNow(position, nullptr);
|
||||||
|
|
Loading…
Add table
Reference in a new issue