mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -05:00
pin/unpin messages in supergroups, local hide for pinned messages
This commit is contained in:
parent
420e82d421
commit
efa5fc443a
24 changed files with 667 additions and 183 deletions
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
|
@ -159,6 +159,9 @@ namespace Global {
|
|||
DeclareVar(int32, SavedGifsLimit);
|
||||
DeclareVar(int32, EditTimeLimit);
|
||||
|
||||
typedef QMap<PeerId, MsgId> HiddenPinnedMessagesMap;
|
||||
DeclareVar(HiddenPinnedMessagesMap, HiddenPinnedMessages);
|
||||
|
||||
};
|
||||
|
||||
namespace Adaptive {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue