forwarded getState fixed, pinned message service msg dependency request and text update done

This commit is contained in:
John Preston 2016-03-05 23:12:55 +02:00
parent 2bfb1e0f1f
commit 1c722a425d
14 changed files with 299 additions and 119 deletions

View file

@ -533,6 +533,17 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_action_created_chat" = "{from} created group «{title}»";
"lng_action_created_channel" = "Channel «{title}» created";
"lng_action_group_migrate" = "The group was upgraded to a supergroup";
"lng_action_pinned_message" = "{from} pinned «{text}»";
"lng_action_pinned_media" = "{from} pinned {media}";
"lng_action_pinned_media_photo" = "a photo";
"lng_action_pinned_media_video" = "a video file";
"lng_action_pinned_media_music" = "a music file";
"lng_action_pinned_media_voice" = "a voice message";
"lng_action_pinned_media_file" = "a file";
"lng_action_pinned_media_gif" = "a GIF animation";
"lng_action_pinned_media_contact" = "a contact information";
"lng_action_pinned_media_location" = "a location mark";
"lng_action_pinned_media_sticker" = "a sticker";
"lng_profile_migrate_reached" = "{count:_not_used_|# member|# members} limit reached";
"lng_profile_migrate_about" = "If you'd like to go over this limit, you can upgrade your group to a supergroup. In supergroups:";

View file

@ -32,7 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
ApiWrap::ApiWrap(QObject *parent) : QObject(parent) {
App::initBackground();
connect(&_replyToTimer, SIGNAL(timeout()), this, SLOT(resolveReplyTo()));
connect(&_dependencyTimer, SIGNAL(timeout()), this, SLOT(resolveDependencyItems()));
connect(&_webPagesTimer, SIGNAL(timeout()), this, SLOT(resolveWebPages()));
}
@ -40,78 +40,78 @@ void ApiWrap::init() {
}
void ApiWrap::itemRemoved(HistoryItem *item) {
if (HistoryReply *reply = item->toHistoryReply()) {
ChannelData *channel = reply->history()->peer->asChannel();
ReplyToRequests *requests(replyToRequests(channel, true));
if (MsgId dependencyMsgId = item->dependencyMsgId()) {
ChannelData *channel = item->history()->peer->asChannel();
DependencyRequests *requests(dependencyRequests(channel, true));
if (requests) {
ReplyToRequests::iterator i = requests->find(reply->replyToId());
DependencyRequests::iterator i = requests->find(dependencyMsgId);
if (i != requests->cend()) {
for (QList<HistoryReply*>::iterator j = i->replies.begin(); j != i->replies.end();) {
if ((*j) == reply) {
j = i->replies.erase(j);
for (QList<HistoryItem*>::iterator j = i->dependentItems.begin(); j != i->dependentItems.end();) {
if ((*j) == item) {
j = i->dependentItems.erase(j);
} else {
++j;
}
}
if (i->replies.isEmpty()) {
if (i->dependentItems.isEmpty()) {
requests->erase(i);
}
}
if (channel && requests->isEmpty()) {
_channelReplyToRequests.remove(channel);
_channelDependencyRequests.remove(channel);
}
}
}
}
void ApiWrap::requestReplyTo(HistoryReply *reply, ChannelData *channel, MsgId id) {
ReplyToRequest &req(channel ? _channelReplyToRequests[channel][id] : _replyToRequests[id]);
req.replies.append(reply);
if (!req.req) _replyToTimer.start(1);
void ApiWrap::requestDependencyItem(HistoryItem *dependency, ChannelData *channel, MsgId id) {
DependencyRequest &req(channel ? _channelDependencyRequests[channel][id] : _dependencyRequests[id]);
req.dependentItems.append(dependency);
if (!req.req) _dependencyTimer.start(1);
}
ApiWrap::MessageIds ApiWrap::collectMessageIds(const ReplyToRequests &requests) {
ApiWrap::MessageIds ApiWrap::collectMessageIds(const DependencyRequests &requests) {
MessageIds result;
result.reserve(requests.size());
for (ReplyToRequests::const_iterator i = requests.cbegin(), e = requests.cend(); i != e; ++i) {
for (DependencyRequests::const_iterator i = requests.cbegin(), e = requests.cend(); i != e; ++i) {
if (i.value().req > 0) continue;
result.push_back(MTP_int(i.key()));
}
return result;
}
ApiWrap::ReplyToRequests *ApiWrap::replyToRequests(ChannelData *channel, bool onlyExisting) {
ApiWrap::DependencyRequests *ApiWrap::dependencyRequests(ChannelData *channel, bool onlyExisting) {
if (channel) {
ChannelReplyToRequests::iterator i = _channelReplyToRequests.find(channel);
if (i == _channelReplyToRequests.cend()) {
ChannelDependencyRequests::iterator i = _channelDependencyRequests.find(channel);
if (i == _channelDependencyRequests.cend()) {
if (onlyExisting) return 0;
i = _channelReplyToRequests.insert(channel, ReplyToRequests());
i = _channelDependencyRequests.insert(channel, DependencyRequests());
}
return &i.value();
}
return &_replyToRequests;
return &_dependencyRequests;
}
void ApiWrap::resolveReplyTo() {
if (_replyToRequests.isEmpty() && _channelReplyToRequests.isEmpty()) return;
void ApiWrap::resolveDependencyItems() {
if (_dependencyRequests.isEmpty() && _channelDependencyRequests.isEmpty()) return;
MessageIds ids = collectMessageIds(_replyToRequests);
MessageIds ids = collectMessageIds(_dependencyRequests);
if (!ids.isEmpty()) {
mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotReplyTo, (ChannelData*)0), RPCFailHandlerPtr(), 0, 5);
for (ReplyToRequests::iterator i = _replyToRequests.begin(); i != _replyToRequests.cend(); ++i) {
mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotDependencyItem, (ChannelData*)0), RPCFailHandlerPtr(), 0, 5);
for (DependencyRequests::iterator i = _dependencyRequests.begin(); i != _dependencyRequests.cend(); ++i) {
if (i.value().req > 0) continue;
i.value().req = req;
}
}
for (ChannelReplyToRequests::iterator j = _channelReplyToRequests.begin(); j != _channelReplyToRequests.cend();) {
for (ChannelDependencyRequests::iterator j = _channelDependencyRequests.begin(); j != _channelDependencyRequests.cend();) {
if (j->isEmpty()) {
j = _channelReplyToRequests.erase(j);
j = _channelDependencyRequests.erase(j);
continue;
}
MessageIds ids = collectMessageIds(j.value());
if (!ids.isEmpty()) {
mtpRequestId req = MTP::send(MTPchannels_GetMessages(j.key()->inputChannel, MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotReplyTo, j.key()), RPCFailHandlerPtr(), 0, 5);
for (ReplyToRequests::iterator i = j->begin(); i != j->cend(); ++i) {
mtpRequestId req = MTP::send(MTPchannels_GetMessages(j.key()->inputChannel, MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotDependencyItem, j.key()), RPCFailHandlerPtr(), 0, 5);
for (DependencyRequests::iterator i = j->begin(); i != j->cend(); ++i) {
if (i.value().req > 0) continue;
i.value().req = req;
}
@ -120,7 +120,7 @@ void ApiWrap::resolveReplyTo() {
}
}
void ApiWrap::gotReplyTo(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
void ApiWrap::gotDependencyItem(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
switch (msgs.type()) {
case mtpc_messages_messages: {
const MTPDmessages_messages &d(msgs.c_messages_messages());
@ -141,10 +141,10 @@ void ApiWrap::gotReplyTo(ChannelData *channel, const MTPmessages_Messages &msgs,
if (channel) {
channel->ptsReceived(d.vpts.v);
} else {
LOG(("App Error: received messages.channelMessages when no channel was passed! (ApiWrap::gotReplyTo)"));
LOG(("App Error: received messages.channelMessages when no channel was passed! (ApiWrap::gotDependencyItem)"));
}
if (d.has_collapsed()) { // should not be returned
LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (ApiWrap::gotReplyTo)"));
LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (ApiWrap::gotDependencyItem)"));
}
App::feedUsers(d.vusers);
@ -152,15 +152,15 @@ void ApiWrap::gotReplyTo(ChannelData *channel, const MTPmessages_Messages &msgs,
App::feedMsgs(d.vmessages, NewMessageExisting);
} break;
}
ReplyToRequests *requests(replyToRequests(channel, true));
DependencyRequests *requests(dependencyRequests(channel, true));
if (requests) {
for (ReplyToRequests::iterator i = requests->begin(); i != requests->cend();) {
for (DependencyRequests::iterator i = requests->begin(); i != requests->cend();) {
if (i.value().req == req) {
for (QList<HistoryReply*>::const_iterator j = i.value().replies.cbegin(), e = i.value().replies.cend(); j != e; ++j) {
for (QList<HistoryItem*>::const_iterator j = i.value().dependentItems.cbegin(), e = i.value().dependentItems.cend(); j != e; ++j) {
if (*j) {
(*j)->updateReplyTo(true);
} else {
App::main()->updateReplyTo();
(*j)->updateDependencyItem();
} else if (App::main()) {
App::main()->updateDependencyItem();
}
}
i = requests->erase(i);
@ -169,7 +169,7 @@ void ApiWrap::gotReplyTo(ChannelData *channel, const MTPmessages_Messages &msgs,
}
}
if (channel && requests->isEmpty()) {
_channelReplyToRequests.remove(channel);
_channelDependencyRequests.remove(channel);
}
}
}

View file

@ -29,8 +29,8 @@ public:
void init();
void itemRemoved(HistoryItem *item);
void requestReplyTo(HistoryReply *reply, ChannelData *channel, MsgId id);
void requestDependencyItem(HistoryItem *dependent, ChannelData *channel, MsgId id);
void requestFullPeer(PeerData *peer);
void requestPeer(PeerData *peer);
@ -59,35 +59,35 @@ signals:
public slots:
void resolveReplyTo();
void resolveDependencyItems();
void resolveWebPages();
void delayedRequestParticipantsCount();
private:
void gotReplyTo(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
struct ReplyToRequest {
ReplyToRequest() : req(0) {
void gotDependencyItem(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
struct DependencyRequest {
DependencyRequest() : req(0) {
}
mtpRequestId req;
QList<HistoryReply*> replies;
QList<HistoryItem*> dependentItems;
};
typedef QMap<MsgId, ReplyToRequest> ReplyToRequests;
ReplyToRequests _replyToRequests;
typedef QMap<ChannelData*, ReplyToRequests> ChannelReplyToRequests;
ChannelReplyToRequests _channelReplyToRequests;
SingleTimer _replyToTimer;
typedef QMap<MsgId, DependencyRequest> DependencyRequests;
DependencyRequests _dependencyRequests;
typedef QMap<ChannelData*, DependencyRequests> ChannelDependencyRequests;
ChannelDependencyRequests _channelDependencyRequests;
SingleTimer _dependencyTimer;
typedef QVector<MTPint> MessageIds;
MessageIds collectMessageIds(const ReplyToRequests &requests);
ReplyToRequests *replyToRequests(ChannelData *channel, bool onlyExisting = false);
MessageIds collectMessageIds(const DependencyRequests &requests);
DependencyRequests *dependencyRequests(ChannelData *channel, bool onlyExisting = false);
void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req);
void gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestId req);
bool gotPeerFullFailed(PeerData *peer, const RPCError &err);
typedef QMap<PeerData*, mtpRequestId> PeerRequests;
PeerRequests _fullPeerRequests;
void gotChat(PeerData *peer, const MTPmessages_Chats &result);
void gotUser(PeerData *peer, const MTPVector<MTPUser> &result);
void gotChats(const MTPmessages_Chats &result);

View file

@ -67,8 +67,8 @@ namespace {
SharedContactItems sharedContactItems;
GifItems gifItems;
typedef QMap<HistoryItem*, QMap<HistoryReply*, bool> > RepliesTo;
RepliesTo repliesTo;
typedef QMap<HistoryItem*, OrderedSet<HistoryItem*> > DependentItems;
DependentItems dependentItems;
Histories histories;
@ -1784,12 +1784,12 @@ namespace App {
}
}
historyItemDetached(item);
RepliesTo::iterator j = ::repliesTo.find(item);
if (j != ::repliesTo.cend()) {
for (QMap<HistoryReply*, bool>::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->replyToReplaced(item, 0);
DependentItems::iterator j = ::dependentItems.find(item);
if (j != ::dependentItems.cend()) {
for (OrderedSet<HistoryItem*>::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->dependencyItemRemoved(item);
}
::repliesTo.erase(j);
::dependentItems.erase(j);
}
if (App::main() && !App::quitting()) {
App::main()->itemRemoved(item);
@ -1797,7 +1797,7 @@ namespace App {
}
void historyClearMsgs() {
::repliesTo.clear();
::dependentItems.clear();
QVector<HistoryItem*> toDelete;
for (MsgsData::const_iterator i = msgsData.cbegin(), e = msgsData.cend(); i != e; ++i) {
@ -1869,16 +1869,16 @@ namespace App {
if (App::wnd()) App::wnd()->updateGlobalMenu();
}
void historyRegReply(HistoryReply *reply, HistoryItem *to) {
::repliesTo[to].insert(reply, true);
void historyRegDependency(HistoryItem *dependent, HistoryItem *dependency) {
::dependentItems[dependency].insert(dependent);
}
void historyUnregReply(HistoryReply *reply, HistoryItem *to) {
RepliesTo::iterator i = ::repliesTo.find(to);
if (i != ::repliesTo.cend()) {
i.value().remove(reply);
void historyUnregDependency(HistoryItem *dependent, HistoryItem *dependency) {
DependentItems::iterator i = ::dependentItems.find(dependency);
if (i != ::dependentItems.cend()) {
i.value().remove(dependent);
if (i.value().isEmpty()) {
::repliesTo.erase(i);
::dependentItems.erase(i);
}
}
}

View file

@ -152,8 +152,8 @@ namespace App {
void historyUnregItem(HistoryItem *item);
void historyClearMsgs();
void historyClearItems();
void historyRegReply(HistoryReply *reply, HistoryItem *to);
void historyUnregReply(HistoryReply *reply, HistoryItem *to);
void historyRegDependency(HistoryItem *dependent, HistoryItem *dependency);
void historyUnregDependency(HistoryItem *dependent, HistoryItem *dependency);
void historyRegRandom(uint64 randomId, const FullMsgId &itemId);
void historyUnregRandom(uint64 randomId);

View file

@ -171,6 +171,8 @@ enum {
ChoosePeerByDragTimeout = 1000, // 1 second mouse not moved to choose dialog when dragging a file
ReloadChannelMembersTimeout = 1000, // 1 second wait before reload members in channel after adding
PinnedMessageTextLimit = 16,
};
inline bool isNotificationsUser(uint64 id) {

View file

@ -1509,6 +1509,13 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo
//const MTPDmessageActionChannelMigrateFrom &d(action.c_messageActionChannelMigrateFrom());
//PeerData *chat = App::peerLoaded(peerFromChat(d.vchat_id));
} break;
case mtpc_messageActionPinMessage: {
if (d.has_reply_to_msg_id() && result && result->history()->peer->isMegagroup()) {
result->history()->peer->asChannel()->mgInfo->pinnedMsgId = d.vreply_to_msg_id.v;
if (App::main()) emit App::main()->peerUpdated(result->history()->peer);
}
} break;
}
}
} break;
@ -1540,7 +1547,7 @@ HistoryItem *History::createItemDocument(HistoryBlock *block, MsgId id, int32 fl
HistoryItem *History::createItemPhoto(HistoryBlock *block, MsgId id, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption) {
HistoryItem *result = 0;
if (flags & MTPDmessage::flag_reply_to_msg_id && replyTo > 0) {
if ((flags & MTPDmessage::flag_reply_to_msg_id) && replyTo > 0) {
result = new HistoryReply(this, block, id, flags, viaBotId, replyTo, date, from, photo, caption);
} else {
result = new HistoryMessage(this, block, id, flags, viaBotId, date, from, photo, caption);
@ -6891,7 +6898,7 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32
bool inText = false;
bool breakEverywhere = (fwd->_text.countHeight(trect.width()) > 2 * st::semiboldFont->height);
textstyleSet(&st::inFwdTextStyle);
fwd->_text.getState(lnk, inText, x - trect.left(), y - trect.top(), trect.right() - trect.left(), style::al_left, breakEverywhere);
fwd->_text.getState(lnk, inText, x - trect.left(), y - trect.top(), trect.width(), style::al_left, breakEverywhere);
textstyleRestore();
if (breakEverywhere) {
state = HistoryInForwardedCursorState;
@ -7035,7 +7042,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, const MTPDmess
, _maxReplyWidth(0)
, _replyToVia(0) {
if (!updateReplyTo() && App::api()) {
App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId);
App::api()->requestDependencyItem(this, history->peer->asChannel(), replyToMsgId);
}
}
@ -7047,7 +7054,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, i
, _maxReplyWidth(0)
, _replyToVia(0) {
if (!updateReplyTo() && App::api()) {
App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId);
App::api()->requestDependencyItem(this, history->peer->asChannel(), replyToMsgId);
}
}
@ -7059,7 +7066,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, i
, _maxReplyWidth(0)
, _replyToVia(0) {
if (!updateReplyTo() && App::api()) {
App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId);
App::api()->requestDependencyItem(this, history->peer->asChannel(), replyToMsgId);
}
replyToNameUpdated();
}
@ -7089,7 +7096,7 @@ bool HistoryReply::updateReplyTo(bool force) {
replyToMsg = App::histItemById(channelId(), replyToMsgId);
if (replyToMsg) {
App::historyRegReply(this, replyToMsg);
App::historyRegDependency(this, replyToMsg);
replyToText.setText(st::msgFont, replyToMsg->inReplyText(), _textDlgOptions);
replyToNameUpdated();
@ -7146,20 +7153,14 @@ HistoryItem *HistoryReply::replyToMessage() const {
return replyToMsg;
}
void HistoryReply::replyToReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
if (replyToMsg == oldItem) {
void HistoryReply::dependencyItemRemoved(HistoryItem *dependency) {
if (replyToMsg == dependency) {
delete _replyToVia;
_replyToVia = 0;
replyToMsg = newItem;
if (!newItem) {
replyToMsgId = 0;
initDimensions();
} else if (!replyToMsg->Is<HistoryMessageForwarded>()) {
if (UserData *bot = replyToMsg->viaBot()) {
_replyToVia = new HistoryMessageVia(0);
_replyToVia->create(peerToUser(bot->id));
}
}
replyToMsg = nullptr;
replyToMsgId = 0;
initDimensions();
}
}
@ -7301,7 +7302,7 @@ void HistoryReply::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x
int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
if (y >= trect.top() && y < trect.top() + h) {
if (replyToMsg && y >= trect.top() + st::msgReplyPadding.top() && y < trect.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() && x >= trect.left() && x < trect.right()) {
if (replyToMsg && y >= trect.top() + st::msgReplyPadding.top() && y < trect.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() && x >= trect.left() && x < trect.left() + trect.width()) {
lnk = replyToLnk;
}
return;
@ -7347,13 +7348,18 @@ void HistoryReply::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, i
HistoryReply::~HistoryReply() {
if (replyToMsg) {
App::historyUnregReply(this, replyToMsg);
App::historyUnregDependency(this, replyToMsg);
} else if (replyToMsgId && App::api()) {
App::api()->itemRemoved(this);
}
deleteAndMark(_replyToVia);
}
HistoryServicePinned::HistoryServicePinned(Interfaces *)
: msgId(0)
, msg(0) {
}
void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) {
QList<TextLinkPtr> links;
LangString text = lang(lng_message_empty);
@ -7486,6 +7492,15 @@ void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) {
}
} break;
case mtpc_messageActionPinMessage: {
if (updatePinnedText(&from, &text)) {
HistoryServicePinned *pinned = Get<HistoryServicePinned>();
t_assert(pinned != nullptr);
links.push_back(pinned->lnk);
}
} break;
default: from = QString(); break;
}
@ -7500,19 +7515,106 @@ void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) {
}
}
bool HistoryServiceMsg::updatePinned(bool force) {
HistoryServicePinned *pinned = Get<HistoryServicePinned>();
t_assert(pinned != nullptr);
if (!pinned->msgId || pinned->msg) return true;
if (!pinned->lnk) {
pinned->lnk = TextLinkPtr(new MessageLink(history()->peer->id, pinned->msgId));
}
pinned->msg = App::histItemById(channelId(), pinned->msgId);
if (pinned->msg) {
App::historyRegDependency(this, pinned->msg);
updatePinnedText();
} else if (force) {
pinned->msgId = 0;
updatePinnedText();
}
if (force) {
initDimensions();
Notify::historyItemResized(this);
}
return (pinned->msg || !pinned->msgId);
}
bool HistoryServiceMsg::updatePinnedText(const QString *pfrom, QString *ptext) {
bool result = false;
QString from, text;
if (pfrom) {
from = *pfrom;
} else {
from = textcmdLink(1, _from->name);
}
HistoryServicePinned *pinned = Get<HistoryServicePinned>();
if (pinned && pinned->msg) {
HistoryMedia *media = pinned->msg->getMedia();
QString mediaText;
switch (media ? media->type() : MediaTypeCount) {
case MediaTypePhoto: mediaText = lang(lng_action_pinned_media_photo); break;
case MediaTypeVideo: mediaText = lang(lng_action_pinned_media_video); break;
case MediaTypeContact: mediaText = lang(lng_action_pinned_media_contact); break;
case MediaTypeFile: mediaText = lang(lng_action_pinned_media_file); break;
case MediaTypeGif: mediaText = lang(lng_action_pinned_media_gif); break;
case MediaTypeSticker: mediaText = lang(lng_action_pinned_media_sticker); break;
case MediaTypeLocation: mediaText = lang(lng_action_pinned_media_location); break;
case MediaTypeMusicFile: mediaText = lang(lng_action_pinned_media_music); break;
case MediaTypeVoiceFile: mediaText = lang(lng_action_pinned_media_voice); break;
}
if (mediaText.isEmpty()) {
QString original = pinned->msg->originalText();
int32 cutat = 0, limit = PinnedMessageTextLimit, size = original.size();
for (; limit > 0;) {
--limit;
if (cutat >= size) break;
if (original.at(cutat).isLowSurrogate() && cutat + 1 < size && original.at(cutat + 1).isHighSurrogate()) {
cutat += 2;
} else {
++cutat;
}
}
if (!limit && cutat + 5 < size) {
original = original.mid(0, cutat) + qstr("..");
}
text = lng_action_pinned_message(lt_from, from, lt_text, textcmdLink(2, original));
} else {
text = lng_action_pinned_media(lt_from, from, lt_media, textcmdLink(2, mediaText));
}
result = true;
} else if (pinned && pinned->msgId) {
text = lng_action_pinned_media(lt_from, from, lt_media, textcmdLink(2, lang(lng_contacts_loading)));
result = true;
} else {
text = lng_action_pinned_media(lt_from, from, lt_media, lang(lng_deleted_message));
}
if (ptext) {
*ptext = text;
} else {
setServiceText(text);
}
return result;
}
HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDmessageService &msg) :
HistoryItem(history, block, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0)
, _text(st::msgMinWidth)
, _media(0)
{
, _media(0) {
if (msg.has_reply_to_msg_id()) {
UpdateInterfaces(HistoryServicePinned::Bit());
Get<HistoryServicePinned>()->msgId = msg.vreply_to_msg_id.v;
if (!updatePinned() && App::api()) {
App::api()->requestDependencyItem(this, history->peer->asChannel(), Get<HistoryServicePinned>()->msgId);
}
}
setMessageByAction(msg.vaction);
}
HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, int32 flags, HistoryMedia *media, int32 from) :
HistoryItem(history, block, msgId, flags, date, from)
, _text(st::msgServiceFont, msg, _historySrvOptions, st::dlgMinWidth)
, _media(media)
{
, _media(media) {
}
void HistoryServiceMsg::initDimensions() {

View file

@ -115,7 +115,6 @@ struct FakeDialogRow {
enum HistoryMediaType {
MediaTypePhoto,
MediaTypeVideo,
MediaTypeGeo,
MediaTypeContact,
MediaTypeFile,
MediaTypeGif,
@ -919,6 +918,15 @@ public:
virtual int32 resize(int32 width) = 0; // return new height
virtual void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const = 0;
virtual void dependencyItemRemoved(HistoryItem *dependency) {
}
virtual bool updateDependencyItem() {
return true;
}
virtual MsgId dependencyMsgId() const {
return 0;
}
virtual UserData *viaBot() const {
return 0;
}
@ -2229,15 +2237,19 @@ public:
void initDimensions();
bool updateReplyTo(bool force = false);
void replyToNameUpdated() const;
bool updateDependencyItem() override {
return updateReplyTo(true);
}
MsgId dependencyMsgId() const override {
return replyToId();
}
int32 replyToWidth() const;
TextLinkPtr replyToLink() const;
MsgId replyToId() const;
HistoryItem *replyToMessage() const;
void replyToReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void dependencyItemRemoved(HistoryItem *dependency) override;
void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const;
void drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selected, bool likeService = false) const;
@ -2265,6 +2277,9 @@ public:
protected:
bool updateReplyTo(bool force = false);
void replyToNameUpdated() const;
MsgId replyToMsgId;
HistoryItem *replyToMsg;
TextLinkPtr replyToLnk;
@ -2296,6 +2311,14 @@ inline int32 newForwardedFlags(PeerData *p, int32 from, HistoryMessage *fwd) {
return result;
}
struct HistoryServicePinned : public BasicInterface<HistoryServicePinned> {
HistoryServicePinned(Interfaces *);
MsgId msgId;
HistoryItem *msg;
TextLinkPtr lnk;
};
class HistoryServiceMsg : public HistoryItem {
public:
@ -2304,6 +2327,16 @@ public:
void initDimensions();
bool updateDependencyItem() override {
return updatePinned(true);
}
MsgId dependencyMsgId() const override {
if (const HistoryServicePinned *pinned = Get<HistoryServicePinned>()) {
return pinned->msgId;
}
return 0;
}
void countPositionAndSize(int32 &left, int32 &width) const;
void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const;
@ -2344,6 +2377,8 @@ public:
protected:
void setMessageByAction(const MTPmessageAction &action);
bool updatePinned(bool force = false);
bool updatePinnedText(const QString *pfrom = nullptr, QString *ptext = nullptr);
Text _text;
HistoryMedia *_media;

View file

@ -3500,7 +3500,7 @@ void HistoryWidget::applyDraft(bool parseLinks) {
if (_editMsgId || _replyToId) {
updateReplyEditTexts();
if (!_replyEditMsg && App::api()) {
App::api()->requestReplyTo(0, _peer->asChannel(), _editMsgId ? _editMsgId : _replyToId);
App::api()->requestDependencyItem(0, _peer->asChannel(), _editMsgId ? _editMsgId : _replyToId);
}
}
}

View file

@ -2056,7 +2056,7 @@ ApiWrap *MainWidget::api() {
return _api;
}
void MainWidget::updateReplyTo() {
void MainWidget::updateDependencyItem() {
history.updateReplyEditTexts(true);
}

View file

@ -373,7 +373,7 @@ public:
ImagePtr newBackgroundThumb();
ApiWrap *api();
void updateReplyTo();
void updateDependencyItem();
void updateBotKeyboard(History *h);
void pushReplyReturn(HistoryItem *item);

View file

@ -1385,8 +1385,9 @@ void _serialize_messageService(MTPStringLogger &to, int32 stage, int32 lev, Type
case 7: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 8: to.add(" from_id: "); ++stages.back(); if (flag & MTPDmessageService::flag_from_id) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 8 IN FIELD flags ]"); } break;
case 9: to.add(" to_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 10: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 11: to.add(" action: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 10: to.add(" reply_to_msg_id: "); ++stages.back(); if (flag & MTPDmessageService::flag_reply_to_msg_id) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 3 IN FIELD flags ]"); } break;
case 11: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 12: to.add(" action: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
@ -1613,6 +1614,10 @@ void _serialize_messageActionChannelMigrateFrom(MTPStringLogger &to, int32 stage
}
}
void _serialize_messageActionPinMessage(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) {
to.add("{ messageActionPinMessage }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_dialog(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) {
if (stage) {
to.add(",\n").addSpaces(lev);
@ -6958,8 +6963,10 @@ void _serialize_channels_updatePinnedMessage(MTPStringLogger &to, int32 stage, i
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" channel: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" silent: "); ++stages.back(); if (flag & MTPchannels_updatePinnedMessage::flag_silent) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" channel: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
@ -7683,6 +7690,7 @@ namespace {
_serializers.insert(mtpc_messageActionChannelCreate, _serialize_messageActionChannelCreate);
_serializers.insert(mtpc_messageActionChatMigrateTo, _serialize_messageActionChatMigrateTo);
_serializers.insert(mtpc_messageActionChannelMigrateFrom, _serialize_messageActionChannelMigrateFrom);
_serializers.insert(mtpc_messageActionPinMessage, _serialize_messageActionPinMessage);
_serializers.insert(mtpc_dialog, _serialize_dialog);
_serializers.insert(mtpc_dialogChannel, _serialize_dialogChannel);
_serializers.insert(mtpc_photoEmpty, _serialize_photoEmpty);

View file

@ -146,7 +146,7 @@ enum {
mtpc_chatPhoto = 0x6153276a,
mtpc_messageEmpty = 0x83e5de54,
mtpc_message = 0xc09be45f,
mtpc_messageService = 0xc06b9607,
mtpc_messageService = 0x9e19a1f6,
mtpc_messageMediaEmpty = 0x3ded6320,
mtpc_messageMediaPhoto = 0x3d8ce53d,
mtpc_messageMediaGeo = 0x56e0d474,
@ -166,6 +166,7 @@ enum {
mtpc_messageActionChannelCreate = 0x95d2ac92,
mtpc_messageActionChatMigrateTo = 0x51bdb021,
mtpc_messageActionChannelMigrateFrom = 0xb055eaee,
mtpc_messageActionPinMessage = 0x94bd38ed,
mtpc_dialog = 0xc1dd804a,
mtpc_dialogChannel = 0x5b8496b2,
mtpc_photoEmpty = 0x2331b22d,
@ -613,7 +614,7 @@ enum {
mtpc_channels_toggleSignatures = 0x1f69b606,
mtpc_channels_getMessageEditData = 0x27ea3a28,
mtpc_channels_editMessage = 0xdcda80ed,
mtpc_channels_updatePinnedMessage = 0x84a41867
mtpc_channels_updatePinnedMessage = 0xa72ded52
};
// Type forward declarations
@ -3535,7 +3536,7 @@ private:
friend MTPmessage MTP_messageEmpty(MTPint _id);
friend MTPmessage MTP_message(MTPint _flags, MTPint _id, MTPint _from_id, const MTPPeer &_to_id, const MTPMessageFwdHeader &_fwd_from, MTPint _via_bot_id, MTPint _reply_to_msg_id, MTPint _date, const MTPstring &_message, const MTPMessageMedia &_media, const MTPReplyMarkup &_reply_markup, const MTPVector<MTPMessageEntity> &_entities, MTPint _views, MTPint _edit_date);
friend MTPmessage MTP_messageService(MTPint _flags, MTPint _id, MTPint _from_id, const MTPPeer &_to_id, MTPint _date, const MTPMessageAction &_action);
friend MTPmessage MTP_messageService(MTPint _flags, MTPint _id, MTPint _from_id, const MTPPeer &_to_id, MTPint _reply_to_msg_id, MTPint _date, const MTPMessageAction &_action);
mtpTypeId _type;
};
@ -3796,6 +3797,7 @@ private:
friend MTPmessageAction MTP_messageActionChannelCreate(const MTPstring &_title);
friend MTPmessageAction MTP_messageActionChatMigrateTo(MTPint _channel_id);
friend MTPmessageAction MTP_messageActionChannelMigrateFrom(const MTPstring &_title, MTPint _chat_id);
friend MTPmessageAction MTP_messageActionPinMessage();
mtpTypeId _type;
};
@ -10199,13 +10201,14 @@ class MTPDmessageService : public mtpDataImpl<MTPDmessageService> {
public:
MTPDmessageService() {
}
MTPDmessageService(MTPint _flags, MTPint _id, MTPint _from_id, const MTPPeer &_to_id, MTPint _date, const MTPMessageAction &_action) : vflags(_flags), vid(_id), vfrom_id(_from_id), vto_id(_to_id), vdate(_date), vaction(_action) {
MTPDmessageService(MTPint _flags, MTPint _id, MTPint _from_id, const MTPPeer &_to_id, MTPint _reply_to_msg_id, MTPint _date, const MTPMessageAction &_action) : vflags(_flags), vid(_id), vfrom_id(_from_id), vto_id(_to_id), vreply_to_msg_id(_reply_to_msg_id), vdate(_date), vaction(_action) {
}
MTPint vflags;
MTPint vid;
MTPint vfrom_id;
MTPPeer vto_id;
MTPint vreply_to_msg_id;
MTPint vdate;
MTPMessageAction vaction;
@ -10217,6 +10220,7 @@ public:
flag_silent = (1 << 13),
flag_post = (1 << 14),
flag_from_id = (1 << 8),
flag_reply_to_msg_id = (1 << 3),
};
bool is_unread() const { return vflags.v & flag_unread; }
@ -10226,6 +10230,7 @@ public:
bool is_silent() const { return vflags.v & flag_silent; }
bool is_post() const { return vflags.v & flag_post; }
bool has_from_id() const { return vflags.v & flag_from_id; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
};
class MTPDmessageMediaPhoto : public mtpDataImpl<MTPDmessageMediaPhoto> {
@ -20603,6 +20608,7 @@ public:
class MTPchannels_updatePinnedMessage { // RPC method 'channels.updatePinnedMessage'
public:
MTPint vflags;
MTPInputChannel vchannel;
MTPint vid;
@ -20611,20 +20617,28 @@ public:
MTPchannels_updatePinnedMessage(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_channels_updatePinnedMessage) {
read(from, end, cons);
}
MTPchannels_updatePinnedMessage(const MTPInputChannel &_channel, MTPint _id) : vchannel(_channel), vid(_id) {
MTPchannels_updatePinnedMessage(MTPint _flags, const MTPInputChannel &_channel, MTPint _id) : vflags(_flags), vchannel(_channel), vid(_id) {
}
enum {
flag_silent = (1 << 0),
};
bool is_silent() const { return vflags.v & flag_silent; }
uint32 innerLength() const {
return vchannel.innerLength() + vid.innerLength();
return vflags.innerLength() + vchannel.innerLength() + vid.innerLength();
}
mtpTypeId type() const {
return mtpc_channels_updatePinnedMessage;
}
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_channels_updatePinnedMessage) {
vflags.read(from, end);
vchannel.read(from, end);
vid.read(from, end);
}
void write(mtpBuffer &to) const {
vflags.write(to);
vchannel.write(to);
vid.write(to);
}
@ -20639,7 +20653,7 @@ public:
}
MTPchannels_UpdatePinnedMessage(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPchannels_updatePinnedMessage>(from, end, cons) {
}
MTPchannels_UpdatePinnedMessage(const MTPInputChannel &_channel, MTPint _id) : MTPBoxed<MTPchannels_updatePinnedMessage>(MTPchannels_updatePinnedMessage(_channel, _id)) {
MTPchannels_UpdatePinnedMessage(MTPint _flags, const MTPInputChannel &_channel, MTPint _id) : MTPBoxed<MTPchannels_updatePinnedMessage>(MTPchannels_updatePinnedMessage(_flags, _channel, _id)) {
}
};
@ -23401,7 +23415,7 @@ inline uint32 MTPmessage::innerLength() const {
}
case mtpc_messageService: {
const MTPDmessageService &v(c_messageService());
return v.vflags.innerLength() + v.vid.innerLength() + (v.has_from_id() ? v.vfrom_id.innerLength() : 0) + v.vto_id.innerLength() + v.vdate.innerLength() + v.vaction.innerLength();
return v.vflags.innerLength() + v.vid.innerLength() + (v.has_from_id() ? v.vfrom_id.innerLength() : 0) + v.vto_id.innerLength() + (v.has_reply_to_msg_id() ? v.vreply_to_msg_id.innerLength() : 0) + v.vdate.innerLength() + v.vaction.innerLength();
}
}
return 0;
@ -23443,6 +23457,7 @@ inline void MTPmessage::read(const mtpPrime *&from, const mtpPrime *end, mtpType
v.vid.read(from, end);
if (v.has_from_id()) { v.vfrom_id.read(from, end); } else { v.vfrom_id = MTPint(); }
v.vto_id.read(from, end);
if (v.has_reply_to_msg_id()) { v.vreply_to_msg_id.read(from, end); } else { v.vreply_to_msg_id = MTPint(); }
v.vdate.read(from, end);
v.vaction.read(from, end);
} break;
@ -23478,6 +23493,7 @@ inline void MTPmessage::write(mtpBuffer &to) const {
v.vid.write(to);
if (v.has_from_id()) v.vfrom_id.write(to);
v.vto_id.write(to);
if (v.has_reply_to_msg_id()) v.vreply_to_msg_id.write(to);
v.vdate.write(to);
v.vaction.write(to);
} break;
@ -23503,8 +23519,8 @@ inline MTPmessage MTP_messageEmpty(MTPint _id) {
inline MTPmessage MTP_message(MTPint _flags, MTPint _id, MTPint _from_id, const MTPPeer &_to_id, const MTPMessageFwdHeader &_fwd_from, MTPint _via_bot_id, MTPint _reply_to_msg_id, MTPint _date, const MTPstring &_message, const MTPMessageMedia &_media, const MTPReplyMarkup &_reply_markup, const MTPVector<MTPMessageEntity> &_entities, MTPint _views, MTPint _edit_date) {
return MTPmessage(new MTPDmessage(_flags, _id, _from_id, _to_id, _fwd_from, _via_bot_id, _reply_to_msg_id, _date, _message, _media, _reply_markup, _entities, _views, _edit_date));
}
inline MTPmessage MTP_messageService(MTPint _flags, MTPint _id, MTPint _from_id, const MTPPeer &_to_id, MTPint _date, const MTPMessageAction &_action) {
return MTPmessage(new MTPDmessageService(_flags, _id, _from_id, _to_id, _date, _action));
inline MTPmessage MTP_messageService(MTPint _flags, MTPint _id, MTPint _from_id, const MTPPeer &_to_id, MTPint _reply_to_msg_id, MTPint _date, const MTPMessageAction &_action) {
return MTPmessage(new MTPDmessageService(_flags, _id, _from_id, _to_id, _reply_to_msg_id, _date, _action));
}
inline uint32 MTPmessageMedia::innerLength() const {
@ -23771,6 +23787,7 @@ inline void MTPmessageAction::read(const mtpPrime *&from, const mtpPrime *end, m
v.vtitle.read(from, end);
v.vchat_id.read(from, end);
} break;
case mtpc_messageActionPinMessage: _type = cons; break;
default: throw mtpErrorUnexpected(cons, "MTPmessageAction");
}
}
@ -23829,6 +23846,7 @@ inline MTPmessageAction::MTPmessageAction(mtpTypeId type) : mtpDataOwner(0), _ty
case mtpc_messageActionChannelCreate: setData(new MTPDmessageActionChannelCreate()); break;
case mtpc_messageActionChatMigrateTo: setData(new MTPDmessageActionChatMigrateTo()); break;
case mtpc_messageActionChannelMigrateFrom: setData(new MTPDmessageActionChannelMigrateFrom()); break;
case mtpc_messageActionPinMessage: break;
default: throw mtpErrorBadTypeId(type, "MTPmessageAction");
}
}
@ -23883,6 +23901,9 @@ inline MTPmessageAction MTP_messageActionChatMigrateTo(MTPint _channel_id) {
inline MTPmessageAction MTP_messageActionChannelMigrateFrom(const MTPstring &_title, MTPint _chat_id) {
return MTPmessageAction(new MTPDmessageActionChannelMigrateFrom(_title, _chat_id));
}
inline MTPmessageAction MTP_messageActionPinMessage() {
return MTPmessageAction(mtpc_messageActionPinMessage);
}
inline uint32 MTPdialog::innerLength() const {
switch (_type) {

View file

@ -227,7 +227,7 @@ chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
messageEmpty#83e5de54 id:int = Message;
message#c09be45f flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int = Message;
messageService#c06b9607 flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer date:int action:MessageAction = Message;
messageService#9e19a1f6 flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
messageMediaPhoto#3d8ce53d photo:Photo caption:string = MessageMedia;
@ -249,6 +249,7 @@ messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction;
messageActionChannelCreate#95d2ac92 title:string = MessageAction;
messageActionChatMigrateTo#51bdb021 channel_id:int = MessageAction;
messageActionChannelMigrateFrom#b055eaee title:string chat_id:int = MessageAction;
messageActionPinMessage#94bd38ed = MessageAction;
dialog#c1dd804a peer:Peer top_message:int read_inbox_max_id:int unread_count:int notify_settings:PeerNotifySettings = Dialog;
dialogChannel#5b8496b2 peer:Peer top_message:int top_important_message:int read_inbox_max_id:int unread_count:int unread_important_count:int notify_settings:PeerNotifySettings pts:int = Dialog;
@ -821,4 +822,4 @@ channels.exportMessageLink#c846d22d channel:InputChannel id:int = ExportedMessag
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.getMessageEditData#27ea3a28 channel:InputChannel id:int = channels.MessageEditData;
channels.editMessage#dcda80ed flags:# no_webpage:flags.1?true channel:InputChannel id:int message:string entities:flags.3?Vector<MessageEntity> = Updates;
channels.updatePinnedMessage#84a41867 channel:InputChannel id:int = Updates;
channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates;