diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 2868a71ea5..07d99da2dd 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -130,6 +130,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_edit_message_text" = "New message text.."; "lng_deleted" = "Unknown"; "lng_deleted_message" = "Deleted message"; +"lng_pinned_message" = "Pinned message"; +"lng_pinned_unpin_sure" = "Would you like to unpin this message?"; +"lng_pinned_pin_sure" = "Would you like to pin this message?"; +"lng_pinned_pin" = "Pin"; +"lng_pinned_unpin" = "Unpin"; +"lng_pinned_notify" = "Notify all members"; "lng_intro" = "Welcome to the official [a href=\"https://telegram.org/\"]Telegram[/a] desktop app.\nIt's [b]fast[/b] and [b]secure[/b]."; "lng_start_msgs" = "START MESSAGING"; @@ -770,6 +776,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_context_forward_msg" = "Forward Message"; "lng_context_delete_msg" = "Delete Message"; "lng_context_select_msg" = "Select Message"; +"lng_context_pin_msg" = "Pin Message"; +"lng_context_unpin_msg" = "Unpin Message"; "lng_context_cancel_upload" = "Cancel Upload"; "lng_context_copy_selected" = "Copy Selected Text"; "lng_context_forward_selected" = "Forward Selected"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 8d9dd68e6a..df9a6e6156 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -67,6 +67,13 @@ layerBg: black; overBg: #edf2f5; +labelDefFlat: flatLabel { + font: font(fsize); + minWidth: 100px; + width: 0px; + align: align(left); +} + boxBg: white; boxVerticalMargin: 10px; boxWidth: 320px; @@ -75,6 +82,8 @@ boxPadding: margins(26px, 30px, 34px, 8px); boxMaxListHeight: 600px; boxFontSize: 14px; boxTextFont: font(boxFontSize); +boxLittleSkip: 10px; +boxMediumSkip: 20px; boxTitleFg: #444444; boxTitleFont: font(boxFontSize bold); @@ -126,6 +135,10 @@ redBoxLinkButton: linkButton(defaultBoxLinkButton) { overColor: #d15948; downColor: #db6352; } +boxLabel: flatLabel(labelDefFlat) { + font: font(boxFontSize); + align: align(topleft); +} defaultInputArea: InputArea { textFg: black; @@ -611,13 +624,6 @@ scrollCountries: flatScroll(scrollDef) { lnkText: #0f7dc7; -labelDefFlat: flatLabel { - font: font(fsize); - minWidth: 100px; - width: 0px; - align: align(left); -} - introBtnTop: 288px; introSkip: 45px; introFinishSkip: 15px; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index c6108e8e0e..3f1ded1739 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -29,89 +29,65 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" -ApiWrap::ApiWrap(QObject *parent) : QObject(parent) { +ApiWrap::ApiWrap(QObject *parent) : QObject(parent) +, _messageDataResolveDelayed(new SingleDelayedCall(this, "resolveMessageDatas")) { App::initBackground(); - connect(&_dependencyTimer, SIGNAL(timeout()), this, SLOT(resolveDependencyItems())); connect(&_webPagesTimer, SIGNAL(timeout()), this, SLOT(resolveWebPages())); } void ApiWrap::init() { } -void ApiWrap::itemRemoved(HistoryItem *item) { - if (MsgId dependencyMsgId = item->dependencyMsgId()) { - ChannelData *channel = item->history()->peer->asChannel(); - DependencyRequests *requests(dependencyRequests(channel, true)); - if (requests) { - DependencyRequests::iterator i = requests->find(dependencyMsgId); - if (i != requests->cend()) { - for (QList::iterator j = i->dependentItems.begin(); j != i->dependentItems.end();) { - if ((*j) == item) { - j = i->dependentItems.erase(j); - } else { - ++j; - } - } - if (i->dependentItems.isEmpty()) { - requests->erase(i); - } - } - if (channel && requests->isEmpty()) { - _channelDependencyRequests.remove(channel); - } - } - } +void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback *callback) { + MessageDataRequest::CallbackPtr pcallback(callback); + MessageDataRequest &req(channel ? _channelMessageDataRequests[channel][msgId] : _messageDataRequests[msgId]); + req.callbacks.append(pcallback); + if (!req.req) _messageDataResolveDelayed->call(); } -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 DependencyRequests &requests) { +ApiWrap::MessageIds ApiWrap::collectMessageIds(const MessageDataRequests &requests) { MessageIds result; result.reserve(requests.size()); - for (DependencyRequests::const_iterator i = requests.cbegin(), e = requests.cend(); i != e; ++i) { + for (MessageDataRequests::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::DependencyRequests *ApiWrap::dependencyRequests(ChannelData *channel, bool onlyExisting) { +ApiWrap::MessageDataRequests *ApiWrap::messageDataRequests(ChannelData *channel, bool onlyExisting) { if (channel) { - ChannelDependencyRequests::iterator i = _channelDependencyRequests.find(channel); - if (i == _channelDependencyRequests.cend()) { + ChannelMessageDataRequests::iterator i = _channelMessageDataRequests.find(channel); + if (i == _channelMessageDataRequests.cend()) { if (onlyExisting) return 0; - i = _channelDependencyRequests.insert(channel, DependencyRequests()); + i = _channelMessageDataRequests.insert(channel, MessageDataRequests()); } return &i.value(); } - return &_dependencyRequests; + return &_messageDataRequests; } -void ApiWrap::resolveDependencyItems() { - if (_dependencyRequests.isEmpty() && _channelDependencyRequests.isEmpty()) return; +void ApiWrap::resolveMessageDatas() { + if (_messageDataRequests.isEmpty() && _channelMessageDataRequests.isEmpty()) return; - MessageIds ids = collectMessageIds(_dependencyRequests); + MessageIds ids = collectMessageIds(_messageDataRequests); if (!ids.isEmpty()) { - mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector(ids)), rpcDone(&ApiWrap::gotDependencyItem, (ChannelData*)0), RPCFailHandlerPtr(), 0, 5); - for (DependencyRequests::iterator i = _dependencyRequests.begin(); i != _dependencyRequests.cend(); ++i) { + mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector(ids)), rpcDone(&ApiWrap::gotMessageDatas, (ChannelData*)nullptr), RPCFailHandlerPtr(), 0, 5); + for (MessageDataRequests::iterator i = _messageDataRequests.begin(); i != _messageDataRequests.cend(); ++i) { if (i.value().req > 0) continue; i.value().req = req; } } - for (ChannelDependencyRequests::iterator j = _channelDependencyRequests.begin(); j != _channelDependencyRequests.cend();) { + for (ChannelMessageDataRequests::iterator j = _channelMessageDataRequests.begin(); j != _channelMessageDataRequests.cend();) { if (j->isEmpty()) { - j = _channelDependencyRequests.erase(j); + j = _channelMessageDataRequests.erase(j); continue; } MessageIds ids = collectMessageIds(j.value()); if (!ids.isEmpty()) { - mtpRequestId req = MTP::send(MTPchannels_GetMessages(j.key()->inputChannel, MTP_vector(ids)), rpcDone(&ApiWrap::gotDependencyItem, j.key()), RPCFailHandlerPtr(), 0, 5); - for (DependencyRequests::iterator i = j->begin(); i != j->cend(); ++i) { + mtpRequestId req = MTP::send(MTPchannels_GetMessages(j.key()->inputChannel, MTP_vector(ids)), rpcDone(&ApiWrap::gotMessageDatas, j.key()), RPCFailHandlerPtr(), 0, 5); + for (MessageDataRequests::iterator i = j->begin(); i != j->cend(); ++i) { if (i.value().req > 0) continue; i.value().req = req; } @@ -120,7 +96,7 @@ void ApiWrap::resolveDependencyItems() { } } -void ApiWrap::gotDependencyItem(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) { +void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) { switch (msgs.type()) { case mtpc_messages_messages: { const MTPDmessages_messages &d(msgs.c_messages_messages()); @@ -152,16 +128,12 @@ void ApiWrap::gotDependencyItem(ChannelData *channel, const MTPmessages_Messages App::feedMsgs(d.vmessages, NewMessageExisting); } break; } - DependencyRequests *requests(dependencyRequests(channel, true)); + MessageDataRequests *requests(messageDataRequests(channel, true)); if (requests) { - for (DependencyRequests::iterator i = requests->begin(); i != requests->cend();) { + for (MessageDataRequests::iterator i = requests->begin(); i != requests->cend();) { if (i.value().req == req) { - for (QList::const_iterator j = i.value().dependentItems.cbegin(), e = i.value().dependentItems.cend(); j != e; ++j) { - if (*j) { - (*j)->updateDependencyItem(); - } else if (App::main()) { - App::main()->updateDependencyItem(); - } + for (MessageDataRequest::Callbacks::const_iterator j = i.value().callbacks.cbegin(), e = i.value().callbacks.cend(); j != e; ++j) { + (*j)->call(channel, i.key()); } i = requests->erase(i); } else { @@ -169,7 +141,7 @@ void ApiWrap::gotDependencyItem(ChannelData *channel, const MTPmessages_Messages } } if (channel && requests->isEmpty()) { - _channelDependencyRequests.remove(channel); + _channelMessageDataRequests.remove(channel); } } } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index d15fb88f71..3ea5eca0fa 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -28,9 +28,8 @@ public: ApiWrap(QObject *parent); void init(); - void itemRemoved(HistoryItem *item); - - void requestDependencyItem(HistoryItem *dependent, ChannelData *channel, MsgId id); + typedef SharedCallback2 RequestMessageDataCallback; + void requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback *callback); void requestFullPeer(PeerData *peer); void requestPeer(PeerData *peer); @@ -59,28 +58,30 @@ signals: public slots: - void resolveDependencyItems(); + void resolveMessageDatas(); void resolveWebPages(); void delayedRequestParticipantsCount(); private: - void gotDependencyItem(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req); - struct DependencyRequest { - DependencyRequest() : req(0) { + void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req); + struct MessageDataRequest { + MessageDataRequest() : req(0) { } + typedef SharedCallback2::Ptr CallbackPtr; + typedef QList Callbacks; mtpRequestId req; - QList dependentItems; + Callbacks callbacks; }; - typedef QMap DependencyRequests; - DependencyRequests _dependencyRequests; - typedef QMap ChannelDependencyRequests; - ChannelDependencyRequests _channelDependencyRequests; - SingleTimer _dependencyTimer; + typedef QMap MessageDataRequests; + MessageDataRequests _messageDataRequests; + typedef QMap ChannelMessageDataRequests; + ChannelMessageDataRequests _channelMessageDataRequests; + SingleDelayedCall *_messageDataResolveDelayed; typedef QVector MessageIds; - MessageIds collectMessageIds(const DependencyRequests &requests); - DependencyRequests *dependencyRequests(ChannelData *channel, bool onlyExisting = false); + MessageIds collectMessageIds(const MessageDataRequests &requests); + MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false); void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req); void gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestId req); diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index fd6524e8e7..fc0eb42370 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -378,3 +378,59 @@ void ConvertToSupergroupBox::resizeEvent(QResizeEvent *e) { _convert.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _convert.height()); _cancel.moveToRight(st::boxButtonPadding.right() + _convert.width() + st::boxButtonPadding.left(), _convert.y()); } + +PinMessageBox::PinMessageBox(ChannelData *channel, MsgId msgId) : AbstractBox(st::boxWidth) +, _channel(channel) +, _msgId(msgId) +, _text(this, lang(lng_pinned_pin_sure), st::boxLabel) +, _notify(this, lang(lng_pinned_notify), true) +, _pin(this, lang(lng_pinned_pin), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _requestId(0) { + _text.resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()); + setMaxHeight(st::boxPadding.top() + _text.height() + st::boxMediumSkip + _notify.height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _pin.height() + st::boxButtonPadding.bottom()); + + connect(&_pin, SIGNAL(clicked()), this, SLOT(onPin())); + connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); +} + +void PinMessageBox::resizeEvent(QResizeEvent *e) { + _text.moveToLeft(st::boxPadding.left(), st::boxPadding.top()); + _notify.moveToLeft(st::boxPadding.left(), _text.y() + _text.height() + st::boxMediumSkip); + _pin.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _pin.height()); + _cancel.moveToRight(st::boxButtonPadding.right() + _pin.width() + st::boxButtonPadding.left(), _pin.y()); +} + +void PinMessageBox::onPin() { + if (_requestId) return; + + int32 flags = _notify.checked() ? 0 : MTPchannels_UpdatePinnedMessage::flag_silent; + _requestId = MTP::send(MTPchannels_UpdatePinnedMessage(MTP_int(flags), _channel->inputChannel, MTP_int(_msgId)), rpcDone(&PinMessageBox::pinDone), rpcFail(&PinMessageBox::pinFail)); +} + +void PinMessageBox::showAll() { + _text.show(); + _notify.show(); + _pin.show(); + _cancel.show(); +} + +void PinMessageBox::hideAll() { + _text.hide(); + _notify.hide(); + _pin.hide(); + _cancel.hide(); +} + +void PinMessageBox::pinDone(const MTPUpdates &updates) { + if (App::main()) { + App::main()->sentUpdatesReceived(updates); + } + Ui::hideLayer(); +} + +bool PinMessageBox::pinFail(const RPCError &error) { + if (mtpIsFlood(error)) return false; + Ui::hideLayer(); + return true; +} diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index 05ed14212c..b2b1d2e989 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -163,4 +163,39 @@ private: int32 _textWidth, _textHeight; BoxButton _convert, _cancel; +}; + +class PinMessageBox : public AbstractBox, public RPCSender { + Q_OBJECT + +public: + + PinMessageBox(ChannelData *channel, MsgId msgId); + + void resizeEvent(QResizeEvent *e); + +public slots: + + void onPin(); + +protected: + + void showAll(); + void hideAll(); + +private: + + void pinDone(const MTPUpdates &updates); + bool pinFail(const RPCError &error); + + ChannelData *_channel; + MsgId _msgId; + + FlatLabel _text; + Checkbox _notify; + + BoxButton _pin, _cancel; + + mtpRequestId _requestId; + }; \ No newline at end of file diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 37e5fdaf30..eccc5255c3 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -371,6 +371,8 @@ struct GlobalDataStruct { int32 PushChatLimit = 2; int32 SavedGifsLimit = 200; int32 EditTimeLimit = 172800; + + Global::HiddenPinnedMessagesMap HiddenPinnedMessages; }; GlobalDataStruct *GlobalData = 0; @@ -413,4 +415,6 @@ namespace Global { DefineVar(Global, int32, SavedGifsLimit); DefineVar(Global, int32, EditTimeLimit); + DefineVar(Global, HiddenPinnedMessagesMap, HiddenPinnedMessages); + }; diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 11ce5c75b7..e572f45c1d 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -159,6 +159,9 @@ namespace Global { DeclareVar(int32, SavedGifsLimit); DeclareVar(int32, EditTimeLimit); + typedef QMap HiddenPinnedMessagesMap; + DeclareVar(HiddenPinnedMessagesMap, HiddenPinnedMessages); + }; namespace Adaptive { diff --git a/Telegram/SourceFiles/gui/flatlabel.cpp b/Telegram/SourceFiles/gui/flatlabel.cpp index 429b96b58a..8255ff1c23 100644 --- a/Telegram/SourceFiles/gui/flatlabel.cpp +++ b/Telegram/SourceFiles/gui/flatlabel.cpp @@ -53,6 +53,13 @@ void FlatLabel::setRichText(const QString &text) { setMouseTracking(_text.hasLinks()); } +void FlatLabel::resizeToWidth(int32 width) { + textstyleSet(&_tst); + int32 w = width, h = _text.countHeight(w); + textstyleRestore(); + resize(w, h); +} + void FlatLabel::setLink(uint16 lnkIndex, const TextLinkPtr &lnk) { _text.setLink(lnkIndex, lnk); } diff --git a/Telegram/SourceFiles/gui/flatlabel.h b/Telegram/SourceFiles/gui/flatlabel.h index 43e9ebd63d..b06e3bff5e 100644 --- a/Telegram/SourceFiles/gui/flatlabel.h +++ b/Telegram/SourceFiles/gui/flatlabel.h @@ -40,6 +40,8 @@ public: void setText(const QString &text); void setRichText(const QString &text); + void resizeToWidth(int32 width); + void setLink(uint16 lnkIndex, const TextLinkPtr &lnk); private: diff --git a/Telegram/SourceFiles/gui/twidget.h b/Telegram/SourceFiles/gui/twidget.h index ad839e7016..4667e43a9e 100644 --- a/Telegram/SourceFiles/gui/twidget.h +++ b/Telegram/SourceFiles/gui/twidget.h @@ -204,3 +204,28 @@ private: const style::color &_color; }; + +class SingleDelayedCall : public QObject { + Q_OBJECT + +public: + SingleDelayedCall(QObject *parent, const char *member) : QObject(parent), _pending(false), _member(member) { + } + void call() { + if (!_pending) { + _pending = true; + QMetaObject::invokeMethod(this, "makeDelayedCall", Qt::QueuedConnection); + } + } + +private slots: + void makeDelayedCall() { + _pending = false; + QMetaObject::invokeMethod(parent(), _member); + } + +private: + bool _pending; + const char *_member; + +}; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 16d36a62c9..25442fe91b 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -2909,6 +2909,12 @@ void HistoryBlock::removeItem(HistoryItem *item) { } } +void HistoryDependentItemCallback::call(ChannelData *channel, MsgId msgId) const { + if (HistoryItem *item = App::histItemById(_dependent)) { + item->updateDependencyItem(); + } +} + HistoryItem::HistoryItem(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime msgDate, int32 from) : y(0) , id(msgId) , date(msgDate) @@ -7042,7 +7048,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, const MTPDmess , _maxReplyWidth(0) , _replyToVia(0) { if (!updateReplyTo() && App::api()) { - App::api()->requestDependencyItem(this, history->peer->asChannel(), replyToMsgId); + App::api()->requestMessageData(history->peer->asChannel(), replyToMsgId, new HistoryDependentItemCallback(fullId())); } } @@ -7054,7 +7060,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, i , _maxReplyWidth(0) , _replyToVia(0) { if (!updateReplyTo() && App::api()) { - App::api()->requestDependencyItem(this, history->peer->asChannel(), replyToMsgId); + App::api()->requestMessageData(history->peer->asChannel(), replyToMsgId, new HistoryDependentItemCallback(fullId())); } } @@ -7066,7 +7072,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, i , _maxReplyWidth(0) , _replyToVia(0) { if (!updateReplyTo() && App::api()) { - App::api()->requestDependencyItem(this, history->peer->asChannel(), replyToMsgId); + App::api()->requestMessageData(history->peer->asChannel(), replyToMsgId, new HistoryDependentItemCallback(fullId())); } replyToNameUpdated(); } @@ -7349,8 +7355,6 @@ void HistoryReply::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, i HistoryReply::~HistoryReply() { if (replyToMsg) { App::historyUnregDependency(this, replyToMsg); - } else if (replyToMsgId && App::api()) { - App::api()->itemRemoved(this); } deleteAndMark(_replyToVia); } @@ -7616,9 +7620,9 @@ HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, cons , _media(0) { if (msg.has_reply_to_msg_id()) { UpdateInterfaces(HistoryServicePinned::Bit()); - Get()->msgId = msg.vreply_to_msg_id.v; + MsgId pinnedMsgId = Get()->msgId = msg.vreply_to_msg_id.v; if (!updatePinned() && App::api()) { - App::api()->requestDependencyItem(this, history->peer->asChannel(), Get()->msgId); + App::api()->requestMessageData(history->peer->asChannel(), pinnedMsgId, new HistoryDependentItemCallback(fullId())); } } setMessageByAction(msg.vaction); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index fb4df056b3..8e96253eeb 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -908,6 +908,17 @@ struct HistoryMessageForwarded : public BasicInterface mutable Text _text; }; +class HistoryDependentItemCallback : public SharedCallback2 { +public: + HistoryDependentItemCallback(FullMsgId dependent) : _dependent(dependent) { + } + void call(ChannelData *channel, MsgId msgId) const override; + +private: + FullMsgId _dependent; + +}; + class HistoryMedia; class HistoryItem : public HistoryElem, public Interfaces { public: @@ -1076,7 +1087,12 @@ public: return (channel->amEditor() || channel->amModerator() || out()); } + bool canPin() const { + return id > 0 && _history->peer->isMegagroup() && (_history->peer->asChannel()->amEditor() || _history->peer->asChannel()->amCreator()) && toHistoryMessage(); + } + bool canEdit(const QDateTime &cur) const; + bool hasDirectLink() const { return id > 0 && _history->peer->isChannel() && _history->peer->asChannel()->isPublic(); } @@ -1536,6 +1552,9 @@ public: } ImagePtr replyPreview(); + QString getCaption() const { + return _caption.original(); + } bool needsBubble(const HistoryItem *parent) const { return !_caption.isEmpty() || parent->Is() || parent->toHistoryReply() || parent->viaBot(); } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 6c72f29bf5..57b6a2493d 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -877,6 +877,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (item->canEdit(::date(unixtime()))) { _menu->addAction(lang(lng_context_edit_msg), _widget, SLOT(onEditMessage())); } + if (item->canPin()) { + bool ispinned = (item->history()->peer->asChannel()->mgInfo->pinnedMsgId == item->id); + _menu->addAction(lang(ispinned ? lng_context_unpin_msg : lng_context_pin_msg), _widget, ispinned ? SLOT(onUnpinMessage()) : SLOT(onPinMessage())); + } } if (lnkPhoto) { _menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextImage()))->setEnabled(true); @@ -933,6 +937,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (item->canEdit(::date(unixtime()))) { _menu->addAction(lang(lng_context_edit_msg), _widget, SLOT(onEditMessage())); } + if (item->canPin()) { + bool ispinned = (item->history()->peer->asChannel()->mgInfo->pinnedMsgId == item->id); + _menu->addAction(lang(ispinned ? lng_context_unpin_msg : lng_context_pin_msg), _widget, ispinned ? SLOT(onUnpinMessage()) : SLOT(onPinMessage())); + } } } else { if (item && item->id > 0 && isUponSelected != -2) { @@ -942,6 +950,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (item->canEdit(::date(unixtime()))) { _menu->addAction(lang(lng_context_edit_msg), _widget, SLOT(onEditMessage())); } + if (item->canPin()) { + bool ispinned = (item->history()->peer->asChannel()->mgInfo->pinnedMsgId == item->id); + _menu->addAction(lang(ispinned ? lng_context_unpin_msg : lng_context_pin_msg), _widget, ispinned ? SLOT(onUnpinMessage()) : SLOT(onPinMessage())); + } } if (item && !isUponSelected && !_contextMenuLnk) { if (HistoryMedia *media = (msg ? msg->getMedia() : 0)) { @@ -2632,6 +2644,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _editMsgId(0) , _replyEditMsg(0) , _fieldBarCancel(this, st::replyCancel) +, _pinnedBar(0) , _saveEditMsgRequestId(0) , _reportSpamStatus(dbiprsUnknown) , _previewData(0) @@ -2687,6 +2700,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _inRecord(false) , _inField(false) , _inReplyEdit(false) +, _inPinnedMsg(false) , a_recordingLevel(0, 0) , _recordingSamples(0) , a_recordOver(0, 0) @@ -2777,7 +2791,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _fieldBarCancel.hide(); _scroll.hide(); - _scroll.move(0, 0); _collapseComments.setParent(&_scroll); _kbScroll.setFocusPolicy(Qt::NoFocus); @@ -3500,7 +3513,7 @@ void HistoryWidget::applyDraft(bool parseLinks) { if (_editMsgId || _replyToId) { updateReplyEditTexts(); if (!_replyEditMsg && App::api()) { - App::api()->requestDependencyItem(0, _peer->asChannel(), _editMsgId ? _editMsgId : _replyToId); + App::api()->requestMessageData(_peer->asChannel(), _editMsgId ? _editMsgId : _replyToId, new ReplyEditMessageDataCallback()); } } } @@ -3591,6 +3604,11 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re if (_migrated && _migrated->unreadBar) { _migrated->unreadBar->destroy(); } + if (_pinnedBar) { + delete _pinnedBar; + _pinnedBar = nullptr; + _inPinnedMsg = false; + } _history = _migrated = 0; updateBotKeyboard(); } @@ -3672,6 +3690,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _updateHistoryItems.stop(); + pinnedMsgVisibilityUpdated(); if (_history->lastWidth || _history->isReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop)) { _fixedInScrollMsgId = 0; _fixedInScrollMsgTop = 0; @@ -3681,7 +3700,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re doneShow(); } - App::main()->peerUpdated(_peer); + emit App::main()->peerUpdated(_peer); Local::readDraftsWithCursors(_history); if (_migrated) { @@ -3886,10 +3905,18 @@ void HistoryWidget::updateControlsVisibility() { _cmdStart.hide(); _attachType.hide(); _emojiPan.hide(); + if (_pinnedBar) { + _pinnedBar->cancel.hide(); + _pinnedBar->shadow.hide(); + } return; } updateToEndVisibility(); + if (_pinnedBar) { + _pinnedBar->cancel.show(); + _pinnedBar->shadow.show(); + } if (_firstLoadRequest) { _scroll.hide(); } else { @@ -4091,7 +4118,7 @@ void HistoryWidget::updateControlsVisibility() { } void HistoryWidget::updateMouseTracking() { - bool trackMouse = !_fieldBarCancel.isHidden() || (cHasAudioCapture() && _send.isHidden() && !_field.isHidden()); + bool trackMouse = !_fieldBarCancel.isHidden() || _pinnedBar || (cHasAudioCapture() && _send.isHidden() && !_field.isHidden()); setMouseTracking(trackMouse); } @@ -4874,6 +4901,10 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo _joinChannel.hide(); _muteUnmute.hide(); _topShadow.hide(); + if (_pinnedBar) { + _pinnedBar->shadow.hide(); + _pinnedBar->cancel.hide(); + } a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width())); a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0); @@ -5056,6 +5087,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { bool inRecord = _send.geometry().contains(pos); bool inField = pos.y() >= (_scroll.y() + _scroll.height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width(); bool inReplyEdit = QRect(st::replySkip, _field.y() - st::sendPadding - st::replyHeight, width() - st::replySkip - _fieldBarCancel.width(), st::replyHeight).contains(pos) && (_editMsgId || replyToId()); + bool inPinnedMsg = QRect(0, 0, width(), st::replyHeight).contains(pos) && _pinnedBar; bool startAnim = false; if (inRecord != _inRecord) { _inRecord = inRecord; @@ -5075,6 +5107,10 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { _inReplyEdit = inReplyEdit; setCursor(inReplyEdit ? style::cur_pointer : style::cur_default); } + if (inPinnedMsg != _inPinnedMsg) { + _inPinnedMsg = inPinnedMsg; + setCursor(inPinnedMsg ? style::cur_pointer : style::cur_default); + } if (startAnim) _a_record.start(); } @@ -6151,6 +6187,14 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { } _field.move(_attachDocument.x() + _attachDocument.width(), height() - kbh - _field.height() - st::sendPadding); + if (_pinnedBar) { + _scroll.move(0, st::replyHeight); + _pinnedBar->cancel.move(width() - _pinnedBar->cancel.width(), 0); + _pinnedBar->shadow.setGeometry(0, st::replyHeight, width(), st::lineWidth); + } else { + _scroll.move(0, _pinnedBar ? st::replyHeight : 0); + } + _attachDocument.move(0, height() - kbh - _attachDocument.height()); _attachPhoto.move(_attachDocument.x(), _attachDocument.y()); @@ -6250,6 +6294,9 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, newScrollHeight -= _kbScroll.height(); } } + if (_pinnedBar) { + newScrollHeight -= st::replyHeight; + } bool wasAtBottom = _scroll.scrollTop() + 1 > _scroll.scrollTopMax(), needResize = _scroll.width() != width() || _scroll.height() != newScrollHeight; if (needResize) { _scroll.resize(width(), newScrollHeight); @@ -6553,6 +6600,9 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { _a_record.start(); } else if (_inReplyEdit) { Ui::showPeerHistory(_peer, _editMsgId ? _editMsgId : replyToId()); + } else if (_inPinnedMsg) { + t_assert(_pinnedBar != nullptr); + Ui::showPeerHistory(_peer, _pinnedBar->msgId); } } @@ -6724,6 +6774,89 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) { _field.setFocus(); } +HistoryWidget::PinnedBar::PinnedBar(MsgId msgId, HistoryWidget *parent) +: msgId(msgId) +, msg(0) +, cancel(parent, st::replyCancel) +, shadow(parent, st::shadowColor) { +} + +void HistoryWidget::updatePinnedBar(bool force) { + if (!_pinnedBar || _pinnedBar->msg) { + return; + } + t_assert(_history != nullptr); + + _pinnedBar->msg = App::histItemById(_history->channelId(), _pinnedBar->msgId); + if (_pinnedBar->msg) { + _pinnedBar->text.setText(st::msgFont, _pinnedBar->msg->inDialogsText(), _textDlgOptions); + } else if (force) { + if (_peer && _peer->isMegagroup()) { + _peer->asChannel()->mgInfo->pinnedMsgId = 0; + } + delete _pinnedBar; + _pinnedBar = nullptr; + _inPinnedMsg = false; + resizeEvent(0); + update(); + } +} + +bool HistoryWidget::pinnedMsgVisibilityUpdated() { + bool result = false; + MsgId pinnedMsgId = (_peer && _peer->isMegagroup()) ? _peer->asChannel()->mgInfo->pinnedMsgId : 0; + if (pinnedMsgId && !_peer->asChannel()->amCreator() && !_peer->asChannel()->amEditor()) { + Global::HiddenPinnedMessagesMap::const_iterator it = Global::HiddenPinnedMessages().constFind(_peer->id); + if (it != Global::HiddenPinnedMessages().cend()) { + if (it.value() == pinnedMsgId) { + pinnedMsgId = 0; + } else { + Global::RefHiddenPinnedMessages().remove(_peer->id); + Local::writeUserSettings(); + } + } + } + if (pinnedMsgId) { + if (!_pinnedBar) { + _pinnedBar = new PinnedBar(pinnedMsgId, this); + if (_a_show.animating()) { + _pinnedBar->cancel.hide(); + _pinnedBar->shadow.hide(); + } else { + _pinnedBar->cancel.show(); + _pinnedBar->shadow.show(); + } + connect(&_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide())); + _sideShadow.raise(); + _topShadow.raise(); + updatePinnedBar(); + result = true; + _scroll.scrollToY(_scroll.scrollTop() + st::replyHeight); + } else if (_pinnedBar->msgId != pinnedMsgId) { + _pinnedBar->msgId = pinnedMsgId; + _pinnedBar->msg = 0; + _pinnedBar->text.clean(); + updatePinnedBar(); + update(); + } + if (!_pinnedBar->msg && App::api()) { + App::api()->requestMessageData(_peer->asChannel(), _pinnedBar->msgId, new ReplyEditMessageDataCallback()); + } + } else if (_pinnedBar) { + delete _pinnedBar; + _pinnedBar = nullptr; + result = true; + _scroll.scrollToY(_scroll.scrollTop() - st::replyHeight); + } + return result; +} + +void HistoryWidget::ReplyEditMessageDataCallback::call(ChannelData *channel, MsgId msgId) const { + if (App::main()) { + App::main()->messageDataReceived(channel, msgId); + } +} + void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &caption) { if (!_history || !doc || !canSendMessages(_peer)) return; @@ -6940,6 +7073,62 @@ void HistoryWidget::onEditMessage() { } } +void HistoryWidget::onPinMessage() { + HistoryItem *to = App::contextItem(); + if (!to || !to->canPin() || !_peer || !_peer->isMegagroup()) return; + + Ui::showLayer(new PinMessageBox(_peer->asChannel(), to->id)); +} + +void HistoryWidget::onUnpinMessage() { + if (!_peer || !_peer->isMegagroup()) return; + + ConfirmBox *box = new ConfirmBox(lang(lng_pinned_unpin_sure), lang(lng_pinned_unpin)); + connect(box, SIGNAL(confirmed()), this, SLOT(onUnpinMessageSure())); + Ui::showLayer(box); +} + +void HistoryWidget::onUnpinMessageSure() { + if (!_peer || !_peer->isMegagroup()) return; + + _peer->asChannel()->mgInfo->pinnedMsgId = 0; + if (pinnedMsgVisibilityUpdated()) { + resizeEvent(0); + update(); + } + + Ui::hideLayer(); + MTP::send(MTPchannels_UpdatePinnedMessage(MTP_int(0), _peer->asChannel()->inputChannel, MTP_int(0)), rpcDone(&HistoryWidget::unpinDone)); +} + +void HistoryWidget::unpinDone(const MTPUpdates &updates) { + if (App::main()) { + App::main()->sentUpdatesReceived(updates); + } +} + +void HistoryWidget::onPinnedHide() { + if (!_peer || !_peer->isMegagroup()) return; + if (!_peer->asChannel()->mgInfo->pinnedMsgId) { + if (pinnedMsgVisibilityUpdated()) { + resizeEvent(0); + update(); + } + return; + } + + if (_peer->asChannel()->amCreator() || _peer->asChannel()->amEditor()) { + onUnpinMessage(); + } else { + Global::RefHiddenPinnedMessages().insert(_peer->id, _peer->asChannel()->mgInfo->pinnedMsgId); + Local::writeUserSettings(); + if (pinnedMsgVisibilityUpdated()) { + resizeEvent(0); + update(); + } + } +} + void HistoryWidget::onCopyPostLink() { HistoryItem *to = App::contextItem(); if (!to || !to->hasDirectLink()) return; @@ -7225,6 +7414,10 @@ void HistoryWidget::peerUpdated(PeerData *data) { QTimer::singleShot(ReloadChannelMembersTimeout, App::api(), SLOT(delayedRequestParticipantsCount())); return; } + bool resize = false; + if (pinnedMsgVisibilityUpdated()) { + resize = true; + } updateListSize(); if (_peer->isChannel()) updateReportSpamStatus(); if (App::api()) { @@ -7237,7 +7430,9 @@ void HistoryWidget::peerUpdated(PeerData *data) { } } if (!_a_show.animating()) { - bool resize = (_unblock.isHidden() == isBlocked() || (!isBlocked() && _joinChannel.isHidden() == isJoinChannel())); + if (_unblock.isHidden() == isBlocked() || (!isBlocked() && _joinChannel.isHidden() == isJoinChannel())) { + resize = true; + } bool newCanSendMessages = canSendMessages(_peer); if (newCanSendMessages != _canSendMessages) { _canSendMessages = newCanSendMessages; @@ -7247,7 +7442,10 @@ void HistoryWidget::peerUpdated(PeerData *data) { resize = true; } updateControlsVisibility(); - if (resize) resizeEvent(0); + if (resize) { + resizeEvent(0); + update(); + } } App::main()->updateOnlineDisplay(); } @@ -7383,6 +7581,16 @@ void HistoryWidget::updateTopBarSelection() { update(); } +void HistoryWidget::messageDataReceived(ChannelData *channel, MsgId msgId) { + if (!_peer || _peer->asChannel() != channel || !msgId) return; + if (_editMsgId == msgId || _replyToId == msgId) { + updateReplyEditTexts(true); + } + if (_pinnedBar && _pinnedBar->msgId == msgId) { + updatePinnedBar(true); + } +} + void HistoryWidget::updateReplyEditTexts(bool force) { if (_replyEditMsg || (!_editMsgId && !_replyToId)) { return; @@ -7556,6 +7764,40 @@ void HistoryWidget::drawRecording(Painter &p) { p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto.y() + st::recordTextTop + st::recordFont->ascent, lang(lng_record_cancel)); } +void HistoryWidget::drawPinnedBar(Painter &p) { + t_assert(_pinnedBar != nullptr); + + Text *from = 0, *text = 0; + bool serviceColor = false, hasForward = readyToForward(); + ImagePtr preview; + p.fillRect(0, 0, width(), st::replyHeight, st::taMsgField.bgColor); + + QRect rbar(rtlrect(st::msgReplyBarSkip + st::msgReplyBarPos.x(), st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), width())); + p.fillRect(rbar, st::msgInReplyBarColor); + + int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip; + if (_pinnedBar->msg) { + if (_pinnedBar->msg->getMedia() && _pinnedBar->msg->getMedia()->hasReplyPreview()) { + ImagePtr replyPreview = _pinnedBar->msg->getMedia()->replyPreview(); + if (!replyPreview->isNull()) { + QRect to(left, st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); + p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height())); + } + left += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); + } + p.setPen(st::replyColor); + p.setFont(st::msgServiceNameFont); + p.drawText(left, st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, lang(lng_pinned_message)); + + p.setPen((((_pinnedBar->msg->toHistoryMessage() && _pinnedBar->msg->toHistoryMessage()->emptyText()) || _pinnedBar->msg->serviceMsg()) ? st::msgInDateFg : st::msgColor)->p); + _pinnedBar->text.drawElided(p, left, st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - left -_fieldBarCancel.width() - st::msgReplyPadding.right()); + } else { + p.setFont(st::msgDateFont); + p.setPen(st::msgInDateFg); + p.drawText(left, st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - left - _pinnedBar->cancel.width() - st::msgReplyPadding.right())); + } +} + void HistoryWidget::paintEvent(QPaintEvent *e) { if (!App::main() || (App::wnd() && App::wnd()->contentOverlapped(this, e))) return; @@ -7614,6 +7856,9 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { drawRecordButton(p); if (_recording) drawRecording(p); } + if (_pinnedBar) { + drawPinnedBar(p); + } } if (_scroll.isHidden()) { QPoint dogPos((width() - st::msgDogImg.pxWidth()) / 2, ((height() - _field.height() - 2 * st::sendPadding - st::msgDogImg.pxHeight()) * 4) / 9); @@ -7723,5 +7968,6 @@ bool HistoryWidget::touchScroll(const QPoint &delta) { } HistoryWidget::~HistoryWidget() { - delete _list; + deleteAndMark(_pinnedBar); + deleteAndMark(_list); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index bd53f26486..23584da8ee 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -517,7 +517,7 @@ public: void updateScrollColors(); MsgId replyToId() const; - void updateReplyEditTexts(bool force = false); + void messageDataReceived(ChannelData *channel, MsgId msgId); bool lastForceReplyReplied(const FullMsgId &replyTo = FullMsgId(NoChannel, -1)) const; void cancelReply(bool lastKeyboardUsed = false); void cancelEdit(); @@ -612,6 +612,10 @@ public slots: void onCancel(); void onReplyToMessage(); void onEditMessage(); + void onPinMessage(); + void onUnpinMessage(); + void onUnpinMessageSure(); + void onPinnedHide(); void onCopyPostLink(); void onFieldBarCancel(); @@ -717,6 +721,26 @@ private: Text _replyEditMsgText; IconedButton _fieldBarCancel; + void updateReplyEditTexts(bool force = false); + + struct PinnedBar { + PinnedBar(MsgId msgId, HistoryWidget *parent); + + MsgId msgId; + HistoryItem *msg; + Text text; + IconedButton cancel; + PlainShadow shadow; + }; + PinnedBar *_pinnedBar; + void updatePinnedBar(bool force = false); + bool pinnedMsgVisibilityUpdated(); + void unpinDone(const MTPUpdates &updates); + + class ReplyEditMessageDataCallback : public SharedCallback2 { + public: + void call(ChannelData *channel, MsgId msgId) const override; + }; void sendExistingDocument(DocumentData *doc, const QString &caption); void sendExistingPhoto(PhotoData *photo, const QString &caption); @@ -724,6 +748,7 @@ private: void drawField(Painter &p); void drawRecordButton(Painter &p); void drawRecording(Painter &p); + void drawPinnedBar(Painter &p); void updateMouseTracking(); @@ -839,7 +864,7 @@ private: bool _cmdStartShown; MessageField _field; Animation _a_record, _a_recording; - bool _recording, _inRecord, _inField, _inReplyEdit; + bool _recording, _inRecord, _inField, _inReplyEdit, _inPinnedMsg; anim::ivalue a_recordingLevel; int32 _recordingSamples; anim::fvalue a_recordOver, a_recordDown; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 9764cad672..118ba1c92d 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -556,6 +556,69 @@ namespace { lskSavedGifs = 0x0f, // no data }; + enum { + dbiKey = 0x00, + dbiUser = 0x01, + dbiDcOptionOld = 0x02, + dbiChatSizeMax = 0x03, + dbiMutePeer = 0x04, + dbiSendKey = 0x05, + dbiAutoStart = 0x06, + dbiStartMinimized = 0x07, + dbiSoundNotify = 0x08, + dbiWorkMode = 0x09, + dbiSeenTrayTooltip = 0x0a, + dbiDesktopNotify = 0x0b, + dbiAutoUpdate = 0x0c, + dbiLastUpdateCheck = 0x0d, + dbiWindowPosition = 0x0e, + dbiConnectionType = 0x0f, + // 0x10 reserved + dbiDefaultAttach = 0x11, + dbiCatsAndDogs = 0x12, + dbiReplaceEmojis = 0x13, + dbiAskDownloadPath = 0x14, + dbiDownloadPathOld = 0x15, + dbiScale = 0x16, + dbiEmojiTabOld = 0x17, + dbiRecentEmojisOld = 0x18, + dbiLoggedPhoneNumber = 0x19, + dbiMutedPeers = 0x1a, + // 0x1b reserved + dbiNotifyView = 0x1c, + dbiSendToMenu = 0x1d, + dbiCompressPastedImage = 0x1e, + dbiLang = 0x1f, + dbiLangFile = 0x20, + dbiTileBackground = 0x21, + dbiAutoLock = 0x22, + dbiDialogLastPath = 0x23, + dbiRecentEmojis = 0x24, + dbiEmojiVariants = 0x25, + dbiRecentStickers = 0x26, + dbiDcOption = 0x27, + dbiTryIPv6 = 0x28, + dbiSongVolume = 0x29, + dbiWindowsNotifications = 0x30, + dbiIncludeMuted = 0x31, + dbiMegagroupSizeMax = 0x32, + dbiDownloadPath = 0x33, + dbiAutoDownload = 0x34, + dbiSavedGifsLimit = 0x35, + dbiShowingSavedGifs = 0x36, + dbiAutoPlay = 0x37, + dbiAdaptiveForWide = 0x38, + dbiHiddenPinnedMessages = 0x39, + + dbiEncryptedWithSalt = 333, + dbiEncrypted = 444, + + // 500-600 reserved + + dbiVersion = 666, + }; + + typedef QMap DraftsMap; DraftsMap _draftsMap, _draftCursorsMap; typedef QMap DraftsNotReadMap; @@ -1266,6 +1329,15 @@ namespace { cSetEmojiVariants(v); } break; + + case dbiHiddenPinnedMessages: { + Global::HiddenPinnedMessagesMap v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + Global::SetHiddenPinnedMessages(v); + } break; + case dbiDialogLastPath: { QString path; stream >> path; @@ -1510,6 +1582,9 @@ namespace { size += sizeof(quint32) + sizeof(qint32) + (cRecentStickersPreload().isEmpty() ? cGetRecentStickers().size() : cRecentStickersPreload().size()) * (sizeof(uint64) + sizeof(ushort)); size += sizeof(quint32) + _stringSize(cDialogLastPath()); size += sizeof(quint32) + 3 * sizeof(qint32); + if (!Global::HiddenPinnedMessages().isEmpty()) { + size += sizeof(quint32) + sizeof(qint32) + Global::HiddenPinnedMessages().size() * (sizeof(PeerId) + sizeof(MsgId)); + } EncryptedDescriptor data(size); data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter); @@ -1553,6 +1628,9 @@ namespace { } data.stream << quint32(dbiRecentStickers) << v; } + if (!Global::HiddenPinnedMessages().isEmpty()) { + data.stream << quint32(dbiHiddenPinnedMessages) << Global::HiddenPinnedMessages(); + } FileWriteDescriptor file(_userSettingsKey); file.writeEncrypted(data); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 9ef400e1c0..e8854ebc3b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1510,7 +1510,6 @@ void MainWidget::changingMsgId(HistoryItem *row, MsgId newId) { } void MainWidget::itemRemoved(HistoryItem *item) { - api()->itemRemoved(item); dialogs.itemRemoved(item); if (history.peer() == item->history()->peer || (history.peer() && history.peer() == item->history()->peer->migrateTo())) { history.itemRemoved(item); @@ -2056,8 +2055,8 @@ ApiWrap *MainWidget::api() { return _api; } -void MainWidget::updateDependencyItem() { - history.updateReplyEditTexts(true); +void MainWidget::messageDataReceived(ChannelData *channel, MsgId msgId) { + history.messageDataReceived(channel, msgId); } void MainWidget::updateBotKeyboard(History *h) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 08c053f5b6..2a039fe42f 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -373,7 +373,7 @@ public: ImagePtr newBackgroundThumb(); ApiWrap *api(); - void updateDependencyItem(); + void messageDataReceived(ChannelData *channel, MsgId msgId); void updateBotKeyboard(History *h); void pushReplyReturn(HistoryItem *item); diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.cpp b/Telegram/SourceFiles/mtproto/mtpScheme.cpp index 5a4af0ce6b..5486b9582d 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.cpp +++ b/Telegram/SourceFiles/mtproto/mtpScheme.cpp @@ -1157,14 +1157,15 @@ void _serialize_channel(MTPStringLogger &to, int32 stage, int32 lev, Types &type case 9: to.add(" restricted: "); ++stages.back(); if (flag & MTPDchannel::flag_restricted) { to.add("YES [ BY BIT 9 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break; case 10: to.add(" democracy: "); ++stages.back(); if (flag & MTPDchannel::flag_democracy) { to.add("YES [ BY BIT 10 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 10 IN FIELD flags ]"); } break; case 11: to.add(" signatures: "); ++stages.back(); if (flag & MTPDchannel::flag_signatures) { to.add("YES [ BY BIT 11 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 11 IN FIELD flags ]"); } break; - case 12: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 13: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 14: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 15: to.add(" username: "); ++stages.back(); if (flag & MTPDchannel::flag_username) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break; - case 16: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 17: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 18: to.add(" version: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 19: to.add(" restriction_reason: "); ++stages.back(); if (flag & MTPDchannel::flag_restriction_reason) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break; + case 12: to.add(" min: "); ++stages.back(); if (flag & MTPDchannel::flag_min) { to.add("YES [ BY BIT 12 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 12 IN FIELD flags ]"); } break; + case 13: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 14: to.add(" access_hash: "); ++stages.back(); if (flag & MTPDchannel::flag_access_hash) { types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 13 IN FIELD flags ]"); } break; + case 15: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 16: to.add(" username: "); ++stages.back(); if (flag & MTPDchannel::flag_username) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break; + case 17: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 18: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 19: to.add(" version: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 20: to.add(" restriction_reason: "); ++stages.back(); if (flag & MTPDchannel::flag_restriction_reason) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; } } @@ -2696,7 +2697,9 @@ void _serialize_updateChannelTooLong(MTPStringLogger &to, int32 stage, int32 lev to.add("\n").addSpaces(lev); } switch (stage) { - case 0: to.add(" channel_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(" channel_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 2: to.add(" pts: "); ++stages.back(); if (flag & MTPDupdateChannelTooLong::flag_pts) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; } } diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.h b/Telegram/SourceFiles/mtproto/mtpScheme.h index 577f80f59b..e618f914c7 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.h +++ b/Telegram/SourceFiles/mtproto/mtpScheme.h @@ -133,7 +133,7 @@ enum { mtpc_chatEmpty = 0x9ba2d800, mtpc_chat = 0xd91cdd54, mtpc_chatForbidden = 0x7328bdb, - mtpc_channel = 0x4b1b7506, + mtpc_channel = 0xa14dca52, mtpc_channelForbidden = 0x2d85832c, mtpc_chatFull = 0x2e02a614, mtpc_channelFull = 0x97bee562, @@ -255,7 +255,7 @@ enum { mtpc_updateReadHistoryOutbox = 0x2f2f21bf, mtpc_updateWebPage = 0x7f891213, mtpc_updateReadMessagesContents = 0x68c13933, - mtpc_updateChannelTooLong = 0x60946422, + mtpc_updateChannelTooLong = 0xeb0467fb, mtpc_updateChannel = 0xb6d45656, mtpc_updateChannelGroup = 0xc36c1e3c, mtpc_updateNewChannelMessage = 0x62ba04d9, @@ -5555,7 +5555,7 @@ private: friend MTPupdate MTP_updateReadHistoryOutbox(const MTPPeer &_peer, MTPint _max_id, MTPint _pts, MTPint _pts_count); friend MTPupdate MTP_updateWebPage(const MTPWebPage &_webpage, MTPint _pts, MTPint _pts_count); friend MTPupdate MTP_updateReadMessagesContents(const MTPVector &_messages, MTPint _pts, MTPint _pts_count); - friend MTPupdate MTP_updateChannelTooLong(MTPint _channel_id); + friend MTPupdate MTP_updateChannelTooLong(MTPint _flags, MTPint _channel_id, MTPint _pts); friend MTPupdate MTP_updateChannel(MTPint _channel_id); friend MTPupdate MTP_updateChannelGroup(MTPint _channel_id, const MTPMessageGroup &_group); friend MTPupdate MTP_updateNewChannelMessage(const MTPMessage &_message, MTPint _pts, MTPint _pts_count); @@ -9966,6 +9966,8 @@ public: flag_restricted = (1 << 9), flag_democracy = (1 << 10), flag_signatures = (1 << 11), + flag_min = (1 << 12), + flag_access_hash = (1 << 13), flag_username = (1 << 6), flag_restriction_reason = (1 << 9), }; @@ -9981,6 +9983,8 @@ public: bool is_restricted() const { return vflags.v & flag_restricted; } bool is_democracy() const { return vflags.v & flag_democracy; } bool is_signatures() const { return vflags.v & flag_signatures; } + bool is_min() const { return vflags.v & flag_min; } + bool has_access_hash() const { return vflags.v & flag_access_hash; } bool has_username() const { return vflags.v & flag_username; } bool has_restriction_reason() const { return vflags.v & flag_restriction_reason; } }; @@ -11210,10 +11214,18 @@ class MTPDupdateChannelTooLong : public mtpDataImpl { public: MTPDupdateChannelTooLong() { } - MTPDupdateChannelTooLong(MTPint _channel_id) : vchannel_id(_channel_id) { + MTPDupdateChannelTooLong(MTPint _flags, MTPint _channel_id, MTPint _pts) : vflags(_flags), vchannel_id(_channel_id), vpts(_pts) { } + MTPint vflags; MTPint vchannel_id; + MTPint vpts; + + enum { + flag_pts = (1 << 0), + }; + + bool has_pts() const { return vflags.v & flag_pts; } }; class MTPDupdateChannel : public mtpDataImpl { @@ -22948,7 +22960,7 @@ inline uint32 MTPchat::innerLength() const { } case mtpc_channel: { const MTPDchannel &v(c_channel()); - return v.vflags.innerLength() + v.vid.innerLength() + v.vaccess_hash.innerLength() + v.vtitle.innerLength() + (v.has_username() ? v.vusername.innerLength() : 0) + v.vphoto.innerLength() + v.vdate.innerLength() + v.vversion.innerLength() + (v.has_restriction_reason() ? v.vrestriction_reason.innerLength() : 0); + return v.vflags.innerLength() + v.vid.innerLength() + (v.has_access_hash() ? v.vaccess_hash.innerLength() : 0) + v.vtitle.innerLength() + (v.has_username() ? v.vusername.innerLength() : 0) + v.vphoto.innerLength() + v.vdate.innerLength() + v.vversion.innerLength() + (v.has_restriction_reason() ? v.vrestriction_reason.innerLength() : 0); } case mtpc_channelForbidden: { const MTPDchannelForbidden &v(c_channelForbidden()); @@ -22992,7 +23004,7 @@ inline void MTPchat::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId MTPDchannel &v(_channel()); v.vflags.read(from, end); v.vid.read(from, end); - v.vaccess_hash.read(from, end); + if (v.has_access_hash()) { v.vaccess_hash.read(from, end); } else { v.vaccess_hash = MTPlong(); } v.vtitle.read(from, end); if (v.has_username()) { v.vusername.read(from, end); } else { v.vusername = MTPstring(); } v.vphoto.read(from, end); @@ -23036,7 +23048,7 @@ inline void MTPchat::write(mtpBuffer &to) const { const MTPDchannel &v(c_channel()); v.vflags.write(to); v.vid.write(to); - v.vaccess_hash.write(to); + if (v.has_access_hash()) v.vaccess_hash.write(to); v.vtitle.write(to); if (v.has_username()) v.vusername.write(to); v.vphoto.write(to); @@ -25502,7 +25514,7 @@ inline uint32 MTPupdate::innerLength() const { } case mtpc_updateChannelTooLong: { const MTPDupdateChannelTooLong &v(c_updateChannelTooLong()); - return v.vchannel_id.innerLength(); + return v.vflags.innerLength() + v.vchannel_id.innerLength() + (v.has_pts() ? v.vpts.innerLength() : 0); } case mtpc_updateChannel: { const MTPDupdateChannel &v(c_updateChannel()); @@ -25761,7 +25773,9 @@ inline void MTPupdate::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeI case mtpc_updateChannelTooLong: _type = cons; { if (!data) setData(new MTPDupdateChannelTooLong()); MTPDupdateChannelTooLong &v(_updateChannelTooLong()); + v.vflags.read(from, end); v.vchannel_id.read(from, end); + if (v.has_pts()) { v.vpts.read(from, end); } else { v.vpts = MTPint(); } } break; case mtpc_updateChannel: _type = cons; { if (!data) setData(new MTPDupdateChannel()); @@ -26024,7 +26038,9 @@ inline void MTPupdate::write(mtpBuffer &to) const { } break; case mtpc_updateChannelTooLong: { const MTPDupdateChannelTooLong &v(c_updateChannelTooLong()); + v.vflags.write(to); v.vchannel_id.write(to); + if (v.has_pts()) v.vpts.write(to); } break; case mtpc_updateChannel: { const MTPDupdateChannel &v(c_updateChannel()); @@ -26326,8 +26342,8 @@ inline MTPupdate MTP_updateWebPage(const MTPWebPage &_webpage, MTPint _pts, MTPi inline MTPupdate MTP_updateReadMessagesContents(const MTPVector &_messages, MTPint _pts, MTPint _pts_count) { return MTPupdate(new MTPDupdateReadMessagesContents(_messages, _pts, _pts_count)); } -inline MTPupdate MTP_updateChannelTooLong(MTPint _channel_id) { - return MTPupdate(new MTPDupdateChannelTooLong(_channel_id)); +inline MTPupdate MTP_updateChannelTooLong(MTPint _flags, MTPint _channel_id, MTPint _pts) { + return MTPupdate(new MTPDupdateChannelTooLong(_flags, _channel_id, _pts)); } inline MTPupdate MTP_updateChannel(MTPint _channel_id) { return MTPupdate(new MTPDupdateChannel(_channel_id)); diff --git a/Telegram/SourceFiles/mtproto/scheme.tl b/Telegram/SourceFiles/mtproto/scheme.tl index 5588272f79..15bb62e2b9 100644 --- a/Telegram/SourceFiles/mtproto/scheme.tl +++ b/Telegram/SourceFiles/mtproto/scheme.tl @@ -209,7 +209,7 @@ userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#9ba2d800 id:int = Chat; chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat; chatForbidden#7328bdb id:int title:string = Chat; -channel#4b1b7506 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true id:int access_hash:long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string = Chat; +channel#a14dca52 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string = Chat; channelForbidden#2d85832c id:int access_hash:long title:string = Chat; chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector = ChatFull; @@ -369,7 +369,7 @@ updateReadHistoryInbox#9961fd5c peer:Peer max_id:int pts:int pts_count:int = Upd updateReadHistoryOutbox#2f2f21bf peer:Peer max_id:int pts:int pts_count:int = Update; updateWebPage#7f891213 webpage:WebPage pts:int pts_count:int = Update; updateReadMessagesContents#68c13933 messages:Vector pts:int pts_count:int = Update; -updateChannelTooLong#60946422 channel_id:int = Update; +updateChannelTooLong#eb0467fb flags:# channel_id:int pts:flags.0?int = Update; updateChannel#b6d45656 channel_id:int = Update; updateChannelGroup#c36c1e3c channel_id:int group:MessageGroup = Update; updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update; diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 16ed4b4c04..2ce22da176 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -58,6 +58,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData , _invitationLink(this, qsl("telegram.me/joinchat/")) , _botSettings(this, lang(lng_profile_bot_settings)) , _botHelp(this, lang(lng_profile_bot_help)) +, _pinnedMessage(this, lang(lng_pinned_message)) , _username(this, (_peerChannel && _peerChannel->isPublic()) ? (qsl("telegram.me/") + _peerChannel->username) : lang(lng_profile_create_public_link)) , _members(this, lng_channel_members_link(lt_count, (_peerChannel && _peerChannel->count > 0) ? _peerChannel->count : 1)) , _admins(this, lng_channel_admins_link(lt_count, (_peerChannel ? (_peerChannel->adminsCount > 0 ? _peerChannel->adminsCount : 1) : ((_peerChat && _peerChat->adminsEnabled()) ? (_peerChat->admins.size() + 1) : 0)))) @@ -180,6 +181,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData connect(&_botSettings, SIGNAL(clicked()), this, SLOT(onBotSettings())); connect(&_botHelp, SIGNAL(clicked()), this, SLOT(onBotHelp())); + connect(&_pinnedMessage, SIGNAL(clicked()), this, SLOT(onPinnedMessage())); connect(App::app(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUpdateDone(PeerId))); connect(App::app(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUpdateFail(PeerId))); @@ -201,6 +203,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData _botSettings.hide(); _botHelp.hide(); } + updatePinnedMessageVisibility(); // migrate to megagroup connect(&_migrate, SIGNAL(clicked()), this, SLOT(onMigrate())); @@ -591,6 +594,14 @@ void ProfileInner::onBotHelp() { updateBotLinksVisibility(); } +void ProfileInner::onPinnedMessage() { + if (!_peerChannel || !_peerChannel->isMegagroup() || !_peerChannel->mgInfo->pinnedMsgId) { + updatePinnedMessageVisibility(); + return; + } + Ui::showPeerHistory(_peer, _peerChannel->mgInfo->pinnedMsgId); +} + void ProfileInner::peerUpdated(PeerData *data) { if (data == _peer) { PhotoData *photo = 0; @@ -614,6 +625,7 @@ void ProfileInner::peerUpdated(PeerData *data) { _members.setText(lng_channel_members_link(lt_count, (_peerChannel->count > 0) ? _peerChannel->count : 1)); _admins.setText(lng_channel_admins_link(lt_count, (_peerChannel->adminsCount > 0) ? _peerChannel->adminsCount : 1)); _onlineText = (_peerChannel->count > 0) ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status); + updatePinnedMessageVisibility(); } _photoLink = (photo && photo->date) ? TextLinkPtr(new PhotoLink(photo, _peer)) : TextLinkPtr(); if (_peer->name != _nameCache) { @@ -1352,7 +1364,10 @@ void ProfileInner::resizeEvent(QResizeEvent *e) { } _members.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop); addbyname += st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); - _admins.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop); + if (!_admins.isHidden()) { + _admins.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop); + addbyname += st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); + } if ((_peerChat && _amCreator && _peerChat->canEdit()) || (_peerChannel && (_amCreator || _peerChannel->amEditor() || _peerChannel->amModerator()))) { _cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhotoSize - st::linkFont->height); } else { @@ -1360,6 +1375,7 @@ void ProfileInner::resizeEvent(QResizeEvent *e) { _botSettings.move(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent) + st::profilePhoneTop); _botHelp.move(_botSettings.x() + (_botSettings.isHidden() ? 0 : _botSettings.width() + st::profilePhoneLeft), _botSettings.y()); } + _pinnedMessage.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop); top += st::profilePhotoSize; top += st::profileButtonTop; @@ -1773,12 +1789,21 @@ void ProfileInner::updateInvitationLink() { } } +void ProfileInner::updatePinnedMessageVisibility() { + if (_peerChannel && _peerChannel->isMegagroup() && _peerChannel->mgInfo->pinnedMsgId && !_amCreator && !_peerChannel->amEditor()) { + _pinnedMessage.show(); + } else { + _pinnedMessage.hide(); + } +} + void ProfileInner::updateBotLinksVisibility() { if (!_peerUser || !_peerUser->botInfo || _peerUser->botInfo->commands.isEmpty()) { _botSettings.hide(); _botHelp.hide(); return; } + bool hasSettings = false, hasHelp = false; for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) { QString cmd = _peerUser->botInfo->commands.at(i).command; diff --git a/Telegram/SourceFiles/profilewidget.h b/Telegram/SourceFiles/profilewidget.h index 4527d483d2..23b6fcd029 100644 --- a/Telegram/SourceFiles/profilewidget.h +++ b/Telegram/SourceFiles/profilewidget.h @@ -124,6 +124,7 @@ public slots: void onBotSettings(); void onBotHelp(); + void onPinnedMessage(); void onUpdateDelayed(); @@ -132,6 +133,7 @@ private: void showAll(); void updateInvitationLink(); void updateBotLinksVisibility(); + void updatePinnedMessageVisibility(); void chatInviteDone(const MTPExportedChatInvite &result); bool updateMediaLinks(int32 *addToScroll = 0); // returns if anything changed @@ -160,7 +162,7 @@ private: FlatButton _sendMessage, _shareContact, _inviteToGroup; LinkButton _cancelPhoto, _createInvitationLink, _invitationLink; QString _invitationText; - LinkButton _botSettings, _botHelp, _username, _members, _admins; + LinkButton _botSettings, _botHelp, _pinnedMessage, _username, _members, _admins; Text _about; int32 _aboutTop, _aboutHeight; diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index 68d6864ced..032d69a3f2 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -323,67 +323,6 @@ protected: QString translitRusEng(const QString &rus); QString rusKeyboardLayoutSwitch(const QString &from); -enum DataBlockId { - dbiKey = 0x00, - dbiUser = 0x01, - dbiDcOptionOld = 0x02, - dbiChatSizeMax = 0x03, - dbiMutePeer = 0x04, - dbiSendKey = 0x05, - dbiAutoStart = 0x06, - dbiStartMinimized = 0x07, - dbiSoundNotify = 0x08, - dbiWorkMode = 0x09, - dbiSeenTrayTooltip = 0x0a, - dbiDesktopNotify = 0x0b, - dbiAutoUpdate = 0x0c, - dbiLastUpdateCheck = 0x0d, - dbiWindowPosition = 0x0e, - dbiConnectionType = 0x0f, -// 0x10 reserved - dbiDefaultAttach = 0x11, - dbiCatsAndDogs = 0x12, - dbiReplaceEmojis = 0x13, - dbiAskDownloadPath = 0x14, - dbiDownloadPathOld = 0x15, - dbiScale = 0x16, - dbiEmojiTabOld = 0x17, - dbiRecentEmojisOld = 0x18, - dbiLoggedPhoneNumber = 0x19, - dbiMutedPeers = 0x1a, -// 0x1b reserved - dbiNotifyView = 0x1c, - dbiSendToMenu = 0x1d, - dbiCompressPastedImage = 0x1e, - dbiLang = 0x1f, - dbiLangFile = 0x20, - dbiTileBackground = 0x21, - dbiAutoLock = 0x22, - dbiDialogLastPath = 0x23, - dbiRecentEmojis = 0x24, - dbiEmojiVariants = 0x25, - dbiRecentStickers = 0x26, - dbiDcOption = 0x27, - dbiTryIPv6 = 0x28, - dbiSongVolume = 0x29, - dbiWindowsNotifications = 0x30, - dbiIncludeMuted = 0x31, - dbiMegagroupSizeMax = 0x32, - dbiDownloadPath = 0x33, - dbiAutoDownload = 0x34, - dbiSavedGifsLimit = 0x35, - dbiShowingSavedGifs = 0x36, - dbiAutoPlay = 0x37, - dbiAdaptiveForWide = 0x38, - - dbiEncryptedWithSalt = 333, - dbiEncrypted = 444, - - // 500-600 reserved - - dbiVersion = 666, -}; - enum DBISendKey { dbiskEnter = 0, dbiskCtrlEnter = 1, @@ -815,6 +754,15 @@ private: }; +template +class SharedCallback2 { +public: + virtual R call(A1 channel, A2 msgId) const = 0; + virtual ~SharedCallback2() { + } + typedef QSharedPointer > Ptr; +}; + template class FunctionImplementation { public: