pin/unpin messages in supergroups, local hide for pinned messages

This commit is contained in:
John Preston 2016-03-10 13:15:21 +03:00
parent 420e82d421
commit efa5fc443a
24 changed files with 667 additions and 183 deletions

View File

@ -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";

View File

@ -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;

View File

@ -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<HistoryItem*>::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<MTPint>(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<MTPint>(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<MTPint>(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<MTPint>(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<HistoryItem*>::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);
}
}
}

View File

@ -28,9 +28,8 @@ public:
ApiWrap(QObject *parent);
void init();
void itemRemoved(HistoryItem *item);
void requestDependencyItem(HistoryItem *dependent, ChannelData *channel, MsgId id);
typedef SharedCallback2<void, ChannelData*, MsgId> 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<void, ChannelData*, MsgId>::Ptr CallbackPtr;
typedef QList<CallbackPtr> Callbacks;
mtpRequestId req;
QList<HistoryItem*> dependentItems;
Callbacks callbacks;
};
typedef QMap<MsgId, DependencyRequest> DependencyRequests;
DependencyRequests _dependencyRequests;
typedef QMap<ChannelData*, DependencyRequests> ChannelDependencyRequests;
ChannelDependencyRequests _channelDependencyRequests;
SingleTimer _dependencyTimer;
typedef QMap<MsgId, MessageDataRequest> MessageDataRequests;
MessageDataRequests _messageDataRequests;
typedef QMap<ChannelData*, MessageDataRequests> ChannelMessageDataRequests;
ChannelMessageDataRequests _channelMessageDataRequests;
SingleDelayedCall *_messageDataResolveDelayed;
typedef QVector<MTPint> 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);

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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);
};

View File

@ -159,6 +159,9 @@ namespace Global {
DeclareVar(int32, SavedGifsLimit);
DeclareVar(int32, EditTimeLimit);
typedef QMap<PeerId, MsgId> HiddenPinnedMessagesMap;
DeclareVar(HiddenPinnedMessagesMap, HiddenPinnedMessages);
};
namespace Adaptive {

View File

@ -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);
}

View File

@ -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:

View File

@ -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;
};

View File

@ -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<HistoryServicePinned>()->msgId = msg.vreply_to_msg_id.v;
MsgId pinnedMsgId = Get<HistoryServicePinned>()->msgId = msg.vreply_to_msg_id.v;
if (!updatePinned() && App::api()) {
App::api()->requestDependencyItem(this, history->peer->asChannel(), Get<HistoryServicePinned>()->msgId);
App::api()->requestMessageData(history->peer->asChannel(), pinnedMsgId, new HistoryDependentItemCallback(fullId()));
}
}
setMessageByAction(msg.vaction);

View File

@ -908,6 +908,17 @@ struct HistoryMessageForwarded : public BasicInterface<HistoryMessageForwarded>
mutable Text _text;
};
class HistoryDependentItemCallback : public SharedCallback2<void, ChannelData*, MsgId> {
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<HistoryMessageForwarded>() || parent->toHistoryReply() || parent->viaBot();
}

View File

@ -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);
}

View File

@ -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<void, ChannelData*, MsgId> {
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;

View File

@ -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<PeerId, FileKey> DraftsMap;
DraftsMap _draftsMap, _draftCursorsMap;
typedef QMap<PeerId, bool> 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);

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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<MTPint> &_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<MTPDupdateChannelTooLong> {
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<MTPDupdateChannel> {
@ -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<MTPint> &_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));

View File

@ -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<BotInfo> = 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<int> 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;

View File

@ -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;

View File

@ -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;

View File

@ -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 <typename R, typename A1, typename A2>
class SharedCallback2 {
public:
virtual R call(A1 channel, A2 msgId) const = 0;
virtual ~SharedCallback2() {
}
typedef QSharedPointer<SharedCallback2<R, A1, A2> > Ptr;
};
template <typename R>
class FunctionImplementation {
public: