channel edit, report spam, etc done

This commit is contained in:
John Preston 2015-09-21 23:57:42 +03:00
parent f9d3921136
commit 72d0271e4d
39 changed files with 2153 additions and 449 deletions

View File

@ -358,15 +358,20 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_profile_actions_section" = "Actions";
"lng_profile_bot_settings" = "Settings";
"lng_profile_bot_help" = "Help";
"lng_profile_create_public_link" = "Create public link";
"lng_profile_edit_public_link" = "Edit link";
"lng_profile_participants_section" = "Members";
"lng_profile_info" = "Contact info";
"lng_profile_group_info" = "Group info";
"lng_profile_channel_info" = "Channel info";
"lng_profile_add_contact" = "Add Contact";
"lng_profile_edit_contact" = "Edit";
"lng_profile_enable_notifications" = "Notifications";
"lng_profile_clear_history" = "Clear history";
"lng_profile_delete_conversation" = "Delete conversation";
"lng_profile_clear_and_exit" = "Delete and exit";
"lng_profile_leave_channel" = "Leave channel";
"lng_profile_delete_channel" = "Delete channel";
"lng_profile_search_messages" = "Search for messages";
"lng_profile_block_user" = "Block user";
"lng_profile_unblock_user" = "Unblock user";
@ -399,8 +404,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_participant_filter" = "Search";
"lng_participant_invite" = "Invite";
"lng_create_new_group" = "New Group";
"lng_create_new_channel" = "New Channel";
"lng_create_group_back" = "Back";
"lng_create_group_next" = "Next";
"lng_create_group_create" = "Create";
@ -429,17 +432,21 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_failed_add_participant" = "Could not add user. Try again later.";
"lng_failed_add_not_mutual" = "Sorry, if a person left a group, only a\nmutual contact can bring them back\n(they need to have your phone\nnumber, and you need theirs).";
"lng_failed_add_not_mutual_channel" = "Sorry, if a person left a channel, only a\nmutual contact can bring them back\n(they need to have your phone\nnumber, and you need theirs).";
"lng_sure_delete_contact" = "Are you sure, you want to delete {contact} from your contact list?";
"lng_sure_delete_history" = "Are you sure, you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
"lng_sure_delete_group_history" = "Are you sure, you want to delete all message history in «{group}»?\n\nThis action cannot be undone.";
"lng_sure_delete_and_exit" = "Are you sure, you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
"lng_sure_leave_channel" = "Are you sure, you want\nto leave this channel?";
"lng_sure_delete_channel" = "Are you sure, you want\nto delete this channel?\n\nAll members will be removed\nand all messages will be lost.";
"lng_message_empty" = "Empty Message";
"lng_media_unsupported" = "Media Unsupported";
"lng_action_add_user" = "{from} added {user}";
"lng_action_add_you" = "{from} added you to this channel";
"lng_action_you_joined" = "You joined this channel";
"lng_action_kick_user" = "{from} kicked {user}";
"lng_action_user_left" = "{from} left the group";
"lng_action_user_left_channel" = "{from} left the channel";
@ -459,9 +466,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_channel_comments_count" = "{count:_not_used_|# comment|# comments}";
"lng_channel_hide_comments" = "Hide comments";
"lng_channel_not_accessible" = "Sorry, this channel is not accessible.";
"lng_channels_too_much_public_existing" = "Sorry, you have created\ntoo many public channels.\n\nFirst you need to delete one of them.";
"lng_channels_too_much_public" = "Sorry, you have created\ntoo many public channels.\n\nYou can either create a private channel\nor delete one of your public channels first.";
"lng_group_invite_bad_link" = "This invite link is broken\nor has expired.";
"lng_group_invite_want_join" = "Do you want to join the group «{title}»?";
"lng_group_invite_want_join" = "Do you want to join group «{title}»?";
"lng_group_invite_want_join_channel" = "Do you want to join channel «{title}»?";
"lng_group_invite_join" = "Join";
"lng_group_invite_link" = "Invite link";
@ -473,6 +485,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_group_invite_copied" = "Invite link copied to clipboard.";
"lng_group_invite_no_room" = "Unable to join this group because there are\ntoo many members in it already.";
"lng_channel_public_link_copied" = "Public channel link copied to clipboard.";
"lng_forwarded_from" = "Forwarded from";
"lng_in_reply_to" = "In reply to";
@ -526,9 +540,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_report_spam_thanks" = "Thank you for your report!";
"lng_report_spam_sure" = "Are you sure you want\nto report spam from this user?";
"lng_report_spam_sure_group" = "Are you sure you want\nto report spam in this group?";
"lng_report_spam_sure_channel" = "Are you sure you want\nto report spam in this channel?";
"lng_report_spam_ok" = "Report";
"lng_cant_send_to_not_contact" = "Sorry, you can only send messages to\nmutual contacts at the moment. {more_info}";
"lng_cant_invite_not_contact" = "Sorry, you can only add mutual contacts\nto groups at the moment. {more_info}";
"lng_cant_invite_not_contact_channel" = "Sorry, you can only add mutual contacts\nto channels at the moment. {more_info}";
"lng_cant_more_info" = "More info »";
"lng_send_button" = "Send";
@ -542,6 +558,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_from_you" = "You";
"lng_bot_description" = "What can this bot do?";
"lng_unblock_button" = "Unblock";
"lng_channel_join" = "Join Channel";
"lng_channel_mute" = "Mute";
"lng_channel_unmute" = "Unmute";
"lng_open_this_link" = "Open this link?";
"lng_open_link" = "Open";
@ -644,6 +663,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_enter_contact_data" = "New Contact";
"lng_edit_group_title" = "Edit group name";
"lng_edit_contact_title" = "Edit contact name";
"lng_edit_channel_title" = "Edit channel";
"lng_edit_self_title" = "Edit your name";
"lng_confirm_contact_data" = "New Contact";
"lng_add_contact" = "Create";
@ -692,6 +712,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mediaview_files_all" = "View all files";
"lng_mediaview_single_photo" = "Single Photo";
"lng_mediaview_group_photo" = "Group Photo";
"lng_mediaview_channel_photo" = "Channel Photo";
"lng_mediaview_profile_photo" = "Profile Photo";
"lng_mediaview_file_n_of_count" = "{file} {n} of {count}";
"lng_mediaview_n_of_count" = "Photo {n} of {count}";
@ -756,6 +777,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mac_menu_contacts" = "Contacts";
"lng_mac_menu_add_contact" = "Add Contact";
"lng_mac_menu_new_group" = "New Group";
"lng_mac_menu_new_channel" = "New Channel";
"lng_mac_menu_show" = "Show Telegram";
// Keys finished

View File

@ -2036,7 +2036,7 @@ mediaviewLoader: size(78px, 33px);
mediaviewLoaderPoint: size(9px, 9px);
mediaviewLoaderSkip: 9px;
minPhotoSize: 90px;
minPhotoSize: 100px;
maxMediaSize: 420px;
maxStickerSize: 256px;

View File

@ -274,7 +274,17 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
} else {
channel->photoId = 0;
}
channel->about = qs(f.vabout);
channel->count = f.has_participants_count() ? f.vparticipants_count.v : 0;
channel->invitationUrl = (f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString();
if (History *h = App::historyLoaded(channel->id)) {
if (h->inboxReadBefore < f.vread_inbox_max_id.v + 1) {
h->unreadCount = f.vunread_important_count.v;
h->inboxReadBefore = f.vread_inbox_max_id.v + 1;
h->asChannelHistory()->unreadCountAll = f.vunread_count.v;
}
}
channel->fullUpdated();
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
}
@ -400,6 +410,60 @@ bool ApiWrap::gotPeerFailed(PeerData *peer, const RPCError &error) {
return true;
}
void ApiWrap::requestSelfParticipant(ChannelData *channel) {
if (_selfParticipantRequests.contains(channel)) return;
_selfParticipantRequests.insert(channel, MTP::send(MTPchannels_GetParticipant(channel->inputChannel, MTP_inputUserSelf()), rpcDone(&ApiWrap::gotSelfParticipant, channel), rpcFail(&ApiWrap::gotSelfParticipantFail, channel), 0, 5));
}
void ApiWrap::gotSelfParticipant(ChannelData *channel, const MTPchannels_ChannelParticipant &result) {
_selfParticipantRequests.remove(channel);
if (result.type() != mtpc_channels_channelParticipant) {
LOG(("API Error: unknown type in gotSelfParticipant (%1)").arg(result.type()));
channel->inviter = -1;
if (App::main()) App::main()->onSelfParticipantUpdated(channel);
return;
}
const MTPDchannels_channelParticipant &p(result.c_channels_channelParticipant());
App::feedUsers(p.vusers);
switch (p.vparticipant.type()) {
case mtpc_channelParticipantSelf: {
const MTPDchannelParticipantSelf &d(p.vparticipant.c_channelParticipantSelf());
channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate);
} break;
case mtpc_channelParticipantCreator: {
const MTPDchannelParticipantCreator &d(p.vparticipant.c_channelParticipantCreator());
channel->inviter = MTP::authedId();
channel->inviteDate = date(MTP_int(channel->date));
} break;
case mtpc_channelParticipantModerator: {
const MTPDchannelParticipantModerator &d(p.vparticipant.c_channelParticipantModerator());
channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate);
} break;
case mtpc_channelParticipantEditor: {
const MTPDchannelParticipantEditor &d(p.vparticipant.c_channelParticipantEditor());
channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate);
} break;
}
if (App::main()) App::main()->onSelfParticipantUpdated(channel);
}
bool ApiWrap::gotSelfParticipantFail(ChannelData *channel, const RPCError &error) {
if (mtpIsFlood(error)) return false;
if (error.type() == qstr("USER_NOT_PARTICIPANT")) {
channel->inviter = -1;
}
_selfParticipantRequests.remove(channel);
return true;
}
void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) {
if (!_stickerSetRequests.contains(setId)) {
_stickerSetRequests.insert(setId, qMakePair(access, 0));

View File

@ -34,6 +34,8 @@ public:
void requestPeer(PeerData *peer);
void requestPeers(const QList<PeerData*> &peers);
void requestSelfParticipant(ChannelData *channel);
void requestWebPageDelayed(WebPageData *page);
void clearWebPageRequest(WebPageData *page);
void clearWebPageRequests();
@ -83,6 +85,11 @@ private:
bool gotPeerFailed(PeerData *peer, const RPCError &err);
PeerRequests _peerRequests;
void gotSelfParticipant(ChannelData *channel, const MTPchannels_ChannelParticipant &result);
bool gotSelfParticipantFail(ChannelData *channel, const RPCError &error);
typedef QMap<ChannelData*, mtpRequestId> SelfParticipantRequests;
SelfParticipantRequests _selfParticipantRequests;
void gotWebPages(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
typedef QMap<WebPageData*, mtpRequestId> WebPagesPending;
WebPagesPending _webPagesPending;

View File

@ -490,8 +490,8 @@ namespace App {
cdata->setPhoto(d.vphoto);
cdata->date = d.vdate.v;
cdata->count = d.vparticipants_count.v;
cdata->left = (d.vflags.v & MTPDchat_flag_left);
cdata->forbidden = (d.vflags.v & MTPDchat_flag_kicked);
cdata->isForbidden = (d.vflags.v & MTPDchat_flag_kicked);
cdata->haveLeft = (d.vflags.v & MTPDchat_flag_left);
if (cdata->version < d.vversion.v) {
cdata->version = d.vversion.v;
cdata->participants = ChatData::Participants();
@ -510,8 +510,8 @@ namespace App {
cdata->setPhoto(MTP_chatPhotoEmpty());
cdata->date = 0;
cdata->count = -1;
cdata->left = false;
cdata->forbidden = true;
cdata->isForbidden = true;
cdata->haveLeft = false;
} break;
case mtpc_channel: {
const MTPDchannel &d(chat.c_channel());
@ -529,13 +529,9 @@ namespace App {
cdata->access = d.vaccess_hash.v;
cdata->setPhoto(d.vphoto);
cdata->date = d.vdate.v;
cdata->adminned = (d.vflags.v & MTPDchannel_flag_am_admin);
cdata->isBroadcast = (d.vflags.v & MTPDchannel_flag_is_broadcast);
cdata->isPublic = d.has_username();
cdata->flags = d.vflags.v;
cdata->isForbidden = false;
cdata->left = (d.vflags.v & MTPDchannel_flag_have_left);
cdata->forbidden = (d.vflags.v & MTPDchannel_flag_was_kicked);
if (cdata->version < d.vversion.v) {
cdata->version = d.vversion.v;
}
@ -555,14 +551,8 @@ namespace App {
cdata->access = d.vaccess_hash.v;
cdata->setPhoto(MTP_chatPhotoEmpty());
cdata->date = 0;
// cdata->count = -1;
cdata->adminned = false;
cdata->isBroadcast = false;
cdata->isPublic = false;
cdata->left = false;
cdata->forbidden = true;
cdata->count = 0;
cdata->isForbidden = true;
} break;
}
if (!data) continue;
@ -591,7 +581,7 @@ namespace App {
case mtpc_chatParticipants: {
const MTPDchatParticipants &d(p.c_chatParticipants());
chat = App::chat(d.vchat_id.v);
chat->admin = d.vadmin_id.v;
chat->creator = d.vadmin_id.v;
if (!requestBotInfos || chat->version <= d.vversion.v) { // !requestBotInfos is true on getFullChat result
chat->version = d.vversion.v;
const QVector<MTPChatParticipant> &v(d.vparticipants.c_vector().v);
@ -757,7 +747,7 @@ namespace App {
existing->setText(qs(m.vmessage), m.has_entities() ? linksFromMTP(m.ventities.c_vector().v) : LinksInText());
existing->initDimensions();
if (App::main()) App::main()->itemResized(existing);
if (existing->hasTextLinks()) {
if (existing->hasTextLinks() && (!existing->history()->isChannel() || existing->fromChannel())) {
existing->history()->addToOverview(existing, OverviewLinks);
}
}
@ -2369,9 +2359,9 @@ namespace App {
}
}
void searchByHashtag(const QString &tag) {
void searchByHashtag(const QString &tag, PeerData *inPeer) {
if (App::main()) {
App::main()->searchMessages(tag + ' ');
App::main()->searchMessages(tag + ' ', (inPeer && inPeer->isChannel()) ? inPeer : 0);
}
}

View File

@ -253,7 +253,7 @@ namespace App {
void sendBotCommand(const QString &cmd, MsgId replyTo = 0);
void insertBotCommand(const QString &cmd);
void searchByHashtag(const QString &tag);
void searchByHashtag(const QString &tag, PeerData *inPeer);
void openPeerByName(const QString &username, bool toProfile = false, const QString &startToken = QString());
void joinGroupByHash(const QString &hash);
void stickersBox(const QString &name);

View File

@ -499,7 +499,7 @@ void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId)
int32 filesize = 0;
QByteArray data;
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, MTP_audioEmpty(MTP_long(0)), photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false, 0);
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, MTP_audioEmpty(MTP_long(0)), photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false, false, 0);
connect(App::uploader(), SIGNAL(photoReady(const FullMsgId&, const MTPInputFile&)), App::app(), SLOT(photoUpdated(const FullMsgId&, const MTPInputFile&)), Qt::UniqueConnection);

View File

@ -63,7 +63,7 @@ void AddContactBox::initBox() {
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 1 * _firstInput.height() + st::addContactPadding.bottom() + _addButton.height());
} else if (_peer->isChannel()) {
// CHANNELS_UX
_boxTitle = lang(lng_edit_group_title);
_boxTitle = lang(lng_edit_channel_title);
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 1 * _firstInput.height() + st::addContactPadding.bottom() + _addButton.height());
}
} else {
@ -334,3 +334,236 @@ void AddContactBox::onRetry() {
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 3 * _firstInput.height() + 2 * st::addContactDelta + st::addContactPadding.bottom() + _addButton.height());
update();
}
EditChannelBox::EditChannelBox(ChannelData *channel) :
_channel(channel),
_saveButton(this, lang(lng_settings_save), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
_title(this, st::inpAddContact, lang(lng_dlg_new_channel_name), _channel->name),
_descriptionOver(false),
a_descriptionBg(st::newGroupName.bgColor->c, st::newGroupName.bgColor->c),
a_descriptionBorder(st::newGroupName.borderColor->c, st::newGroupName.borderColor->c),
a_description(animFunc(this, &EditChannelBox::descriptionAnimStep)),
_description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about),
_saveTitleRequestId(0), _saveDescriptionRequestId(0) {
_boxTitle = lang(lng_edit_channel_title);
_description.installEventFilter(this);
setMouseTracking(true);
_description.resize(width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::newGroupDescriptionPadding.left() - st::newGroupDescriptionPadding.right(), _title.height() - st::newGroupDescriptionPadding.top() - st::newGroupDescriptionPadding.bottom());
_description.setMinHeight(_description.height());
_description.setMaxHeight(3 * _description.height() + 2 * st::newGroupDescriptionPadding.top() + 2 * st::newGroupDescriptionPadding.bottom());
updateMaxHeight();
_description.setMaxLength(MaxChannelDescription);
connect(&_description, SIGNAL(resized()), this, SLOT(onDescriptionResized()));
connect(&_description, SIGNAL(submitted(bool)), this, SLOT(onSave()));
connect(&_description, SIGNAL(cancelled()), this, SLOT(onClose()));
connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
prepare();
}
void EditChannelBox::hideAll() {
_title.hide();
_description.hide();
_saveButton.hide();
_cancelButton.hide();
}
void EditChannelBox::showAll() {
_title.show();
_description.show();
_saveButton.show();
_cancelButton.show();
}
void EditChannelBox::showDone() {
_title.setFocus();
}
void EditChannelBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
if (_title.hasFocus()) {
onSave();
}
} else {
AbstractBox::keyPressEvent(e);
}
}
void EditChannelBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (paint(p)) return;
paintTitle(p, _boxTitle, true);
QRect descRect(descriptionRect());
if (descRect.intersects(e->rect())) {
p.fillRect(descRect, a_descriptionBg.current());
if (st::newGroupName.borderWidth) {
QBrush b(a_descriptionBorder.current());
p.fillRect(descRect.x(), descRect.y(), descRect.width() - st::newGroupName.borderWidth, st::newGroupName.borderWidth, b);
p.fillRect(descRect.x() + descRect.width() - st::newGroupName.borderWidth, descRect.y(), st::newGroupName.borderWidth, descRect.height() - st::newGroupName.borderWidth, b);
p.fillRect(descRect.x() + st::newGroupName.borderWidth, descRect.y() + descRect.height() - st::newGroupName.borderWidth, descRect.width() - st::newGroupName.borderWidth, st::newGroupName.borderWidth, b);
p.fillRect(descRect.x(), descRect.y() + st::newGroupName.borderWidth, st::newGroupName.borderWidth, descRect.height() - st::newGroupName.borderWidth, b);
}
if (descRect.contains(e->rect())) {
return;
}
}
// paint shadows
p.fillRect(0, size().height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
}
bool EditChannelBox::descriptionAnimStep(float64 ms) {
float dt = ms / st::newGroupName.phDuration;
bool res = true;
if (dt >= 1) {
res = false;
a_descriptionBg.finish();
a_descriptionBorder.finish();
} else {
a_descriptionBg.update(dt, st::newGroupName.phColorFunc);
a_descriptionBorder.update(dt, st::newGroupName.phColorFunc);
}
update(descriptionRect());
return res;
}
void EditChannelBox::onDescriptionResized() {
updateMaxHeight();
update();
}
QRect EditChannelBox::descriptionRect() const {
return rtlrect(_description.x() - st::newGroupDescriptionPadding.left(), _description.y() - st::newGroupDescriptionPadding.top(), _description.width() + st::newGroupDescriptionPadding.left() + st::newGroupDescriptionPadding.right(), _description.height() + st::newGroupDescriptionPadding.top() + st::newGroupDescriptionPadding.bottom(), width());
}
void EditChannelBox::updateMaxHeight() {
int32 h = st::boxTitleHeight + st::newGroupPadding.top() + _title.height() + st::newGroupPadding.bottom() + _saveButton.height();
h += st::newGroupDescriptionSkip + st::newGroupDescriptionPadding.top() + _description.height() + st::newGroupDescriptionPadding.bottom();
setMaxHeight(h);
}
bool EditChannelBox::eventFilter(QObject *obj, QEvent *e) {
if (obj == &_description) {
if (e->type() == QEvent::FocusIn) {
a_descriptionBorder.start(st::newGroupName.borderActive->c);
a_descriptionBg.start(st::newGroupName.bgActive->c);
a_description.start();
} else if (e->type() == QEvent::FocusOut) {
a_descriptionBorder.start(st::newGroupName.borderColor->c);
a_descriptionBg.start(st::newGroupName.bgColor->c);
a_description.start();
}
}
return AbstractBox::eventFilter(obj, e);
}
void EditChannelBox::resizeEvent(QResizeEvent *e) {
_title.resize(width() - st::newGroupPadding.left() - st::newGroupPadding.right(), _title.height());
_title.moveToLeft(st::newGroupPadding.left(), st::boxTitleHeight + st::newGroupPadding.top(), width());
_description.moveToLeft(st::newGroupPadding.left() + st::newGroupDescriptionPadding.left(), _title.y() + _title.height() + st::newGroupDescriptionSkip + st::newGroupDescriptionPadding.top(), width());
int32 buttonTop = _description.y() + _description.height() + st::newGroupDescriptionPadding.bottom();
buttonTop += st::newGroupPadding.bottom();
_cancelButton.move(0, buttonTop);
_saveButton.move(width() - _saveButton.width(), buttonTop);
}
void EditChannelBox::mouseMoveEvent(QMouseEvent *e) {
updateSelected(e->globalPos());
}
void EditChannelBox::updateSelected(const QPoint &cursorGlobalPosition) {
QPoint p(mapFromGlobal(cursorGlobalPosition));
bool descriptionOver = descriptionRect().contains(p);
if (descriptionOver != _descriptionOver) {
_descriptionOver = descriptionOver;
}
setCursor(_descriptionOver ? style::cur_text : style::cur_default);
}
void EditChannelBox::mousePressEvent(QMouseEvent *e) {
mouseMoveEvent(e);
if (_descriptionOver) {
_description.setFocus();
}
}
void EditChannelBox::leaveEvent(QEvent *e) {
updateSelected(QCursor::pos());
}
void EditChannelBox::onSave() {
if (_saveTitleRequestId || _saveDescriptionRequestId) return;
QString title = _title.text().trimmed(), description = _description.getLastText().trimmed();
if (title.isEmpty()) {
_title.setFocus();
_title.notaBene();
return;
}
_sentTitle = title;
_sentDescription = description;
_saveTitleRequestId = MTP::send(MTPchannels_EditTitle(_channel->inputChannel, MTP_string(_sentTitle)), rpcDone(&EditChannelBox::onSaveTitleDone), rpcFail(&EditChannelBox::onSaveFail));
}
void EditChannelBox::saveDescription() {
_saveDescriptionRequestId = MTP::send(MTPchannels_EditAbout(_channel->inputChannel, MTP_string(_sentDescription)), rpcDone(&EditChannelBox::onSaveDescriptionDone), rpcFail(&EditChannelBox::onSaveFail));
}
bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) {
if (mtpIsFlood(error)) return false;
QString err(error.type());
if (req == _saveTitleRequestId) {
_saveTitleRequestId = 0;
if (err == qstr("CHAT_NOT_MODIFIED") || err == qstr("CHAT_TITLE_NOT_MODIFIED")) {
_channel->setName(_sentTitle, _channel->username);
saveDescription();
return true;
} else if (err == qstr("NO_CHAT_TITLE")) {
_title.setFocus();
_title.notaBene();
return true;
} else {
_title.setFocus();
}
} else if (req == _saveDescriptionRequestId) {
_saveDescriptionRequestId = 0;
if (err == qstr("CHAT_ABOUT_NOT_MODIFIED")) {
_channel->about = _sentDescription;
emit App::api()->fullPeerUpdated(_channel);
onClose();
} else {
_description.setFocus();
}
}
return true;
}
void EditChannelBox::onSaveTitleDone(const MTPUpdates &updates) {
_saveTitleRequestId = 0;
App::main()->sentUpdatesReceived(updates);
saveDescription();
}
void EditChannelBox::onSaveDescriptionDone(const MTPBool &result) {
_saveDescriptionRequestId = 0;
_channel->about = _sentDescription;
emit App::api()->fullPeerUpdated(_channel);
onClose();
}

View File

@ -69,3 +69,64 @@ private:
mtpRequestId _addRequest;
QString _sentName;
};
class EditChannelBox : public AbstractBox, public RPCSender {
Q_OBJECT
public:
EditChannelBox(ChannelData *channel);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void leaveEvent(QEvent *e);
bool eventFilter(QObject *obj, QEvent *e);
bool descriptionAnimStep(float64 ms);
void setInnerFocus() {
if (!_description.hasFocus()) {
_title.setFocus();
}
}
public slots:
void onSave();
void onDescriptionResized();
protected:
void hideAll();
void showAll();
void showDone();
private:
QRect descriptionRect() const;
void updateMaxHeight();
void updateSelected(const QPoint &cursorGlobalPosition);
void onSaveTitleDone(const MTPUpdates &updates);
void onSaveDescriptionDone(const MTPBool &result);
bool onSaveFail(const RPCError &e, mtpRequestId req);
void saveDescription();
ChannelData *_channel;
QString _boxTitle;
FlatButton _saveButton, _cancelButton;
FlatInput _title;
bool _descriptionOver;
anim::cvalue a_descriptionBg, a_descriptionBorder;
Animation a_description;
FlatTextarea _description;
mtpRequestId _saveTitleRequestId, _saveDescriptionRequestId;
QString _sentTitle, _sentDescription;
};

View File

@ -77,7 +77,7 @@ _byUsernameSel(-1),
_addContactLnk(this, lang(lng_add_contact_button)) {
DialogsIndexed &v(App::main()->dialogsList());
for (DialogRow *r = v.list.begin; r != v.list.end; r = r->next) {
if (r->history->peer->isChat() && !r->history->peer->asChat()->forbidden && !r->history->peer->asChat()->left) {
if (r->history->peer->isChat() && !r->history->peer->asChat()->isForbidden && !r->history->peer->asChat()->haveLeft) {
_contacts->addToEnd(r->history);
}
}
@ -94,10 +94,10 @@ void ContactsInner::init() {
_filter = qsl("a");
updateFilter();
connect(App::main(), SIGNAL(dialogRowReplaced(DialogRow *, DialogRow *)), this, SLOT(onDialogRowReplaced(DialogRow *, DialogRow *)));
connect(App::main(), SIGNAL(dialogRowReplaced(DialogRow*,DialogRow*)), this, SLOT(onDialogRowReplaced(DialogRow*,DialogRow*)));
connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData *)));
connect(App::main(), SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(onPeerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)));
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(peerUpdated(PeerData *)));
connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)));
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*)));
}
void ContactsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
@ -119,7 +119,7 @@ void ContactsInner::onAddBot() {
void ContactsInner::peerUpdated(PeerData *peer) {
if (_chat && (!peer || peer == _chat)) {
if (_chat->forbidden || _chat->left) {
if (_chat->isForbidden || _chat->haveLeft) {
App::wnd()->hideLayer();
} else if (!_chat->participants.isEmpty() || _chat->count <= 0) {
for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) {
@ -201,7 +201,7 @@ ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) {
data->online = App::onlineText(peer->asUser(), _time);
} else if (peer->isChat()) {
ChatData *chat = peer->asChat();
if (chat->forbidden || chat->left) {
if (chat->isForbidden || chat->haveLeft) {
data->online = lang(lng_chat_status_unaccessible);
} else {
data->online = lng_chat_status_members(lt_count, chat->count);
@ -1096,6 +1096,7 @@ void ContactsBox::peopleReceived(const MTPcontacts_Found &result, mtpRequestId r
switch (result.type()) {
case mtpc_contacts_found: {
App::feedUsers(result.c_contacts_found().vusers);
App::feedChats(result.c_contacts_found().vchats);
_inner.peopleReceived(q, result.c_contacts_found().vresults.c_vector().v);
} break;
}
@ -1390,10 +1391,10 @@ void NewGroupBox::resizeEvent(QResizeEvent *e) {
}
void NewGroupBox::onNext() {
App::wnd()->replaceLayer(new GroupInfoBox(_group.checked() ? CreatingGroupGroup : CreatingGroupChannel));
App::wnd()->replaceLayer(new GroupInfoBox(_group.checked() ? CreatingGroupGroup : CreatingGroupChannel, true));
}
GroupInfoBox::GroupInfoBox(CreatingGroupType creating) : AbstractBox(),
GroupInfoBox::GroupInfoBox(CreatingGroupType creating, bool fromTypeChoose) : AbstractBox(),
_creating(creating),
a_photoOver(0, 0),
a_photo(animFunc(this, &GroupInfoBox::photoAnimStep)),
@ -1406,7 +1407,7 @@ _name(this, st::newGroupName, lang(_creating == CreatingGroupChannel ? lng_dlg_n
_photo(this, lang(lng_create_group_photo), st::newGroupPhoto),
_description(this, st::newGroupDescription, lang(lng_create_group_description)),
_next(this, lang(_creating == CreatingGroupChannel ? lng_create_group_create : lng_create_group_next), st::btnSelectDone),
_cancel(this, lang(lng_create_group_back), st::btnSelectCancel),
_cancel(this, lang(fromTypeChoose ? lng_create_group_back : lng_cancel), st::btnSelectCancel),
_creationRequestId(0), _createdChannel(0) {
setMouseTracking(true);
@ -1420,6 +1421,7 @@ _creationRequestId(0), _createdChannel(0) {
connect(&_description, SIGNAL(resized()), this, SLOT(onDescriptionResized()));
connect(&_description, SIGNAL(submitted(bool)), this, SLOT(onNext()));
connect(&_description, SIGNAL(cancelled()), this, SLOT(onClose()));
_description.installEventFilter(this);
connect(&_photo, SIGNAL(clicked()), this, SLOT(onPhoto()));
@ -1523,7 +1525,6 @@ void GroupInfoBox::resizeEvent(QResizeEvent *e) {
_photo.moveToLeft(_name.x(), _name.y() + st::newGroupPhotoSize - _photo.height(), width());
_description.moveToLeft(st::newGroupPadding.left() + st::newGroupDescriptionPadding.left(), _photo.y() + _photo.height() + st::newGroupDescriptionSkip + st::newGroupDescriptionPadding.top(), width());
_description.installEventFilter(this);
int32 buttonTop = (_creating == CreatingGroupChannel) ? (_description.y() + _description.height() + st::newGroupDescriptionPadding.bottom()) : (_photo.y() + _photo.height());
buttonTop += st::newGroupPadding.bottom();
@ -1646,7 +1647,7 @@ bool GroupInfoBox::creationFail(const RPCError &error) {
_name.notaBene();
return true;
} else if (error.type() == "PEER_FLOOD") {
App::wnd()->replaceLayer(new ConfirmBox(lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)))));
App::wnd()->replaceLayer(new ConfirmBox(lng_cant_invite_not_contact_channel(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)))));
return true;
}
return false;
@ -1715,8 +1716,9 @@ void GroupInfoBox::onPhotoReady(const QImage &img) {
_photoSmall.setDevicePixelRatio(cRetinaFactor());
}
SetupChannelBox::SetupChannelBox(ChannelData *channel) : AbstractBox(),
SetupChannelBox::SetupChannelBox(ChannelData *channel, bool existing) : AbstractBox(),
_channel(channel),
_existing(existing),
_public(this, qsl("channel_privacy"), 0, lang(lng_create_public_channel_title), true),
_private(this, qsl("channel_privacy"), 1, lang(lng_create_private_channel_title)),
_comments(this, lang(lng_create_channel_comments), false),
@ -1725,14 +1727,17 @@ _aboutPublic(st::normalFont, lang(lng_create_public_channel_about), _defaultOpti
_aboutPrivate(st::normalFont, lang(lng_create_private_channel_about), _defaultOptions, _aboutPublicWidth),
_aboutComments(st::normalFont, lang(lng_create_channel_comments_about), _defaultOptions, _aboutPublicWidth),
_linkPlaceholder(qsl("telegram.me/")),
_link(this, st::newGroupLink, QString()),
_link(this, st::newGroupLink, QString(), channel->username),
_linkOver(false),
_save(this, lang(lng_create_group_save), st::btnSelectDone),
_skip(this, lang(lng_create_group_skip), st::btnSelectCancel),
_skip(this, lang(existing ? lng_cancel : lng_create_group_skip), st::btnSelectCancel),
_tooMuchUsernames(false),
_saveRequestId(0), _checkRequestId(0),
a_goodOpacity(0, 0), a_good(animFunc(this, &SetupChannelBox::goodAnimStep)) {
setMouseTracking(true);
_checkRequestId = MTP::send(MTPchannels_CheckUsername(_channel->inputChannel, MTP_string("preston")), RPCDoneHandlerPtr(), rpcFail(&SetupChannelBox::onFirstCheckFail));
_link.setTextMargin(style::margins(st::newGroupLink.textMrg.left() + st::newGroupLink.font->m.width(_linkPlaceholder), st::newGroupLink.textMrg.top(), st::newGroupLink.textMrg.right(), st::newGroupLink.textMrg.bottom()));
_aboutPublicHeight = _aboutPublic.countHeight(_aboutPublicWidth);
@ -1902,15 +1907,22 @@ bool SetupChannelBox::goodAnimStep(float64 ms) {
}
void SetupChannelBox::closePressed() {
App::wnd()->showLayer(new ContactsBox(_channel), true);
if (!_existing) {
App::wnd()->showLayer(new ContactsBox(_channel), true);
}
}
void SetupChannelBox::onSave() {
if (!_public.checked()) {
if (_comments.checked()) {
if (!_existing && !_comments.isHidden() && _comments.checked()) {
MTP::send(MTPchannels_ToggleComments(_channel->inputChannel, MTP_bool(true)));
}
onClose();
if (_existing) {
_sentUsername = QString();
_saveRequestId = MTP::send(MTPchannels_UpdateUsername(_channel->inputChannel, MTP_string(_sentUsername)), rpcDone(&SetupChannelBox::onUpdateDone), rpcFail(&SetupChannelBox::onUpdateFail));
} else {
onClose();
}
}
if (_saveRequestId) return;
@ -1922,7 +1934,7 @@ void SetupChannelBox::onSave() {
return;
}
if (_comments.checked()) {
if (!_existing && !_comments.isHidden() && _comments.checked()) {
MTP::send(MTPchannels_ToggleComments(_channel->inputChannel, MTP_bool(true)), RPCResponseHandler(), 0, 5);
}
_sentUsername = link;
@ -1979,6 +1991,11 @@ void SetupChannelBox::onCheck() {
void SetupChannelBox::onPrivacyChange() {
if (_public.checked()) {
if (_tooMuchUsernames) {
_private.setChecked(true);
App::wnd()->replaceLayer(new ConfirmBox(lang(lng_channels_too_much_public)));
return;
}
_link.show();
_link.setFocus();
} else {
@ -2035,7 +2052,17 @@ bool SetupChannelBox::onCheckFail(const RPCError &error) {
_checkRequestId = 0;
QString err(error.type());
if (err == "USERNAME_INVALID") {
if (err == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH") {
if (_existing) {
App::wnd()->hideLayer(true);
App::wnd()->showLayer(new ConfirmBox(lang(lng_channels_too_much_public_existing)), true);
} else {
_tooMuchUsernames = true;
_private.setChecked(true);
onPrivacyChange();
}
return true;
} else if (err == "USERNAME_INVALID") {
_errorText = lang(lng_create_channel_link_invalid);
update();
return true;
@ -2048,3 +2075,24 @@ bool SetupChannelBox::onCheckFail(const RPCError &error) {
_link.setFocus();
return true;
}
bool SetupChannelBox::onFirstCheckFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_checkRequestId = 0;
QString err(error.type());
if (err == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH") {
if (_existing) {
App::wnd()->hideLayer(true);
App::wnd()->showLayer(new ConfirmBox(lang(lng_channels_too_much_public_existing)), true);
} else {
_tooMuchUsernames = true;
_private.setChecked(true);
onPrivacyChange();
}
return true;
}
_goodText = QString();
_link.setFocus();
return true;
}

View File

@ -245,7 +245,7 @@ class GroupInfoBox : public AbstractBox, public RPCSender {
public:
GroupInfoBox(CreatingGroupType creating);
GroupInfoBox(CreatingGroupType creating, bool fromTypeChoose);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
@ -313,7 +313,7 @@ class SetupChannelBox : public AbstractBox, public RPCSender {
public:
SetupChannelBox(ChannelData *channel);
SetupChannelBox(ChannelData *channel, bool existing = false);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
@ -350,6 +350,7 @@ private:
bool goodAnimStep(float64 ms);
ChannelData *_channel;
bool _existing;
FlatRadiobutton _public, _private;
FlatCheckbox _comments;
@ -366,6 +367,9 @@ private:
void onCheckDone(const MTPBool &result);
bool onCheckFail(const RPCError &error);
bool onFirstCheckFail(const RPCError &error);
bool _tooMuchUsernames;
mtpRequestId _saveRequestId, _checkRequestId;
QString _sentUsername, _checkUsername, _errorText, _goodText;

View File

@ -335,6 +335,9 @@ enum {
UpdateChunk = 100 * 1024, // 100kb parts when downloading the update
IdleMsecs = 60 * 1000, // after 60secs without user input we think we are idle
UpdateFullChannelTimeout = 5000, // not more than once in 5 seconds
SendViewsTimeout = 1000, // send views each second
ForwardOnAdd = 100, // how many messages from chat history server should forward to user, that was added to this chat
};

View File

@ -419,7 +419,7 @@ void DialogsListWidget::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow
}
}
void DialogsListWidget::createDialogAtTop(History *history, int32 unreadCount) {
void DialogsListWidget::createDialog(History *history) {
if (history->dialogs.isEmpty()) {
History::DialogLinks links = dialogs.addToEnd(history);
int32 movedFrom = links[0]->pos * st::dlgHeight;
@ -781,6 +781,13 @@ void DialogsListWidget::dialogsReceived(const QVector<MTPDialog> &added) {
if (history->peer->isChannel()) {
history->asChannelHistory()->unreadCountAll = d.vunread_count.v;
history->peer->asChannel()->ptsReceived(d.vpts.v);
if (!history->peer->asChannel()->amCreator()) {
if (HistoryItem *top = App::histItemById(history->channelId(), d.vtop_important_message.v)) {
if (top->date <= date(history->peer->asChannel()->date)) {
App::api()->requestSelfParticipant(history->peer->asChannel());
}
}
}
}
if (d.vtop_message.v > d.vtop_important_message.v) {
history->setNotLoadedAtBottom();
@ -1530,8 +1537,8 @@ void DialogsWidget::activate() {
list.activate();
}
void DialogsWidget::createDialogAtTop(History *history, int32 unreadCount) {
list.createDialogAtTop(history, unreadCount);
void DialogsWidget::createDialog(History *history) {
list.createDialog(history);
}
void DialogsWidget::dlgUpdated(DialogRow *row) {
@ -1778,11 +1785,15 @@ void DialogsWidget::onChooseByDrag() {
list.choosePeer();
}
void DialogsWidget::searchMessages(const QString &query) {
if (_filter.getLastText() != query) {
void DialogsWidget::searchMessages(const QString &query, PeerData *inPeer) {
if ((_filter.getLastText() != query) || (inPeer && inPeer != _searchInPeer)) {
if (inPeer) {
_searchInPeer = inPeer;
list.searchInPeer(inPeer);
}
_filter.setText(query);
_filter.updatePlaceholder();
onFilterUpdate();
onFilterUpdate(true);
_searchTimer.stop();
onSearchMessages();
@ -1905,6 +1916,7 @@ void DialogsWidget::peopleReceived(const MTPcontacts_Found &result, mtpRequestId
switch (result.type()) {
case mtpc_contacts_found: {
App::feedUsers(result.c_contacts_found().vusers);
App::feedChats(result.c_contacts_found().vchats);
list.peopleReceived(q, result.c_contacts_found().vresults.c_vector().v);
} break;
}

View File

@ -55,7 +55,7 @@ public:
void selectSkip(int32 direction);
void selectSkipPage(int32 pixels, int32 direction);
void createDialogAtTop(History *history, int32 unreadCount);
void createDialog(History *history);
void moveDialogToTop(const History::DialogLinks &links);
void dlgUpdated(DialogRow *row);
void dlgUpdated(History *row);
@ -194,7 +194,7 @@ public:
void searchInPeer(PeerData *peer);
void loadDialogs();
void createDialogAtTop(History *history, int32 unreadCount);
void createDialog(History *history);
void dlgUpdated(DialogRow *row);
void dlgUpdated(History *row);
@ -217,7 +217,7 @@ public:
void enableShadow(bool enable = true);
void searchMessages(const QString &query);
void searchMessages(const QString &query, PeerData *inPeer = 0);
void onSearchMore(MsgId minMsgId);
void itemRemoved(HistoryItem *item);

View File

@ -21,11 +21,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "flattextarea.h"
#include "window.h"
FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v) : QTextEdit(v, parent),
FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v) : QTextEdit(QString(), parent),
_minHeight(-1), _maxHeight(-1), _maxLength(-1), _ctrlEnterSubmit(true),
_ph(pholder), _oldtext(v), _phVisible(!v.length()),
_oldtext(v), _phVisible(!v.length()),
a_phLeft(_phVisible ? 0 : st.phShift), a_phAlpha(_phVisible ? 1 : 0), a_phColor(st.phColor->c),
_st(st), _undoAvailable(false), _redoAvailable(false), _inDrop(false), _fakeMargin(0),
_st(st), _undoAvailable(false), _redoAvailable(false), _inDrop(false), _inHeightCheck(false), _fakeMargin(0),
_touchPress(false), _touchRightButton(false), _touchMove(false), _replacingEmojis(false) {
setAcceptRichText(false);
resize(_st.width, _st.font->height);
@ -33,7 +33,7 @@ _touchPress(false), _touchRightButton(false), _touchMove(false), _replacingEmoji
setFont(_st.font->f);
setAlignment(_st.align);
_phelided = _st.font->m.elidedText(_ph, Qt::ElideRight, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1);
setPlaceholder(pholder);
QPalette p(palette());
p.setColor(QPalette::Text, _st.textColor->c);
@ -63,6 +63,10 @@ _touchPress(false), _touchRightButton(false), _touchMove(false), _replacingEmoji
connect(this, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool)));
connect(this, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool)));
if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu()));
if (!v.isEmpty()) {
setPlainText(v);
}
}
void FlatTextarea::setMaxLength(int32 maxLength) {
@ -80,7 +84,11 @@ void FlatTextarea::setMaxHeight(int32 maxHeight) {
}
bool FlatTextarea::heightAutoupdated() {
if (_minHeight < 0 || _maxHeight < 0) return false;
if (_minHeight < 0 || _maxHeight < 0 || _inHeightCheck) return false;
_inHeightCheck = true;
myEnsureResized(this);
int newh = ceil(document()->size().height()) + 2 * fakeMargin();
if (newh > _maxHeight) {
newh = _maxHeight;
@ -794,6 +802,12 @@ const QString &FlatTextarea::getLastText() const {
return _oldtext;
}
void FlatTextarea::setPlaceholder(const QString &ph) {
_ph = ph;
_phelided = _st.font->m.elidedText(_ph, Qt::ElideRight, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1);
if (_phVisible) update();
}
void FlatTextarea::updatePlaceholder() {
bool vis = getLastText().isEmpty();
if (vis == _phVisible) return;

View File

@ -44,6 +44,7 @@ public:
void setMaxHeight(int32 maxHeight);
const QString &getLastText() const;
void setPlaceholder(const QString &ph);
void updatePlaceholder();
QRect getTextRect() const;
@ -118,7 +119,7 @@ private:
anim::cvalue a_phColor;
style::flatTextarea _st;
bool _undoAvailable, _redoAvailable, _inDrop;
bool _undoAvailable, _redoAvailable, _inDrop, _inHeightCheck;
int32 _fakeMargin;

View File

@ -847,7 +847,7 @@ void MentionLink::onClick(Qt::MouseButton button) const {
void HashtagLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
App::searchByHashtag(_tag);
App::searchByHashtag(_tag, App::mousedItem() ? App::mousedItem()->history()->peer : 0);
}
}

View File

@ -385,7 +385,7 @@ bool History::updateTyping(uint64 ms, uint32 dots, bool force) {
newTypingStr += qsl("...");
}
if (typingStr != newTypingStr) {
typingText.setText(st::dlgHistFont, (typingStr = newTypingStr), _textNameOptions);
typingText.setText(st::dlgHistFont, (typingStr = newTypingStr), _textNameOptions);
}
}
if (!typingStr.isEmpty()) {
@ -422,7 +422,7 @@ ChannelHistory::ChannelHistory(const PeerId &peer) : History(peer),
unreadCountAll(0),
_onlyImportant(true),
_otherOldLoaded(false), _otherNewLoaded(true),
_collapse(0) {
_collapseMessage(0), _joinedMessage(0) {
}
bool ChannelHistory::isSwitchReadyFor(MsgId switchId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop) {
@ -533,14 +533,14 @@ void ChannelHistory::insertCollapseItem(MsgId wasMinId) {
if (_onlyImportant) return;
bool insertAfter = false;
for (int32 blockIndex = 0, blocksCount = blocks.size(); blockIndex < blocksCount; ++blockIndex) {
for (int32 blockIndex = 1, blocksCount = blocks.size(); blockIndex < blocksCount; ++blockIndex) { // skip first date block
HistoryBlock *block = blocks.at(blockIndex);
for (int32 itemIndex = 0, itemsCount = block->items.size(); itemIndex < itemsCount; ++itemIndex) {
HistoryItem *item = block->items.at(itemIndex);
if (insertAfter || item->id > wasMinId || (item->id == wasMinId && !item->isImportant())) {
_collapse = new HistoryCollapse(this, block, wasMinId, item->date);
if (!addNewInTheMiddle(_collapse, blockIndex, itemIndex)) {
_collapse = 0;
_collapseMessage = new HistoryCollapse(this, block, wasMinId, item->date);
if (!addNewInTheMiddle(_collapseMessage, blockIndex, itemIndex)) {
_collapseMessage = 0;
}
return;
} else if (item->id == wasMinId && item->isImportant()) {
@ -550,6 +550,296 @@ void ChannelHistory::insertCollapseItem(MsgId wasMinId) {
}
}
void ChannelHistory::getRangeDifference() {
MsgId fromId = 0, toId = 0;
for (int32 blockIndex = 0, blocksCount = blocks.size(); blockIndex < blocksCount; ++blockIndex) {
HistoryBlock *block = blocks.at(blockIndex);
for (int32 itemIndex = 0, itemsCount = block->items.size(); itemIndex < itemsCount; ++itemIndex) {
HistoryItem *item = block->items.at(itemIndex);
if (item->type() == HistoryItemMsg && item->id > 0) {
fromId = item->id;
break;
} else if (item->type() == HistoryItemGroup) {
fromId = static_cast<HistoryGroup*>(item)->minId() + 1;
break;
}
}
if (fromId) break;
}
if (!fromId) return;
for (int32 blockIndex = blocks.size(); blockIndex > 0;) {
HistoryBlock *block = blocks.at(--blockIndex);
for (int32 itemIndex = block->items.size(); itemIndex > 0;) {
HistoryItem *item = block->items.at(--itemIndex);
if (item->type() == HistoryItemMsg && item->id > 0) {
toId = item->id;
break;
} else if (item->type() == HistoryItemGroup) {
toId = static_cast<HistoryGroup*>(item)->maxId() - 1;
break;
}
}
if (toId) break;
}
if (fromId > 0 && peer->asChannel()->pts() > 0) {
if (_rangeDifferenceRequestId) {
MTP::cancel(_rangeDifferenceRequestId);
}
_rangeDifferenceFromId = fromId;
_rangeDifferenceToId = toId;
MTP_LOG(0, ("getChannelDifference { good - after channelDifferenceTooLong was received, validating history part }%1").arg(cTestMode() ? " TESTMODE" : ""));
getRangeDifferenceNext(peer->asChannel()->pts());
}
}
void ChannelHistory::getRangeDifferenceNext(int32 pts) {
if (!App::main() || _rangeDifferenceToId < _rangeDifferenceFromId) return;
int32 limit = _rangeDifferenceToId + 1 - _rangeDifferenceFromId;
_rangeDifferenceRequestId = MTP::send(MTPupdates_GetChannelDifference(peer->asChannel()->inputChannel, MTP_channelMessagesFilter(MTP_int(0), MTP_vector<MTPMessageRange>(1, MTP_messageRange(MTP_int(_rangeDifferenceFromId), MTP_int(_rangeDifferenceToId)))), MTP_int(pts), MTP_int(limit)), App::main()->rpcDone(&MainWidget::gotRangeDifference, peer->asChannel()));
}
void ChannelHistory::addNewGroup(const MTPMessageGroup &group) {
if (group.type() != mtpc_messageGroup) return;
const MTPDmessageGroup &d(group.c_messageGroup());
if (onlyImportant()) {
_otherNewLoaded = false;
} else if (_otherNewLoaded) {
if (_otherList.isEmpty() || _otherList.back()->type() != HistoryItemGroup) {
_otherList.push_back(regItem(new HistoryGroup(this, 0, d, _otherList.isEmpty() ? date(d.vdate) : _otherList.back()->date)));
} else {
static_cast<HistoryGroup*>(_otherList.back())->uniteWith(d.vmin_id.v, d.vmax_id.v, d.vcount.v);
}
}
if (onlyImportant()) {
if (newLoaded) {
HistoryItem *prev = blocks.isEmpty() ? 0 : blocks.back()->items.back();
HistoryBlock *to = 0;
bool newBlock = blocks.isEmpty();
if (newBlock) {
to = new HistoryBlock(this);
to->y = height;
} else {
to = blocks.back();
height -= to->height;
}
prev = addMessageGroupAfterPrevToBlock(d, prev, to);
height += to->height;
if (newBlock) {
HistoryBlock *dateBlock = new HistoryBlock(this);
HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, blocks.front()->items.front()->date);
dateBlock->items.push_back(dayItem);
int32 dh = dayItem->resize(width);
dateBlock->height = dh;
for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) {
(*i)->y += dh;
}
blocks.push_front(dateBlock); // date block
height += dh;
}
}
} else {
setNotLoadedAtBottom();
}
}
HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) {
if (_joinedMessage || peer->asChannel()->haveLeft() || peer->asChannel()->wasKicked()) return _joinedMessage;
UserData *inviter = (peer->asChannel()->inviter > 0) ? App::userLoaded(peer->asChannel()->inviter) : 0;
if (!inviter) return 0;
int32 flags = (unread ? MTPDmessage_flag_unread : 0);
QDateTime inviteDate = peer->asChannel()->inviteDate;
if (unread) _maxReadMessageDate = inviteDate;
if (isEmpty()) {
HistoryBlock *to = new HistoryBlock(this);
bool newBlock = true;
_joinedMessage = new HistoryJoined(this, to, inviteDate, inviter, flags);
if (!addNewItem(to, newBlock, regItem(_joinedMessage), unread)) {
_joinedMessage = 0;
}
return _joinedMessage;
}
HistoryItem *lastSeenDateItem = 0;
for (int32 blockIndex = blocks.size(); blockIndex > 1;) {
HistoryBlock *block = blocks.at(--blockIndex);
for (int32 itemIndex = block->items.size(); itemIndex > 0;) {
HistoryItem *item = block->items.at(--itemIndex);
HistoryItemType type = item->type();
if (type == HistoryItemMsg || type == HistoryItemGroup) {
if (item->date <= inviteDate) {
++itemIndex;
if (item->date.date() != inviteDate.date()) {
HistoryDateMsg *joinedDateItem = new HistoryDateMsg(this, block, inviteDate.date());
if (addNewInTheMiddle(joinedDateItem, blockIndex, itemIndex)) {
++itemIndex;
}
}
_joinedMessage = new HistoryJoined(this, block, inviteDate, inviter, flags);
if (!addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex)) {
_joinedMessage = 0;
}
if (lastSeenDateItem && lastSeenDateItem->date.date() == inviteDate.date()) {
lastSeenDateItem->destroy();
}
if (!lastMsgDate.isNull() && inviteDate >= lastMsgDate) {
setLastMessage(_joinedMessage);
if (unread) {
newItemAdded(_joinedMessage);
}
}
return _joinedMessage;
} else {
lastSeenDateItem = 0;
}
} else if (type == HistoryItemDate) {
lastSeenDateItem = item;
}
}
}
// adding new item to new block
int32 addToH = 0, skip = 0;
if (!blocks.isEmpty()) { // remove date block
if (width) addToH = -blocks.front()->height;
delete blocks.front();
blocks.pop_front();
}
HistoryItem *till = blocks.isEmpty() ? 0 : blocks.front()->items.front();
HistoryBlock *block = new HistoryBlock(this);
_joinedMessage = new HistoryJoined(this, block, inviteDate, inviter, flags);
if (regItem(_joinedMessage)) {
addItemAfterPrevToBlock(_joinedMessage, 0, block);
} else {
_joinedMessage = 0;
}
if (till && _joinedMessage && inviteDate.date() != till->date.date()) {
HistoryItem *dayItem = createDayServiceMsg(this, block, till->date);
block->items.push_back(dayItem);
if (width) {
dayItem->y = block->height;
block->height += dayItem->resize(width);
}
}
if (!block->items.isEmpty()) {
blocks.push_front(block);
if (width) {
addToH += block->height;
++skip;
}
} else {
delete block;
}
if (!blocks.isEmpty()) {
HistoryBlock *dateBlock = new HistoryBlock(this);
HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, blocks.front()->items.front()->date);
dateBlock->items.push_back(dayItem);
if (width) {
int32 dh = dayItem->resize(width);
dateBlock->height = dh;
if (skip) {
blocks.front()->y += dh;
}
addToH += dh;
++skip;
}
blocks.push_front(dateBlock); // date block
}
if (width && addToH) {
for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) {
if (skip) {
--skip;
} else {
(*i)->y += addToH;
}
}
height += addToH;
}
if (!lastMsgDate.isNull() && inviteDate >= lastMsgDate) {
setLastMessage(_joinedMessage);
if (unread) {
newItemAdded(_joinedMessage);
}
}
return _joinedMessage;
}
void ChannelHistory::checkJoinedMessage() {
if (_joinedMessage || peer->asChannel()->inviter <= 0) return;
if (isEmpty()) {
if (loadedAtTop() && loadedAtBottom()) {
if (insertJoinedMessage(false)) {
setLastMessage(_joinedMessage);
}
return;
}
}
QDateTime inviteDate = peer->asChannel()->inviteDate;
QDateTime firstDate, lastDate;
for (int32 blockIndex = 1, blocksCount = blocks.size(); blockIndex < blocksCount; ++blockIndex) {
HistoryBlock *block = blocks.at(blockIndex);
int32 itemIndex = 0, itemsCount = block->items.size();
for (; itemIndex < itemsCount; ++itemIndex) {
HistoryItem *item = block->items.at(itemIndex);
HistoryItemType type = item->type();
if (type == HistoryItemMsg || type == HistoryItemGroup) {
firstDate = item->date;
break;
}
}
if (itemIndex < itemsCount) break;
}
for (int32 blockIndex = blocks.size(); blockIndex > 1;) {
HistoryBlock *block = blocks.at(--blockIndex);
int32 itemIndex = block->items.size();
for (; itemIndex > 0;) {
HistoryItem *item = block->items.at(--itemIndex);
HistoryItemType type = item->type();
if (type == HistoryItemMsg || type == HistoryItemGroup) {
lastDate = item->date;
++itemIndex;
break;
}
}
if (itemIndex) break;
}
if (!firstDate.isNull() && !lastDate.isNull() && (firstDate <= inviteDate || loadedAtTop()) && (lastDate > inviteDate || loadedAtBottom())) {
if (insertJoinedMessage(false) && inviteDate >= lastDate) {
setLastMessage(_joinedMessage);
}
}
}
void ChannelHistory::checkMaxReadMessageDate() {
if (_maxReadMessageDate.isValid()) return;
for (int32 blockIndex = blocks.size(); blockIndex > 0;) {
HistoryBlock *block = blocks.at(--blockIndex);
for (int32 itemIndex = block->items.size(); itemIndex > 0;) {
HistoryItem *item = block->items.at(--itemIndex);
if (item->isImportant() && !item->unread()) {
_maxReadMessageDate = item->date;
return;
}
}
}
if (loadedAtTop()) {
_maxReadMessageDate = date(MTP_int(peer->asChannel()->date));
}
}
const QDateTime &ChannelHistory::maxReadMessageDate() {
return _maxReadMessageDate;
}
HistoryItem *ChannelHistory::addNewChannelMessage(const MTPMessage &msg, NewMessageType type) {
if (type == NewMessageExisting) return addToHistory(msg);
@ -559,8 +849,7 @@ HistoryItem *ChannelHistory::addNewChannelMessage(const MTPMessage &msg, NewMess
}
HistoryItem *ChannelHistory::addNewToBlocks(const MTPMessage &msg, NewMessageType type) {
int32 flags = flagsFromMessage(msg);
bool isImportant = isChannel() ? isImportantChannelMessage(flags) : true;
bool isImportant = isChannel() ? isImportantChannelMessage(idFromMessage(msg), flagsFromMessage(msg)) : true;
if (!loadedAtBottom()) {
HistoryItem *item = addToHistory(msg);
@ -665,6 +954,13 @@ void ChannelHistory::switchMode() {
_onlyImportant = !_onlyImportant;
lastWidth = 0;
checkJoinedMessage();
}
void ChannelHistory::cleared() {
_collapseMessage = 0;
_joinedMessage = 0;
}
HistoryGroup *ChannelHistory::findGroup(MsgId msgId) const { // find message group using binary search
@ -790,8 +1086,10 @@ HistoryItem *ChannelHistory::findPrevItem(HistoryItem *item) const {
}
void ChannelHistory::messageDetached(HistoryItem *msg) {
if (_collapse == msg) {
_collapse = 0;
if (_collapseMessage == msg) {
_collapseMessage = 0;
} else if (_joinedMessage == msg) {
_joinedMessage = 0;
}
}
@ -1005,15 +1303,14 @@ void Histories::remove(const PeerId &peer) {
}
}
HistoryItem *Histories::addNewMessage(const MTPmessage &msg, NewMessageType type) {
int32 flags = 0;
HistoryItem *Histories::addNewMessage(const MTPMessage &msg, NewMessageType type) {
PeerId peer = peerFromMessage(msg);
if (!peer) return 0;
return findOrInsert(peer, 0, 0)->addNewMessage(msg, type);
}
HistoryItem *History::createItem(HistoryBlock *block, const MTPmessage &msg, bool applyServiceAction, bool returnExisting) {
HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, bool applyServiceAction, bool returnExisting) {
HistoryItem *result = 0;
MsgId msgId = 0;
@ -1227,7 +1524,7 @@ HistoryItem *History::addNewService(MsgId msgId, QDateTime date, const QString &
return addNewItem(to, newBlock, regItem(new HistoryServiceMsg(this, to, msgId, date, text, flags, media)), newMsg);
}
HistoryItem *History::addNewMessage(const MTPmessage &msg, NewMessageType type) {
HistoryItem *History::addNewMessage(const MTPMessage &msg, NewMessageType type) {
if (isChannel()) return asChannelHistory()->addNewChannelMessage(msg, type);
if (type == NewMessageExisting) return addToHistory(msg);
@ -1252,7 +1549,7 @@ HistoryItem *History::addNewMessage(const MTPmessage &msg, NewMessageType type)
return addNewItem(to, newBlock, createItem(to, msg, (type == NewMessageUnread)), (type == NewMessageUnread));
}
HistoryItem *History::addToHistory(const MTPmessage &msg) {
HistoryItem *History::addToHistory(const MTPMessage &msg) {
return createItem(0, msg, false, true);
}
@ -1345,20 +1642,22 @@ HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *a
newItemAdded(adding);
}
HistoryMedia *media = adding->getMedia(true);
if (media) {
HistoryMediaType mt = media->type();
MediaOverviewType t = mediaToOverviewType(mt);
if (t != OverviewCount) {
if (mt == MediaTypeDocument && static_cast<HistoryDocument*>(media)->document()->song()) {
addToOverview(adding, OverviewAudioDocuments);
} else {
addToOverview(adding, t);
if (!isChannel() || adding->fromChannel()) {
HistoryMedia *media = adding->getMedia(true);
if (media) {
HistoryMediaType mt = media->type();
MediaOverviewType t = mediaToOverviewType(mt);
if (t != OverviewCount) {
if (mt == MediaTypeDocument && static_cast<HistoryDocument*>(media)->document()->song()) {
addToOverview(adding, OverviewAudioDocuments);
} else {
addToOverview(adding, t);
}
}
}
}
if (adding->hasTextLinks()) {
addToOverview(adding, OverviewLinks);
if (adding->hasTextLinks()) {
addToOverview(adding, OverviewLinks);
}
}
if (adding->from()->id) {
if (peer->isChat() && adding->from()->isUser()) {
@ -1374,7 +1673,7 @@ HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *a
if (adding->hasReplyMarkup()) {
int32 markupFlags = App::replyMarkup(channelId(), adding->id).flags;
if (!(markupFlags & MTPDreplyKeyboardMarkup_flag_personal) || adding->notifyByFrom()) {
if (peer->isChat()) { // CHANNELS_UX
if (peer->isChat()) {
peer->asChat()->markupSenders.insert(adding->from(), true);
}
if (markupFlags & MTPDreplyKeyboardMarkup_flag_ZERO) { // zero markup means replyKeyboardHide
@ -1392,6 +1691,7 @@ HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *a
}
}
}
return adding;
}
@ -1465,7 +1765,7 @@ HistoryItem *History::addMessageGroupAfterPrev(HistoryItem *newItem, HistoryItem
}
QDateTime date = prev ? prev->date : newItem->date;
HistoryBlock *block = prev->block();
HistoryBlock *block = prev ? prev->block() : 0;
if (!block) {
createInitialDateBlock(date);
@ -1479,11 +1779,16 @@ HistoryItem *History::addMessageGroupAfterPrev(HistoryItem *newItem, HistoryItem
void History::addOlderSlice(const QVector<MTPMessage> &slice, const QVector<MTPMessageGroup> *collapsed) {
if (slice.isEmpty()) {
oldLoaded = true;
if (!collapsed || collapsed->isEmpty() || !isChannel()) return;
if (!collapsed || collapsed->isEmpty() || !isChannel()) {
if (isChannel()) {
asChannelHistory()->checkJoinedMessage();
asChannelHistory()->checkMaxReadMessageDate();
}
return;
}
}
ChannelHistory *channel = collapsed ? asChannelHistory() : 0;
const MTPMessageGroup *groupsBegin = channel ? collapsed->constData() : 0, *groupsIt = groupsBegin, *groupsEnd = channel ? (groupsBegin + collapsed->size()) : 0;
const MTPMessageGroup *groupsBegin = (isChannel() && collapsed) ? collapsed->constData() : 0, *groupsIt = groupsBegin, *groupsEnd = (isChannel() && collapsed) ? (groupsBegin + collapsed->size()) : 0;
int32 addToH = 0, skip = 0;
if (!blocks.isEmpty()) { // remove date block
@ -1543,10 +1848,13 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice, const QVector<MTPM
}
if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors
bool channel = isChannel();
int32 mask = 0;
QList<UserData*> *lastAuthors = peer->isChat() ? &(peer->asChat()->lastAuthors) : 0;
for (int32 i = block->items.size(); i > 0; --i) {
HistoryItem *item = block->items[i - 1];
if (channel && !item->fromChannel()) continue;
HistoryMedia *media = item->getMedia(true);
if (media) {
HistoryMediaType mt = media->type();
@ -1635,16 +1943,25 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice, const QVector<MTPM
}
height += addToH;
}
if (isChannel()) {
asChannelHistory()->checkJoinedMessage();
asChannelHistory()->checkMaxReadMessageDate();
}
if (newLoaded && !lastMsg) setLastMessage(lastImportantMessage());
}
void History::addNewerSlice(const QVector<MTPMessage> &slice, const QVector<MTPMessageGroup> *collapsed) {
if (slice.isEmpty()) {
newLoaded = true;
if (!collapsed || collapsed->isEmpty() || !isChannel()) return;
if (!lastMsg) setLastMessage(lastImportantMessage());
if (!collapsed || collapsed->isEmpty() || !isChannel()) {
if (isChannel()) asChannelHistory()->checkJoinedMessage();
return;
}
}
ChannelHistory *channel = collapsed ? asChannelHistory() : 0;
const MTPMessageGroup *groupsBegin = channel ? collapsed->constData() : 0, *groupsIt = groupsBegin, *groupsEnd = channel ? (groupsBegin + collapsed->size()) : 0;
const MTPMessageGroup *groupsBegin = (isChannel() && collapsed) ? collapsed->constData() : 0, *groupsIt = groupsBegin, *groupsEnd = (isChannel() && collapsed) ? (groupsBegin + collapsed->size()) : 0;
bool wasEmpty = isEmpty();
@ -1681,7 +1998,7 @@ void History::addNewerSlice(const QVector<MTPMessage> &slice, const QVector<MTPM
height += block->height;
} else {
newLoaded = true;
fixLastMessage(true);
setLastMessage(lastImportantMessage());
delete block;
}
if (!wasLoadedAtBottom && loadedAtBottom()) { // add all loaded photos to overview
@ -1694,10 +2011,13 @@ void History::addNewerSlice(const QVector<MTPMessage> &slice, const QVector<MTPM
mask |= (1 << i);
}
}
bool channel = isChannel();
for (int32 i = 0; i < blocks.size(); ++i) {
HistoryBlock *b = blocks[i];
for (int32 j = 0; j < b->items.size(); ++j) {
HistoryItem *item = b->items[j];
if (channel && !item->fromChannel()) continue;
HistoryMedia *media = item->getMedia(true);
if (media) {
HistoryMediaType mt = media->type();
@ -1745,6 +2065,8 @@ void History::addNewerSlice(const QVector<MTPMessage> &slice, const QVector<MTPM
blocks.push_front(dateBlock); // date block
height += dh;
}
if (isChannel()) asChannelHistory()->checkJoinedMessage();
}
int32 History::countUnread(MsgId upTo) {
@ -1896,13 +2218,13 @@ HistoryItem *History::addNewInTheMiddle(HistoryItem *newItem, int32 blockIndex,
if (!regItem(newItem)) {
return 0;
}
if (blockIndex < 0 || itemIndex < 0 || blockIndex >= blocks.size() || itemIndex >= blocks.at(blockIndex)->items.size()) {
if (blockIndex < 0 || itemIndex < 0 || blockIndex >= blocks.size() || itemIndex > blocks.at(blockIndex)->items.size()) {
delete newItem;
return 0;
}
HistoryBlock *block = blocks.at(blockIndex);
newItem->y = block->items.at(itemIndex)->y;
newItem->y = (itemIndex < block->items.size()) ? block->items.at(itemIndex)->y : block->height;
block->items.insert(itemIndex, newItem);
if (width) {
@ -1977,26 +2299,32 @@ inline uint64 dialogPosFromDate(const QDateTime &date) {
return (uint64(date.toTime_t()) << 32) | (++_dialogsPosToTopShift);
}
void History::setLastMessage(HistoryItem *msg, bool updatePosInDialogs) {
void History::setLastMessage(HistoryItem *msg) {
if (msg) {
if (!lastMsg) Local::removeSavedPeer(peer);
lastMsg = msg;
if (updatePosInDialogs) setPosInDialogsDate(msg->date);
setPosInDialogsDate(msg->date);
} else {
lastMsg = 0;
}
}
void History::setPosInDialogsDate(const QDateTime &date) {
bool updateDialog = (App::main() && (!peer->isChannel() || peer->asChannel()->amCreator() || (!peer->asChannel()->haveLeft() && !peer->asChannel()->wasKicked())));
if (!lastMsgDate.isNull() && lastMsgDate >= date) {
if (!updateDialog || !dialogs.isEmpty()) {
return;
}
}
lastMsgDate = date;
posInDialogs = dialogPosFromDate(lastMsgDate);
if (App::main()) {
App::main()->createDialogAtTop(this, unreadCount);
if (updateDialog) {
App::main()->createDialog(this);
}
}
void History::fixLastMessage(bool wasAtBottom) {
setLastMessage(wasAtBottom ? lastImportantMessage() : 0, false);
setLastMessage(wasAtBottom ? lastImportantMessage() : 0);
}
MsgId History::minMsgId() const {
@ -2099,6 +2427,8 @@ void History::clear(bool leaveItems) {
if (peer->isChat()) {
peer->asChat()->lastAuthors.clear();
peer->asChat()->markupSenders.clear();
} else if (isChannel()) {
asChannelHistory()->cleared();
}
if (leaveItems && App::main()) App::main()->historyCleared(this);
}
@ -5724,11 +6054,30 @@ HistoryItem(history, block, msgId, flags, date, from)
setText(QString(), LinksInText());
}
QString formatViewsCount(int32 views) {
if (views > 999999) {
views /= 100000;
if (views % 10) {
return QString::number(views / 10) + '.' + QString::number(views % 10) + 'M';
}
return QString::number(views / 10) + 'M';
} else if (views > 9999) {
views /= 100;
if (views % 10) {
return QString::number(views / 10) + '.' + QString::number(views % 10) + 'K';
}
return QString::number(views / 10) + 'K';
} else if (views > 0) {
return QString::number(views);
}
return qsl("1");
}
void HistoryMessage::initTime() {
_timeText = date.toString(cTimeFormat());// + qsl(" (%1)").arg(id);
_timeText = date.toString(cTimeFormat());
_timeWidth = st::msgDateFont->m.width(_timeText);
_viewsText = (_views >= 0) ? QString::number(_views ? _views : 1) : QString();
_viewsText = (_views >= 0) ? formatViewsCount(_views) : QString();
_viewsWidth = _viewsText.isEmpty() ? 0 : st::msgDateFont->m.width(_viewsText);
}
@ -5999,7 +6348,7 @@ void HistoryMessage::setViewsCount(int32 count) {
int32 was = _viewsWidth;
_views = count;
_viewsText = (_views >= 0) ? QString::number(_views ? _views : 1) : QString();
_viewsText = (_views >= 0) ? formatViewsCount(_views) : QString();
_viewsWidth = _viewsText.isEmpty() ? 0 : st::msgDateFont->m.width(_viewsText);
if (was == _viewsWidth) {
if (App::main()) App::main()->msgUpdated(history()->peer->id, this);
@ -6366,7 +6715,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const
fwdNameUpdated();
}
HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) : HistoryMessage(history, block, id, newMessageFlags(history->peer) | (msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage_flag_media_unread : 0), date, from, msg->justMedia() ? QString() : msg->HistoryMessage::selectedText(FullItemSel), msg->HistoryMessage::textLinks(), msg->getMedia())
HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) : HistoryMessage(history, block, id, newMessageFlags(history->peer) | (!history->peer->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage_flag_media_unread : 0), date, from, msg->justMedia() ? QString() : msg->HistoryMessage::selectedText(FullItemSel), msg->HistoryMessage::textLinks(), msg->getMedia())
, fwdDate(msg->dateForwarded())
, fwdFrom(msg->fromForwarded())
, fwdFromVersion(fwdFrom->nameVersion)
@ -7211,6 +7560,8 @@ void HistoryGroup::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x
}
void HistoryGroup::uniteWith(MsgId minId, MsgId maxId, int32 count) {
if (minId < 0 || maxId < 0) return;
if (minId == _minId && maxId == _maxId && count == _count) return;
if (minId < _minId) {
@ -7265,6 +7616,16 @@ void HistoryCollapse::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3
state = HistoryDefaultCursorState;
}
HistoryJoined::HistoryJoined(History *history, HistoryBlock *block, const QDateTime &inviteDate, UserData *inviter, int32 flags) :
HistoryServiceMsg(history, block, clientMsgId(), inviteDate, QString(), flags) {
if (inviter->id == MTP::authedId()) {
_text.setText(st::msgServiceFont, lang(lng_action_you_joined), _historySrvOptions);
} else {
_text.setText(st::msgServiceFont, lng_action_add_you(lt_from, textcmdLink(1, inviter->name)), _historySrvOptions);
_text.setLink(1, TextLinkPtr(new PeerLink(inviter)));
}
}
HistoryUnreadBar::HistoryUnreadBar(History *history, HistoryBlock *block, int32 count, const QDateTime &date) : HistoryItem(history, block, clientMsgId(), 0, date, 0), freezed(false) {
setCount(count);
initDimensions();

View File

@ -62,7 +62,7 @@ public:
unreadFull = unreadMuted = 0;
}
HistoryItem *addNewMessage(const MTPmessage &msg, NewMessageType type);
HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type);
// HistoryItem *addToBack(const MTPgeoChatMessage &msg, bool newMsg = true);
typedef QMap<History*, uint64> TypingHistories; // when typing in this history started
@ -193,13 +193,13 @@ public:
clear();
}
HistoryItem *createItem(HistoryBlock *block, const MTPmessage &msg, bool applyServiceAction, bool returnExisting = false);
HistoryItem *createItem(HistoryBlock *block, const MTPMessage &msg, bool applyServiceAction, bool returnExisting = false);
HistoryItem *createItemForwarded(HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg);
HistoryItem *createItemDocument(HistoryBlock *block, MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc);
HistoryItem *addNewService(MsgId msgId, QDateTime date, const QString &text, int32 flags = 0, HistoryMedia *media = 0, bool newMsg = true);
HistoryItem *addNewMessage(const MTPmessage &msg, NewMessageType type);
HistoryItem *addToHistory(const MTPmessage &msg);
HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type);
HistoryItem *addToHistory(const MTPMessage &msg);
HistoryItem *addNewForwarded(MsgId id, QDateTime date, int32 from, HistoryMessage *item);
HistoryItem *addNewDocument(MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc);
@ -232,7 +232,7 @@ public:
bool isReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop); // has messages for showing history at msgId
void getReadyFor(MsgId msgId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop);
void setLastMessage(HistoryItem *msg, bool updatePosInDialogs = true);
void setLastMessage(HistoryItem *msg);
void setPosInDialogsDate(const QDateTime &date);
void fixLastMessage(bool wasAtBottom);
@ -355,6 +355,7 @@ private:
class HistoryGroup;
class HistoryCollapse;
class HistoryJoined;
class ChannelHistory : public History {
public:
@ -368,6 +369,10 @@ public:
void getSwitchReadyFor(MsgId switchId, MsgId &fixInScrollMsgId, int32 &fixInScrollMsgTop);
void insertCollapseItem(MsgId wasMinId);
void getRangeDifference();
void getRangeDifferenceNext(int32 pts);
void addNewGroup(const MTPMessageGroup &group);
int32 unreadCountAll;
bool onlyImportant() const {
@ -375,7 +380,7 @@ public:
}
HistoryCollapse *collapse() const {
return _collapse;
return _collapseMessage;
}
void clearOther() {
@ -384,6 +389,10 @@ public:
_otherList.clear();
}
HistoryJoined *insertJoinedMessage(bool unread);
void checkJoinedMessage();
const QDateTime &maxReadMessageDate();
private:
friend class History;
@ -391,19 +400,30 @@ private:
HistoryItem *addNewToBlocks(const MTPMessage &msg, NewMessageType type);
void addNewToOther(HistoryItem *item, NewMessageType type);
void checkMaxReadMessageDate();
HistoryGroup *findGroup(MsgId msgId) const;
HistoryBlock *findGroupBlock(MsgId msgId) const;
HistoryGroup *findGroupInOther(MsgId msgId) const;
HistoryItem *findPrevItem(HistoryItem *item) const;
void switchMode();
void cleared();
bool _onlyImportant;
QDateTime _maxReadMessageDate;
typedef QList<HistoryItem*> OtherList;
OtherList _otherList;
bool _otherOldLoaded, _otherNewLoaded;
HistoryCollapse *_collapse;
HistoryCollapse *_collapseMessage;
HistoryJoined *_joinedMessage;
void switchMode();
MsgId _rangeDifferenceFromId, _rangeDifferenceToId;
int32 _rangeDifferencePts;
mtpRequestId _rangeDifferenceRequestId;
};
@ -755,9 +775,8 @@ enum InfoDisplayType {
InfoDisplayOverImage,
};
inline bool isImportantChannelMessage(int32 flags) {
/**/
return (flags & MTPDmessage_flag_out) || (flags & MTPDmessage_flag_notify_by_from) || (!(flags & MTPDmessage::flag_from_id) && (flags != 0)); // always has_from_id || has_views
inline bool isImportantChannelMessage(MsgId id, int32 flags) { // client-side important msgs always has_views or has_from_id
return (flags & MTPDmessage_flag_out) || (flags & MTPDmessage_flag_notify_by_from) || ((id > 0 || flags != 0) && !(flags & MTPDmessage::flag_from_id));
}
enum HistoryItemType {
@ -765,7 +784,8 @@ enum HistoryItemType {
HistoryItemDate,
HistoryItemUnreadBar,
HistoryItemGroup,
HistoryItemCollapse
HistoryItemCollapse,
HistoryItemJoined
};
class HistoryMedia;
@ -813,7 +833,7 @@ public:
return _flags & MTPDmessage_flag_notify_by_from;
}
bool isMediaUnread() const {
return (_flags & MTPDmessage_flag_media_unread) && (channelId() == NoChannel); // CHANNELS_UI
return (_flags & MTPDmessage_flag_media_unread) && (channelId() == NoChannel);
}
void markMediaRead() {
_flags &= ~MTPDmessage_flag_media_unread;
@ -824,11 +844,14 @@ public:
bool hasTextLinks() const {
return _flags & MTPDmessage_flag_HAS_TEXT_LINKS;
}
bool hasViews() const {
return _flags & MTPDmessage::flag_views;
}
bool fromChannel() const {
return _from->isChannel();
}
bool isImportant() const {
return _history->isChannel() && isImportantChannelMessage(_flags);
return _history->isChannel() && isImportantChannelMessage(id, _flags);
}
virtual bool needCheck() const {
return out();
@ -877,6 +900,19 @@ public:
}
virtual QString notificationText() const = 0;
bool canDelete() const {
ChannelData *channel = _history->peer->asChannel();
if (!channel) return true;
if (id == 1) return false;
if (channel->amCreator()) return true;
if (fromChannel()) {
if (channel->amEditor() && out()) return true;
return false;
}
return (channel->amEditor() || channel->amModerator() || out());
}
int32 y;
MsgId id;
QDateTime date;
@ -1811,6 +1847,15 @@ private:
};
class HistoryJoined : public HistoryServiceMsg {
public:
HistoryJoined(History *history, HistoryBlock *block, const QDateTime &date, UserData *from, int32 flags);
HistoryItemType type() const {
return HistoryItemJoined;
}
};
HistoryItem *createDayServiceMsg(History *history, HistoryBlock *block, QDateTime date);
class HistoryUnreadBar : public HistoryItem {

View File

@ -96,6 +96,8 @@ void HistoryList::updateMsg(const HistoryItem *msg) {
}
void HistoryList::paintEvent(QPaintEvent *e) {
if (!App::main()) return;
QRect r(e->rect());
bool trivial = (rect() == r);
@ -151,6 +153,11 @@ void HistoryList::paintEvent(QPaintEvent *e) {
}
}
item->draw(p, sel);
if (item->hasViews()) {
App::main()->scheduleViewIncrement(item);
}
p.translate(0, h);
++iItem;
if (iItem == block->items.size()) {
@ -822,7 +829,7 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (dynamic_cast<HistoryMessage*>(App::hoveredLinkItem()) && App::hoveredLinkItem()->id > 0) {
_menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true);
}
if ((!hist->peer->isChannel() || hist->peer->asChannel()->adminned || App::hoveredLinkItem()->out())) {
if (App::hoveredLinkItem()->canDelete()) {
_menu->addAction(lang(lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true);
}
}
@ -832,7 +839,7 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
App::contextItem(App::hoveredLinkItem());
}
} else { // maybe cursor on some text history item?
bool canDelete = (item && item->type() == HistoryItemMsg) && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned || item->out());
bool canDelete = (item && item->type() == HistoryItemMsg) && item->canDelete();
bool canForward = (item && item->type() == HistoryItemMsg) && (item->id > 0) && !item->serviceMsg();
HistoryMessage *msg = dynamic_cast<HistoryMessage*>(item);
@ -1322,9 +1329,7 @@ void HistoryList::getSelectionState(int32 &selectedForForward, int32 &selectedFo
selectedForForward = selectedForDelete = 0;
for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) {
if (i.key()->type() == HistoryItemMsg && i.value() == FullItemSel) {
if (!hist || !hist->peer || !hist->peer->isChannel() || hist->peer->asChannel()->adminned) {
++selectedForDelete;
} else if (i.key()->out()) {
if (i.key()->canDelete()) {
++selectedForDelete;
}
++selectedForForward;
@ -1710,9 +1715,10 @@ void ReportSpamPanel::paintEvent(QPaintEvent *e) {
}
}
void ReportSpamPanel::setReported(bool reported) {
void ReportSpamPanel::setReported(bool reported, PeerData *onPeer) {
if (reported) {
_report.hide();
_clear.setText(lang(onPeer->isChannel() ? lng_profile_leave_channel : lng_profile_delete_conversation));
_clear.show();
} else {
_report.show();
@ -2321,6 +2327,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
, _send(this, lang(lng_send_button), st::btnSend)
, _unblock(this, lang(lng_unblock_button), st::btnUnblock)
, _botStart(this, lang(lng_bot_start), st::btnSend)
, _joinChannel(this, lang(lng_channel_join), st::btnSend)
, _muteUnmute(this, lang(lng_channel_mute), st::btnSend)
, _unblockRequest(0)
, _reportSpamRequest(0)
, _attachDocument(this, st::btnAttachDocument)
@ -2372,6 +2380,9 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
connect(&_send, SIGNAL(clicked()), this, SLOT(onSend()));
connect(&_unblock, SIGNAL(clicked()), this, SLOT(onUnblock()));
connect(&_botStart, SIGNAL(clicked()), this, SLOT(onBotStart()));
connect(&_joinChannel, SIGNAL(clicked()), this, SLOT(onJoinChannel()));
connect(&_muteUnmute, SIGNAL(clicked()), this, SLOT(onMuteUnmute()));
connect(&_broadcast, SIGNAL(changed()), this, SLOT(onBroadcastChange()));
connect(&_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool)));
@ -2439,6 +2450,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
_send.hide();
_unblock.hide();
_botStart.hide();
_joinChannel.hide();
_muteUnmute.hide();
_reportSpamPanel.move(0, 0);
_reportSpamPanel.hide();
@ -2486,7 +2499,9 @@ void HistoryWidget::onMentionHashtagOrBotCommandInsert(QString str) {
}
void HistoryWidget::onTextChange() {
updateSendAction(_history, SendActionTyping);
if (_peer && (!_peer->isChannel() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) {
updateSendAction(_history, SendActionTyping);
}
if (cHasAudioCapture()) {
if (!_field.hasSendText() && !readyToForward()) {
@ -2640,7 +2655,7 @@ void HistoryWidget::onRecordDone(QByteArray result, qint32 samples) {
App::wnd()->activateWindow();
int32 duration = samples / AudioVoiceMsgFrequency;
_imageLoader.append(result, duration, _peer->id, replyToId(), ToPrepareAudio);
_imageLoader.append(result, duration, _peer->id, _broadcast.checked(), replyToId(), ToPrepareAudio);
cancelReply(lastForceReplyReplied());
}
@ -2656,7 +2671,9 @@ void HistoryWidget::onRecordUpdate(qint16 level, qint32 samples) {
stopRecording(_peer && samples > 0 && _inField);
}
updateField();
updateSendAction(_history, SendActionRecordAudio);
if (_peer && (!_peer->isChannel() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) {
updateSendAction(_history, SendActionRecordAudio);
}
}
void HistoryWidget::updateStickers() {
@ -2872,7 +2889,9 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) {
update();
return;
}
if (_history->mySendActions.contains(SendActionTyping)) updateSendAction(_history, SendActionTyping, -1);
if (_history->mySendActions.contains(SendActionTyping)) {
updateSendAction(_history, SendActionTyping, -1);
}
}
stopGif();
@ -2926,6 +2945,7 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) {
_peer = peerId ? App::peer(peerId) : 0;
_channel = _peer ? peerToChannel(_peer->id) : NoChannel;
_canSendMessages = canSendMessages(_peer);
if (_peer && _peer->isChannel()) _peer->asChannel()->updateFull();
_unblockRequest = _reportSpamRequest = 0;
@ -2954,6 +2974,8 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) {
}
_history = App::history(_peer->id);
if (_channel) updateNotifySettings();
if (_showAtMsgId == ShowAtUnreadMsgId) {
if (_history->lastWidth) {
_showAtMsgId = _history->lastShowAtMsgId;
@ -3055,6 +3077,12 @@ void HistoryWidget::ctrlEnterSubmitUpdated() {
_field.setCtrlEnterSubmit(cCtrlEnter());
}
void HistoryWidget::updateNotifySettings() {
if (!_peer || !_peer->isChannel()) return;
_muteUnmute.setText(lang(_history->mute ? lng_channel_unmute : lng_channel_mute));
}
void HistoryWidget::updateReportSpamStatus() {
if (!_peer || (_peer->isUser() && (peerToUser(_peer->id) == MTP::authedId() || isNotificationsUser(_peer->id) || isServiceUser(_peer->id) || _peer->asUser()->botInfo))) {
_reportSpamStatus = dbiprsNoButton;
@ -3063,7 +3091,7 @@ void HistoryWidget::updateReportSpamStatus() {
ReportSpamStatuses::const_iterator i = cReportSpamStatuses().constFind(_peer->id);
if (i != cReportSpamStatuses().cend()) {
_reportSpamStatus = i.value();
_reportSpamPanel.setReported(_reportSpamStatus == dbiprsReportSent);
_reportSpamPanel.setReported(_reportSpamStatus == dbiprsReportSent, _peer);
return;
}
}
@ -3105,10 +3133,21 @@ void HistoryWidget::updateReportSpamStatus() {
_reportSpamStatus = dbiprsNoButton;
}
} else if (_peer->isChannel()) {
_reportSpamStatus = dbiprsUnknown;
if (!_peer->asChannel()->inviter || _history->asChannelHistory()->maxReadMessageDate().isNull()) {
_reportSpamStatus = dbiprsUnknown;
} else if (_peer->asChannel()->inviter > 0) {
UserData *user = App::userLoaded(_peer->asChannel()->inviter);
if ((user && user->contact > 0) || (_peer->asChannel()->inviter == MTP::authedId()) || _history->asChannelHistory()->maxReadMessageDate() > _peer->asChannel()->inviteDate) {
_reportSpamStatus = dbiprsNoButton;
} else {
_reportSpamStatus = dbiprsShowButton;
}
} else {
_reportSpamStatus = dbiprsNoButton;
}
}
if (_reportSpamStatus == dbiprsShowButton || _reportSpamStatus == dbiprsNoButton) {
_reportSpamPanel.setReported(false);
_reportSpamPanel.setReported(false, _peer);
cRefReportSpamStatuses().insert(_peer->id, _reportSpamStatus);
Local::writeReportSpamStatuses();
}
@ -3122,6 +3161,8 @@ void HistoryWidget::updateControlsVisibility() {
_send.hide();
_unblock.hide();
_botStart.hide();
_joinChannel.hide();
_muteUnmute.hide();
_attachMention.hide();
_field.hide();
_replyForwardPreviewCancel.hide();
@ -3150,33 +3191,64 @@ void HistoryWidget::updateControlsVisibility() {
} else {
_reportSpamPanel.hide();
}
if (_canSendMessages) {
checkMentionDropdown();
if (isBlocked() || isJoinChannel() || isMuteUnmute()) {
if (isBlocked()) {
_botStart.hide();
_joinChannel.hide();
_muteUnmute.hide();
if (_unblock.isHidden()) {
_unblock.clearState();
_unblock.show();
_kbShown = false;
}
_send.hide();
_field.hide();
_attachEmoji.hide();
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_attachDocument.hide();
_attachPhoto.hide();
_broadcast.hide();
_kbScroll.hide();
_replyForwardPreviewCancel.hide();
} else if (isBotStart()) {
} else if (isJoinChannel()) {
_unblock.hide();
if (_botStart.isHidden()) {
_botStart.clearState();
_botStart.show();
_kbShown = false;
_muteUnmute.hide();
if (_joinChannel.isHidden()) {
_joinChannel.clearState();
_joinChannel.show();
}
} else if (isMuteUnmute()) {
_unblock.hide();
_joinChannel.hide();
if (_muteUnmute.isHidden()) {
_muteUnmute.clearState();
_muteUnmute.show();
}
}
_kbShown = false;
_attachMention.hide();
_send.hide();
_botStart.hide();
_attachDocument.hide();
_attachPhoto.hide();
_broadcast.hide();
_kbScroll.hide();
_replyForwardPreviewCancel.hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachEmoji.hide();
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_attachType.hide();
_emojiPan.hide();
if (!_field.isHidden()) {
_field.hide();
resizeEvent(0);
update();
}
} else if (_canSendMessages) {
checkMentionDropdown();
if (isBotStart()) {
if (isBotStart()) {
_unblock.hide();
_joinChannel.hide();
_muteUnmute.hide();
if (_botStart.isHidden()) {
_botStart.clearState();
_botStart.show();
}
}
_kbShown = false;
_send.hide();
_field.hide();
_attachEmoji.hide();
@ -3191,6 +3263,8 @@ void HistoryWidget::updateControlsVisibility() {
} else {
_unblock.hide();
_botStart.hide();
_joinChannel.hide();
_muteUnmute.hide();
if (cHasAudioCapture() && !_field.hasSendText() && !readyToForward()) {
_send.hide();
setMouseTracking(true);
@ -3255,8 +3329,10 @@ void HistoryWidget::updateControlsVisibility() {
}
if (hasBroadcastToggle()) {
_broadcast.show();
_field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph));
} else {
_broadcast.hide();
_field.setPlaceholder(lang((_history && _history->peer->isChannel()) ? (_history->peer->asChannel()->canPublish() ? lng_broadcast_ph : lng_comment_ph) : lng_message_ph));
}
}
if (_replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) {
@ -3274,6 +3350,8 @@ void HistoryWidget::updateControlsVisibility() {
_send.hide();
_unblock.hide();
_botStart.hide();
_joinChannel.hide();
_muteUnmute.hide();
_attachDocument.hide();
_attachPhoto.hide();
_broadcast.hide();
@ -3337,6 +3415,12 @@ void HistoryWidget::historyCleared(History *history) {
bool HistoryWidget::messagesFailed(const RPCError &error, mtpRequestId requestId) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (error.type() == qstr("CHANNEL_PRIVATE")) {
App::wnd()->showLayer(new ConfirmBox(lang(lng_channel_not_accessible), true));
App::main()->showDialogs();
return true;
}
LOG(("RPC Error: %1 %2: %3").arg(error.code()).arg(error.type()).arg(error.description()));
if (_preloadRequest == requestId) {
_preloadRequest = 0;
@ -3703,7 +3787,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
} else if (readyToForward()) {
App::main()->readServerHistory(_history, false);
fastShowAtEnd(_history);
App::main()->finishForwarding(_history);
App::main()->finishForwarding(_history, _broadcast.checked());
}
if (replyTo < 0) cancelReply(lastKeyboardUsed);
if (_previewData && _previewData->pendingTill) previewCancel();
@ -3722,17 +3806,17 @@ void HistoryWidget::onUnblock() {
_unblockRequest = MTP::send(MTPcontacts_Unblock(_peer->asUser()->inputUser), rpcDone(&HistoryWidget::unblockDone, _peer), rpcFail(&HistoryWidget::unblockFail));
}
void HistoryWidget::unblockDone(PeerData *peer, const MTPBool &result) {
void HistoryWidget::unblockDone(PeerData *peer, const MTPBool &result, mtpRequestId req) {
if (!peer->isUser()) return;
_unblockRequest = 0;
if (_unblockRequest == req) _unblockRequest = 0;
peer->asUser()->blocked = UserIsNotBlocked;
emit App::main()->peerUpdated(peer);
}
bool HistoryWidget::unblockFail(const RPCError &error) {
bool HistoryWidget::unblockFail(const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_unblockRequest = 0;
if (_unblockRequest == req) _unblockRequest = 0;
return false;
}
@ -3768,6 +3852,36 @@ void HistoryWidget::onBotStart() {
resizeEvent(0);
}
void HistoryWidget::onJoinChannel() {
if (_unblockRequest) return;
if (!_peer || !_peer->isChannel() || !isJoinChannel()) {
updateControlsVisibility();
return;
}
_unblockRequest = MTP::send(MTPchannels_JoinChannel(_peer->asChannel()->inputChannel), rpcDone(&HistoryWidget::joinDone), rpcFail(&HistoryWidget::joinFail));
}
void HistoryWidget::joinDone(const MTPUpdates &result, mtpRequestId req) {
if (_unblockRequest == req) _unblockRequest = 0;
if (App::main()) App::main()->sentUpdatesReceived(result);
}
bool HistoryWidget::joinFail(const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (_unblockRequest == req) _unblockRequest = 0;
return false;
}
void HistoryWidget::onMuteUnmute() {
App::main()->updateNotifySetting(_peer, _history->mute);
}
void HistoryWidget::onBroadcastChange() {
_field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph));
}
void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
if (!contact || contact->phone.isEmpty()) return;
@ -3797,7 +3911,7 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const
sendFlags |= MTPmessages_SendMedia::flag_reply_to_msg_id;
}
bool fromChannelName = p->isChannel() && p->asChannel()->adminned && _broadcast.checked();
bool fromChannelName = p->isChannel() && p->asChannel()->canPublish() && (p->asChannel()->isBroadcast() || _broadcast.checked());
if (fromChannelName) {
sendFlags |= MTPmessages_SendMessage_flag_broadcast;
flags |= MTPDmessage::flag_views;
@ -3809,7 +3923,7 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const
App::historyRegRandom(randomId, newId);
App::main()->finishForwarding(h);
App::main()->finishForwarding(h, _broadcast.checked());
cancelReply(lastKeyboardUsed);
}
@ -3861,6 +3975,8 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo
_send.hide();
_unblock.hide();
_botStart.hide();
_joinChannel.hide();
_muteUnmute.hide();
a_coord = back ? anim::ivalue(-st::introSlideShift, 0) : anim::ivalue(st::introSlideShift, 0);
a_alpha = anim::fvalue(0, 1);
a_bgCoord = back ? anim::ivalue(0, st::introSlideShift) : anim::ivalue(0, -st::introSlideShift);
@ -4084,7 +4200,9 @@ void HistoryWidget::stopRecording(bool send) {
_recording = false;
_recordingSamples = 0;
updateSendAction(_history, SendActionRecordAudio, -1);
if (_peer && (!_peer->isChannel() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) {
updateSendAction(_history, SendActionRecordAudio, -1);
}
updateControlsVisibility();
activate();
@ -4236,9 +4354,9 @@ bool HistoryWidget::canSendMessages(PeerData *peer) const {
if (peer->isUser()) {
return peer->asUser()->access != UserNoAccess;
} else if (peer->isChat()) {
return !peer->asChat()->forbidden && !peer->asChat()->left;
return !peer->asChat()->isForbidden && !peer->asChat()->haveLeft;
} else if (peer->isChannel()) {
return !peer->asChannel()->forbidden && !peer->asChannel()->left && (peer->asChannel()->adminned || !peer->asChannel()->isBroadcast);
return !peer->asChannel()->isForbidden && !peer->asChannel()->haveLeft() && !peer->asChannel()->wasKicked() && (peer->asChannel()->canPublish() || !peer->asChannel()->isBroadcast());
}
}
return false;
@ -4249,7 +4367,7 @@ bool HistoryWidget::readyToForward() const {
}
bool HistoryWidget::hasBroadcastToggle() const {
return _history && _history->peer->isChannel() && _history->peer->asChannel()->adminned && !_history->peer->asChannel()->isBroadcast;
return _history && _history->peer->isChannel() && _history->peer->asChannel()->canPublish() && !_history->peer->asChannel()->isBroadcast();
}
bool HistoryWidget::isBotStart() const {
@ -4261,6 +4379,14 @@ bool HistoryWidget::isBlocked() const {
return _peer && _peer->isUser() && _peer->asUser()->blocked == UserIsBlocked;
}
bool HistoryWidget::isJoinChannel() const {
return _peer && _peer->isChannel() && !_peer->asChannel()->amParticipant();
}
bool HistoryWidget::isMuteUnmute() const {
return _peer && _peer->isChannel() && _peer->asChannel()->isBroadcast() && !_peer->asChannel()->canPublish();
}
bool HistoryWidget::updateCmdStartShown() {
bool cmdStartShown = false;
if (_history && _peer && ((_peer->isChat() && _peer->asChat()->botStatus > 0) || (_peer->isChannel() && _peer->asChannel()->botStatus > 0) || (_peer->isUser() && _peer->asUser()->botInfo))) {
@ -4478,7 +4604,7 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) {
text = App::onlineText(_peer->asUser(), t);
} else if (_peer->isChat()) {
ChatData *chat = _peer->asChat();
if (chat->forbidden || chat->left) {
if (chat->isForbidden || chat->haveLeft) {
text = lang(lng_chat_status_unaccessible);
} else if (chat->participants.isEmpty()) {
text = _titlePeerText.isEmpty() ? lng_chat_status_members(lt_count, chat->count < 0 ? 0 : chat->count) : _titlePeerText;
@ -4498,7 +4624,7 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) {
}
}
} else if (_peer->isChannel()) {
text = lang(lng_channel_status);
text = _peer->asChannel()->count ? lng_chat_status_members(lt_count, _peer->asChannel()->count) : lang(lng_channel_status);
}
if (_titlePeerText != text) {
_titlePeerText = text;
@ -4527,7 +4653,6 @@ void HistoryWidget::updateOnlineDisplayTimer() {
}
}
} else if (_peer->isChannel()) {
// CHANNELS_UI
}
App::main()->updateOnlineDisplayIn(minIn * 1000);
}
@ -4548,6 +4673,8 @@ void HistoryWidget::onFieldResize() {
_attachPhoto.move(_attachDocument.x(), _attachDocument.y());
_botStart.setGeometry(0, _attachDocument.y(), width(), _botStart.height());
_unblock.setGeometry(0, _attachDocument.y(), width(), _unblock.height());
_joinChannel.setGeometry(0, _attachDocument.y(), width(), _joinChannel.height());
_muteUnmute.setGeometry(0, _attachDocument.y(), width(), _muteUnmute.height());
_send.move(width() - _send.width(), _attachDocument.y());
_broadcast.move(_send.x() - _broadcast.width(), height() - kbh - _broadcast.height());
_attachEmoji.move((hasBroadcastToggle() ? _broadcast.x() : _send.x()) - _attachEmoji.width(), height() - kbh - _attachEmoji.height());
@ -4593,7 +4720,7 @@ void HistoryWidget::uploadImage(const QImage &img, bool withText, const QString
_confirmImage = img;
_confirmWithText = withText;
_confirmSource = source;
_confirmImageId = _imageLoader.append(img, _peer->id, replyToId(), ToPreparePhoto);
_confirmImageId = _imageLoader.append(img, _peer->id, _broadcast.checked(), replyToId(), ToPreparePhoto);
}
void HistoryWidget::uploadFile(const QString &file, bool withText) {
@ -4601,7 +4728,7 @@ void HistoryWidget::uploadFile(const QString &file, bool withText) {
App::wnd()->activateWindow();
_confirmWithText = withText;
_confirmImageId = _imageLoader.append(file, _peer->id, replyToId(), ToPrepareDocument);
_confirmImageId = _imageLoader.append(file, _peer->id, _broadcast.checked(), replyToId(), ToPrepareDocument);
}
void HistoryWidget::shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool withText) {
@ -4622,7 +4749,7 @@ void HistoryWidget::uploadConfirmImageUncompressed(bool ctrlShiftEnter, MsgId re
onSend(ctrlShiftEnter, replyTo);
}
bool lastKeyboardUsed = lastForceReplyReplied(FullMsgId(_channel, replyTo));
_imageLoader.append(_confirmImage, peerId, replyTo, ToPrepareDocument, ctrlShiftEnter);
_imageLoader.append(_confirmImage, peerId, _broadcast.checked(), replyTo, ToPrepareDocument, ctrlShiftEnter);
_confirmImageId = 0;
_confirmWithText = false;
_confirmImage = QImage();
@ -4633,7 +4760,7 @@ void HistoryWidget::uploadMedias(const QStringList &files, ToPrepareMediaType ty
if (!_history) return;
App::wnd()->activateWindow();
_imageLoader.append(files, _peer->id, replyToId(), type);
_imageLoader.append(files, _peer->id, _broadcast.checked(), replyToId(), type);
cancelReply(lastForceReplyReplied());
}
@ -4641,7 +4768,7 @@ void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaTyp
if (!peer && !_history) return;
App::wnd()->activateWindow();
_imageLoader.append(fileContent, peer ? peer : _peer->id, replyToId(), type);
_imageLoader.append(fileContent, peer ? peer : _peer->id, _broadcast.checked(), replyToId(), type);
cancelReply(lastForceReplyReplied());
}
@ -4721,7 +4848,7 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
int32 flags = newMessageFlags(h->peer) | MTPDmessage::flag_media; // unread, out
if (img.replyTo) flags |= MTPDmessage::flag_reply_to_msg_id;
bool fromChannelName = h->peer->isChannel() && h->peer->asChannel()->adminned && _broadcast.checked();
bool fromChannelName = h->peer->isChannel() && h->peer->asChannel()->canPublish() && (h->peer->asChannel()->isBroadcast() || img.broadcast);
if (fromChannelName) {
flags |= MTPDmessage::flag_views;
} else {
@ -4732,7 +4859,9 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
} else if (img.type == ToPrepareDocument) {
h->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(img.peer), MTPPeer(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(img.document), MTPnullMarkup, MTPnullEntities, MTP_int(1)), NewMessageUnread);
} else if (img.type == ToPrepareAudio) {
flags |= MTPDmessage_flag_media_unread;
if (!h->peer->isChannel()) {
flags |= MTPDmessage_flag_media_unread;
}
h->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(img.peer), MTPPeer(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaAudio(img.audio), MTPnullMarkup, MTPnullEntities, MTP_int(1)), NewMessageUnread);
}
@ -4763,7 +4892,7 @@ void HistoryWidget::onPhotoUploaded(const FullMsgId &newId, const MTPInputFile &
sendFlags |= MTPmessages_SendMedia::flag_reply_to_msg_id;
}
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->adminned && item->fromChannel();
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->canPublish() && item->fromChannel();
if (fromChannelName) {
sendFlags |= MTPmessages_SendMessage_flag_broadcast;
}
@ -4808,7 +4937,7 @@ void HistoryWidget::onDocumentUploaded(const FullMsgId &newId, const MTPInputFil
sendFlags |= MTPmessages_SendMedia::flag_reply_to_msg_id;
}
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->adminned && item->fromChannel();
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->canPublish() && item->fromChannel();
if (fromChannelName) {
sendFlags |= MTPmessages_SendMessage_flag_broadcast;
}
@ -4837,7 +4966,7 @@ void HistoryWidget::onThumbDocumentUploaded(const FullMsgId &newId, const MTPInp
sendFlags |= MTPmessages_SendMedia::flag_reply_to_msg_id;
}
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->adminned && item->fromChannel();
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->canPublish() && item->fromChannel();
if (fromChannelName) {
sendFlags |= MTPmessages_SendMessage_flag_broadcast;
}
@ -4864,7 +4993,7 @@ void HistoryWidget::onAudioUploaded(const FullMsgId &newId, const MTPInputFile &
sendFlags |= MTPmessages_SendMedia::flag_reply_to_msg_id;
}
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->adminned && item->fromChannel();
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->canPublish() && item->fromChannel();
if (fromChannelName) {
sendFlags |= MTPmessages_SendMessage_flag_broadcast;
}
@ -4875,30 +5004,33 @@ void HistoryWidget::onAudioUploaded(const FullMsgId &newId, const MTPInputFile &
void HistoryWidget::onPhotoProgress(const FullMsgId &newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
if (item) {
if (HistoryItem *item = App::histItemById(newId)) {
PhotoData *photo = (item->getMedia() && item->getMedia()->type() == MediaTypePhoto) ? static_cast<HistoryPhoto*>(item->getMedia())->photo() : 0;
updateSendAction(item->history(), SendActionUploadPhoto, 0);
if (!item->fromChannel()) {
updateSendAction(item->history(), SendActionUploadPhoto, 0);
}
// msgUpdated(item->history()->peer->id, item);
}
}
void HistoryWidget::onDocumentProgress(const FullMsgId &newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
if (item) {
if (HistoryItem *item = App::histItemById(newId)) {
DocumentData *doc = (item->getMedia() && item->getMedia()->type() == MediaTypeDocument) ? static_cast<HistoryDocument*>(item->getMedia())->document() : 0;
updateSendAction(item->history(), SendActionUploadFile, doc ? doc->uploadOffset : 0);
if (!item->fromChannel()) {
updateSendAction(item->history(), SendActionUploadFile, doc ? doc->uploadOffset : 0);
}
msgUpdated(item->history()->peer->id, item);
}
}
void HistoryWidget::onAudioProgress(const FullMsgId &newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
if (item) {
if (HistoryItem *item = App::histItemById(newId)) {
AudioData *audio = (item->getMedia() && item->getMedia()->type() == MediaTypeAudio) ? static_cast<HistoryAudio*>(item->getMedia())->audio() : 0;
updateSendAction(item->history(), SendActionUploadAudio, audio ? audio->uploadOffset : 0);
if (!item->fromChannel()) {
updateSendAction(item->history(), SendActionUploadAudio, audio ? audio->uploadOffset : 0);
}
msgUpdated(item->history()->peer->id, item);
}
}
@ -4907,7 +5039,9 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
if (item) {
updateSendAction(item->history(), SendActionUploadPhoto, -1);
if (!item->fromChannel()) {
updateSendAction(item->history(), SendActionUploadPhoto, -1);
}
// msgUpdated(item->history()->peer->id, item);
}
}
@ -4916,7 +5050,9 @@ void HistoryWidget::onDocumentFailed(const FullMsgId &newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
if (item) {
updateSendAction(item->history(), SendActionUploadFile, -1);
if (!item->fromChannel()) {
updateSendAction(item->history(), SendActionUploadFile, -1);
}
msgUpdated(item->history()->peer->id, item);
}
}
@ -4925,13 +5061,15 @@ void HistoryWidget::onAudioFailed(const FullMsgId &newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
if (item) {
updateSendAction(item->history(), SendActionUploadAudio, -1);
if (!item->fromChannel()) {
updateSendAction(item->history(), SendActionUploadAudio, -1);
}
msgUpdated(item->history()->peer->id, item);
}
}
void HistoryWidget::onReportSpamClicked() {
ConfirmBox *box = new ConfirmBox(lang(_peer->isUser() ? lng_report_spam_sure : lng_report_spam_sure_group), lang(lng_report_spam_ok));
ConfirmBox *box = new ConfirmBox(lang(_peer->isUser() ? lng_report_spam_sure : (_peer->isChat() ? lng_report_spam_sure_group : lng_report_spam_sure_channel)), lang(lng_report_spam_ok));
connect(box, SIGNAL(confirmed()), this, SLOT(onReportSpamSure()));
App::wnd()->showLayer(box);
_clearPeer = _peer;
@ -4954,7 +5092,7 @@ void HistoryWidget::reportSpamDone(PeerData *peer, const MTPBool &result, mtpReq
Local::writeReportSpamStatuses();
}
_reportSpamStatus = dbiprsReportSent;
_reportSpamPanel.setReported(_reportSpamStatus == dbiprsReportSent);
_reportSpamPanel.setReported(_reportSpamStatus == dbiprsReportSent, peer);
}
bool HistoryWidget::reportSpamFail(const RPCError &error, mtpRequestId req) {
@ -4976,21 +5114,15 @@ void HistoryWidget::onReportSpamHide() {
}
void HistoryWidget::onReportSpamClear() {
ConfirmBox *box = new ConfirmBox(_peer->isUser() ? lng_sure_delete_history(lt_contact, _peer->name) : lng_sure_delete_and_exit(lt_group, _peer->name));
connect(box, SIGNAL(confirmed()), this, SLOT(onReportSpamClearSure()));
App::wnd()->showLayer(box);
_clearPeer = _peer;
}
void HistoryWidget::onReportSpamClearSure() {
App::wnd()->hideLayer();
if (_clearPeer->isUser()) {
App::main()->deleteConversation(_clearPeer);
} else if (_clearPeer->isChat()) {
App::main()->showDialogs();
MTP::send(MTPmessages_DeleteChatUser(_clearPeer->asChat()->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, _clearPeer), App::main()->rpcFail(&MainWidget::leaveChatFailed, _clearPeer));
} else if (_clearPeer->isChannel()) { // CHANNELS_UX
} else if (_clearPeer->isChannel()) {
App::main()->showDialogs();
MTP::send(MTPchannels_LeaveChannel(_clearPeer->asChannel()->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived));
}
}
@ -4998,9 +5130,16 @@ void HistoryWidget::peerMessagesUpdated(PeerId peer) {
if (_peer && _list && peer == _peer->id) {
updateListSize();
updateBotKeyboard();
if (!_scroll.isHidden() && !isBlocked() && _botStart.isHidden() == isBotStart()) {
updateControlsVisibility();
resizeEvent(0);
if (!_scroll.isHidden()) {
bool unblock = isBlocked(), botStart = isBotStart(), joinChannel = isJoinChannel(), muteUnmute = isMuteUnmute();
bool upd = (_unblock.isHidden() == unblock);
if (!upd && !unblock) upd = (_botStart.isHidden() == botStart);
if (!upd && !unblock && !botStart) upd = (_joinChannel.isHidden() == joinChannel);
if (!upd && !unblock && !botStart && !joinChannel) upd = (_muteUnmute.isHidden() == muteUnmute);
if (upd) {
updateControlsVisibility();
resizeEvent(0);
}
}
}
}
@ -5043,6 +5182,8 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
_send.move(width() - _send.width(), _attachDocument.y());
_botStart.setGeometry(0, _attachDocument.y(), width(), _botStart.height());
_unblock.setGeometry(0, _attachDocument.y(), width(), _unblock.height());
_joinChannel.setGeometry(0, _attachDocument.y(), width(), _joinChannel.height());
_muteUnmute.setGeometry(0, _attachDocument.y(), width(), _muteUnmute.height());
_broadcast.move(_send.x() - _broadcast.width(), height() - kbh - _broadcast.height());
_attachEmoji.move((hasBroadcastToggle() ? _broadcast.x() : _send.x()) - _attachEmoji.width(), height() - kbh - _attachEmoji.height());
_kbShow.move(_attachEmoji.x() - _kbShow.width(), height() - kbh - _kbShow.height());
@ -5113,10 +5254,8 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown,
}
int32 newScrollHeight = height();
if (isBlocked()) {
if (isBlocked() || isBotStart() || isJoinChannel() || isMuteUnmute()) {
newScrollHeight -= _unblock.height();
} else if (isBotStart()) {
newScrollHeight -= _botStart.height();
} else {
if (_canSendMessages) {
newScrollHeight -= (_field.height() + 2 * st::sendPadding);
@ -5469,7 +5608,7 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) {
flags |= MTPDmessage::flag_reply_to_msg_id;
sendFlags |= MTPmessages_SendMedia::flag_reply_to_msg_id;
}
bool fromChannelName = _history->peer->isChannel() && _history->peer->asChannel()->adminned && _broadcast.checked();
bool fromChannelName = _history->peer->isChannel() && _history->peer->asChannel()->canPublish() && (_history->peer->asChannel()->isBroadcast() || _broadcast.checked());
if (fromChannelName) {
sendFlags |= MTPmessages_SendMessage_flag_broadcast;
} else {
@ -5478,7 +5617,7 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) {
_history->addNewDocument(newId.msg, flags, replyToId(), date(MTP_int(unixtime())), fromChannelName ? 0 : MTP::authedId(), sticker);
_history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_int(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaDocument(MTP_inputDocument(MTP_long(sticker->id), MTP_long(sticker->access))), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId);
App::main()->finishForwarding(_history);
App::main()->finishForwarding(_history, _broadcast.checked());
cancelReply(lastKeyboardUsed);
if (sticker->sticker()) App::main()->incrementSticker(sticker);
@ -5755,11 +5894,12 @@ void HistoryWidget::peerUpdated(PeerData *data) {
if (data && data == _peer) {
updateListSize();
if (!_showAnim.animating()) updateControlsVisibility();
if (_peer->isChannel()) updateReportSpamStatus();
if (data->isChat() && data->asChat()->count > 0 && data->asChat()->participants.isEmpty()) {
App::api()->requestFullPeer(data);
} else if (data->isUser() && data->asUser()->blocked == UserBlockUnknown) {
App::api()->requestFullPeer(data);
} else if (!_scroll.isHidden() && _unblock.isHidden() == isBlocked()) {
} else if (!_scroll.isHidden() && (_unblock.isHidden() == isBlocked() || (!isBlocked() && _joinChannel.isHidden() == isJoinChannel()))) {
updateControlsVisibility();
resizeEvent(0);
}

View File

@ -229,7 +229,7 @@ public:
void resizeEvent(QResizeEvent *e);
void paintEvent(QPaintEvent *e);
void setReported(bool reported);
void setReported(bool reported, PeerData *onPeer);
signals:
@ -533,6 +533,8 @@ public:
void setInnerFocus();
bool canSendMessages(PeerData *peer) const;
void updateNotifySettings();
~HistoryWidget();
signals:
@ -574,14 +576,17 @@ public slots:
void onReportSpamSure();
void onReportSpamHide();
void onReportSpamClear();
void onReportSpamClearSure();
void onListScroll();
void onHistoryToEnd();
void onCollapseComments();
void onSend(bool ctrlShiftEnter = false, MsgId replyTo = -1);
void onUnblock();
void onBotStart();
void onJoinChannel();
void onMuteUnmute();
void onBroadcastChange();
void onPhotoSelect();
void onDocumentSelect();
@ -673,10 +678,13 @@ private:
void reportSpamDone(PeerData *peer, const MTPBool &result, mtpRequestId request);
bool reportSpamFail(const RPCError &error, mtpRequestId request);
void unblockDone(PeerData *peer, const MTPBool &result);
bool unblockFail(const RPCError &error);
void unblockDone(PeerData *peer, const MTPBool &result, mtpRequestId req);
bool unblockFail(const RPCError &error, mtpRequestId req);
void blockDone(PeerData *peer, const MTPBool &result);
void joinDone(const MTPUpdates &result, mtpRequestId req);
bool joinFail(const RPCError &error, mtpRequestId req);
void countHistoryShowFrom();
void stickersGot(const MTPmessages_AllStickers &stickers);
@ -719,11 +727,13 @@ private:
bool isBotStart() const;
bool isBlocked() const;
bool isJoinChannel() const;
bool isMuteUnmute() const;
bool updateCmdStartShown();
ReportSpamPanel _reportSpamPanel;
FlatButton _send, _unblock, _botStart;
FlatButton _send, _unblock, _botStart, _joinChannel, _muteUnmute;
mtpRequestId _unblockRequest, _reportSpamRequest;
IconedButton _attachDocument, _attachPhoto, _attachEmoji, _kbShow, _kbHide, _cmdStart;
FlatCheckbox _broadcast;

View File

@ -42,6 +42,7 @@ void LocalImageLoaderPrivate::prepareImages() {
QString thumbExt = "jpg";
ToPrepareMediaType type;
bool animated = false;
bool broadcast = false;
bool ctrlShiftEnter = false;
MsgId replyTo;
{
@ -56,6 +57,7 @@ void LocalImageLoaderPrivate::prepareImages() {
id = list.front().id;
type = list.front().type;
duration = list.front().duration;
broadcast = list.front().broadcast;
ctrlShiftEnter = list.front().ctrlShiftEnter;
replyTo = list.front().replyTo;
}
@ -262,7 +264,7 @@ void LocalImageLoaderPrivate::prepareImages() {
{
QMutexLocker lock(loader->readyMutex());
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, audio, photoThumbs, document, jpeg, ctrlShiftEnter, replyTo));
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, audio, photoThumbs, document, jpeg, broadcast, ctrlShiftEnter, replyTo));
}
{
@ -284,11 +286,11 @@ LocalImageLoaderPrivate::~LocalImageLoaderPrivate() {
LocalImageLoader::LocalImageLoader(QObject *parent) : QObject(parent), thread(0), priv(0) {
}
void LocalImageLoader::append(const QStringList &files, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
void LocalImageLoader::append(const QStringList &files, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t) {
{
QMutexLocker lock(toPrepareMutex());
for (QStringList::const_iterator i = files.cbegin(), e = files.cend(); i != e; ++i) {
toPrepare.push_back(ToPrepareMedia(*i, peer, t, false, replyTo));
toPrepare.push_back(ToPrepareMedia(*i, peer, t, broadcast, false, replyTo));
}
}
if (!thread) {
@ -299,11 +301,11 @@ void LocalImageLoader::append(const QStringList &files, const PeerId &peer, MsgI
emit needToPrepare();
}
PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t) {
PhotoId result = 0;
{
QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(img, peer, t, false, replyTo));
toPrepare.push_back(ToPrepareMedia(img, peer, t, broadcast, false, replyTo));
result = toPrepare.back().id;
}
if (!thread) {
@ -315,11 +317,11 @@ PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, MsgI
return result;
}
AudioId LocalImageLoader::append(const QByteArray &audio, int32 duration, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
AudioId LocalImageLoader::append(const QByteArray &audio, int32 duration, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t) {
AudioId result = 0;
{
QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(audio, duration, peer, t, false, replyTo));
toPrepare.push_back(ToPrepareMedia(audio, duration, peer, t, broadcast, false, replyTo));
result = toPrepare.back().id;
}
if (!thread) {
@ -331,11 +333,11 @@ AudioId LocalImageLoader::append(const QByteArray &audio, int32 duration, const
return result;
}
PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter) {
PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter) {
PhotoId result = 0;
{
QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(img, peer, t, ctrlShiftEnter, replyTo));
toPrepare.push_back(ToPrepareMedia(img, peer, t, broadcast, ctrlShiftEnter, replyTo));
result = toPrepare.back().id;
}
if (!thread) {
@ -347,11 +349,11 @@ PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, MsgId re
return result;
}
PhotoId LocalImageLoader::append(const QString &file, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
PhotoId LocalImageLoader::append(const QString &file, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t) {
PhotoId result = 0;
{
QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(file, peer, t, false, replyTo));
toPrepare.push_back(ToPrepareMedia(file, peer, t, broadcast, false, replyTo));
result = toPrepare.back().id;
}
if (!thread) {

View File

@ -26,13 +26,13 @@ enum ToPrepareMediaType {
};
struct ToPrepareMedia {
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool broadcast, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
}
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool broadcast, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
}
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool broadcast, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
}
ToPrepareMedia(const QByteArray &data, int32 duration, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), duration(duration), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
ToPrepareMedia(const QByteArray &data, int32 duration, const PeerId &peer, ToPrepareMediaType t, bool broadcast, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), duration(duration), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
}
PhotoId id;
QString file;
@ -41,6 +41,7 @@ struct ToPrepareMedia {
PeerId peer;
ToPrepareMediaType type;
int32 duration;
bool broadcast;
bool ctrlShiftEnter;
MsgId replyTo;
};
@ -48,8 +49,8 @@ typedef QList<ToPrepareMedia> ToPrepareMedias;
typedef QMap<int32, QByteArray> LocalFileParts;
struct ReadyLocalMedia {
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const MTPAudio &audio, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter, MsgId replyTo) :
replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), audio(audio), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) {
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const MTPAudio &audio, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool broadcast, bool ctrlShiftEnter, MsgId replyTo) :
replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), audio(audio), photoThumbs(photoThumbs), broadcast(broadcast), ctrlShiftEnter(ctrlShiftEnter) {
if (!jpeg.isEmpty()) {
int32 size = jpeg.size();
for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) {
@ -75,6 +76,7 @@ struct ReadyLocalMedia {
LocalFileParts parts;
QByteArray jpeg_md5;
bool broadcast;
bool ctrlShiftEnter;
};
typedef QList<ReadyLocalMedia> ReadyLocalMedias;
@ -110,11 +112,11 @@ class LocalImageLoader : public QObject {
public:
LocalImageLoader(QObject *parent);
void append(const QStringList &files, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
PhotoId append(const QByteArray &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
AudioId append(const QByteArray &audio, int32 duration, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
PhotoId append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter = false);
PhotoId append(const QString &file, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
void append(const QStringList &files, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t);
PhotoId append(const QByteArray &img, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t);
AudioId append(const QByteArray &audio, int32 duration, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t);
PhotoId append(const QImage &img, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter = false);
PhotoId append(const QString &file, const PeerId &peer, bool broadcast, MsgId replyTo, ToPrepareMediaType t);
QMutex *readyMutex();
ReadyLocalMedias &readyList();

View File

@ -2861,13 +2861,13 @@ namespace Local {
} else if (peer->isChat()) {
ChatData *chat = peer->asChat();
stream << chat->name << qint32(chat->count) << qint32(chat->date) << qint32(chat->version) << qint32(chat->admin);
stream << qint32(chat->forbidden ? 1 : 0) << qint32(chat->left ? 1 : 0) << chat->invitationUrl;
stream << chat->name << qint32(chat->count) << qint32(chat->date) << qint32(chat->version) << qint32(chat->creator);
stream << qint32(chat->isForbidden ? 1 : 0) << qint32(chat->haveLeft ? 1 : 0) << chat->invitationUrl;
} else if (peer->isChannel()) {
ChannelData *channel = peer->asChannel();
stream << channel->name << quint64(channel->access) << qint32(channel->date) << qint32(channel->version) << qint32(channel->adminned ? 1 : 0);
stream << qint32(channel->forbidden ? 1 : 0) << qint32(channel->left ? 1 : 0) << channel->invitationUrl;
stream << channel->name << quint64(channel->access) << qint32(channel->date) << qint32(channel->version);
stream << qint32(channel->isForbidden ? 1 : 0) << qint32(channel->flags) << channel->invitationUrl;
}
}
@ -2911,16 +2911,16 @@ namespace Local {
ChatData *chat = result->asChat();
QString name, invitationUrl;
qint32 count, date, version, admin, forbidden, left;
from.stream >> name >> count >> date >> version >> admin >> forbidden >> left >> invitationUrl;
qint32 count, date, version, creator, forbidden, left;
from.stream >> name >> count >> date >> version >> creator >> forbidden >> left >> invitationUrl;
chat->updateName(name, QString(), QString());
chat->count = count;
chat->date = date;
chat->version = version;
chat->admin = admin;
chat->forbidden = (forbidden == 1);
chat->left = (left == 1);
chat->creator = creator;
chat->isForbidden = (forbidden == 1);
chat->haveLeft = (left == 1);
chat->invitationUrl = invitationUrl;
chat->input = MTP_inputPeerChat(MTP_int(peerToChat(chat->id)));
@ -2932,16 +2932,15 @@ namespace Local {
QString name, invitationUrl;
quint64 access;
qint32 date, version, adminned, forbidden, left;
from.stream >> name >> access >> date >> version >> adminned >> forbidden >> left >> invitationUrl;
qint32 date, version, adminned, forbidden, flags;
from.stream >> name >> access >> date >> version >> forbidden >> flags >> invitationUrl;
channel->updateName(name, QString(), QString());
channel->access = access;
channel->date = date;
channel->version = version;
channel->adminned = (adminned == 1);
channel->forbidden = (forbidden == 1);
channel->left = (left == 1);
channel->isForbidden = (forbidden == 1);
channel->flags = flags;
channel->invitationUrl = invitationUrl;
channel->input = MTP_inputPeerChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access));

View File

@ -83,7 +83,13 @@ void TopBarWidget::onAddContact() {
void TopBarWidget::onEdit() {
PeerData *p = App::main() ? App::main()->profilePeer() : 0;
if (p) App::wnd()->showLayer(new AddContactBox(p));
if (p) {
if (p->isChannel()) {
App::wnd()->showLayer(new EditChannelBox(p->asChannel()));
} else {
App::wnd()->showLayer(new AddContactBox(p));
}
}
}
void TopBarWidget::onDeleteContact() {
@ -286,7 +292,7 @@ void TopBarWidget::showAll() {
PeerData *p = App::main() ? App::main()->profilePeer() : 0, *h = App::main() ? App::main()->historyPeer() : 0, *o = App::main() ? App::main()->overviewPeer() : 0;
if (p && (p->isChat() || (p->isUser() && (p->asUser()->contact >= 0 || !App::phoneFromSharedContact(peerToUser(p->id)).isEmpty())))) {
if (p->isChat()) {
if (p->asChat()->forbidden) {
if (p->asChat()->isForbidden) {
_edit.hide();
} else {
_edit.show();
@ -311,7 +317,7 @@ void TopBarWidget::showAll() {
_forward.hide();
_mediaType.hide();
} else {
if (p && p->isChannel() && p->asChannel()->adminned) {
if (p && p->isChannel() && p->asChannel()->amCreator()) {
_edit.show();
} else {
_edit.hide();
@ -373,9 +379,10 @@ _forwardConfirm(0), _hider(0), _peerInStack(0), _msgIdInStack(0),
_playerHeight(0), _contentScrollAddToY(0), _mediaType(this), _mediaTypeMask(0),
updDate(0), updQts(-1), updSeq(0), _getDifferenceTimeByPts(0), _getDifferenceTimeAfterFail(0),
_onlineRequest(0), _lastWasOnline(false), _lastSetOnline(0), _isIdle(false),
_failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _background(0), _api(new ApiWrap(this)) {
_failDifferenceTimeout(1), _lastUpdateTime(0), _handlingChannelDifference(false), _cachedX(0), _cachedY(0), _background(0), _api(new ApiWrap(this)) {
setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight));
MTP::setGlobalDoneHandler(rpcDone(&MainWidget::updateReceived));
_ptsWaiter.setRequesting(true);
updateScrollColors();
@ -390,7 +397,7 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
connect(&_bySeqTimer, SIGNAL(timeout()), this, SLOT(getDifference()));
connect(&_byPtsTimer, SIGNAL(timeout()), this, SLOT(onGetDifferenceTimeByPts()));
connect(&_failDifferenceTimer, SIGNAL(timeout()), this, SLOT(onGetDifferenceTimeAfterFail()));
connect(_api, SIGNAL(fullPeerUpdated(PeerData*)), this, SIGNAL(peerUpdated(PeerData*)));
connect(_api, SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*)));
connect(this, SIGNAL(peerUpdated(PeerData*)), &history, SLOT(peerUpdated(PeerData*)));
connect(&_topBar, SIGNAL(clicked()), this, SLOT(onTopBarClick()));
connect(&history, SIGNAL(peerShown(PeerData*)), this, SLOT(onPeerShown(PeerData*)));
@ -403,6 +410,7 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
connect(audioPlayer(), SIGNAL(stopped(const SongMsgId&)), this, SLOT(documentPlayProgress(const SongMsgId&)));
}
connect(&_updateMutedTimer, SIGNAL(timeout()), this, SLOT(onUpdateMuted()));
connect(&_viewsIncrementTimer, SIGNAL(timeout()), this, SLOT(onViewsIncrement()));
_webPageUpdater.setSingleShot(true);
connect(&_webPageUpdater, SIGNAL(timeout()), this, SLOT(webPagesUpdate()));
@ -435,7 +443,7 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
bool MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) {
PeerData *p = App::peer(peer);
if (!peer || (p->isChannel() && !p->asChannel()->adminned) || (p->isChat() && p->asChat()->forbidden) || (p->isUser() && p->asUser()->access == UserNoAccess)) {
if (!peer || (p->isChannel() && !p->asChannel()->canPublish() && p->asChannel()->isBroadcast()) || (p->isChat() && (p->asChat()->haveLeft || p->asChat()->isForbidden)) || (p->isUser() && p->asUser()->access == UserNoAccess)) {
App::wnd()->showLayer(new ConfirmBox(lang(lng_forward_cant), true));
return false;
}
@ -528,8 +536,10 @@ void MainWidget::cancelForwarding() {
history.cancelForwarding();
}
void MainWidget::finishForwarding(History *hist) {
void MainWidget::finishForwarding(History *hist, bool broadcast) {
if (!hist) return;
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->canPublish() && (hist->peer->asChannel()->isBroadcast() || broadcast);
if (!_toForward.isEmpty()) {
bool genClientSideMessage = (_toForward.size() < 2);
PeerData *forwardFrom = _toForward.cbegin().value()->history()->peer;
@ -544,7 +554,7 @@ void MainWidget::finishForwarding(History *hist) {
if (genClientSideMessage) {
FullMsgId newId(peerToChannel(hist->peer->id), clientMsgId());
HistoryMessage *msg = static_cast<HistoryMessage*>(_toForward.cbegin().value());
hist->addNewForwarded(newId.msg, date(MTP_int(unixtime())), hist->peer->isChannel() ? 0 : MTP::authedId(), msg);
hist->addNewForwarded(newId.msg, date(MTP_int(unixtime())), fromChannelName ? 0 : MTP::authedId(), msg);
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(msg->getMedia())) {
App::main()->incrementSticker(sticker->document());
}
@ -553,7 +563,7 @@ void MainWidget::finishForwarding(History *hist) {
ids.push_back(MTP_int(i.key()));
randomIds.push_back(MTP_long(randomId));
}
int32 flags = hist->peer->isChannel() ? MTPmessages_ForwardMessages_flag_broadcast : 0;
int32 flags = fromChannelName ? MTPmessages_ForwardMessages_flag_broadcast : 0;
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessages(MTP_int(flags), forwardFrom->input, MTP_vector<MTPint>(ids), MTP_vector<MTPlong>(randomIds), hist->peer->input), rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
if (history.peer() == hist->peer) history.peerMessagesUpdated();
@ -756,7 +766,7 @@ DragState MainWidget::getDragState(const QMimeData *mime) {
bool MainWidget::leaveChatFailed(PeerData *peer, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (error.type() == "CHAT_ID_INVALID") { // left this chat already
if (error.type() == qstr("USER_NOT_PARTICIPANT") || error.type() == qstr("CHAT_ID_INVALID")) { // left this chat already
if ((profile && profile->peer() == peer) || (overview && overview->peer() == peer) || _stack.contains(peer) || history.peer() == peer) {
showDialogs();
}
@ -880,7 +890,7 @@ bool MainWidget::addParticipantsFail(const RPCError &error) {
QString text = lang(lng_failed_add_participant);
if (error.type() == "USER_LEFT_CHAT") { // trying to return banned user to his group
} else if (error.type() == "USER_NOT_MUTUAL_CONTACT") { // trying to return user who does not have me in contacts
text = lang(lng_failed_add_not_mutual);
text = lang(lng_failed_add_not_mutual_channel);
} else if (error.type() == "PEER_FLOOD") {
text = lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)));
}
@ -911,6 +921,7 @@ void MainWidget::checkPeerHistory(PeerData *peer) {
void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &result) {
const QVector<MTPMessage> *v = 0;
const QVector<MTPMessageGroup> *collapsed = 0;
switch (result.type()) {
case mtpc_messages_messages: {
const MTPDmessages_messages &d(result.c_messages_messages());
@ -934,7 +945,7 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu
LOG(("API Error: received messages.channelMessages when no channel was passed! (MainWidget::checkedHistory)"));
}
// CHANNELS_TODO use collapsed to remove last important messages from not important after History::addNewMessage
collapsed = &d.vcollapsed.c_vector().v;
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
v = &d.vmessages.c_vector().v;
@ -943,19 +954,50 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu
if (!v) return;
if (v->isEmpty()) {
if (peer->isChat() && peer->asChat()->left) {
if (peer->isChat() && peer->asChat()->haveLeft) {
dialogs.removePeer(peer);
if (history.peer() == peer) {
showDialogs();
}
} else { // CHANNELS_TODO
} else if (peer->isChannel()) {
if (peer->asChannel()->inviter > 0) {
if (!peer->asChannel()->isForbidden && !peer->asChannel()->haveLeft() && !peer->asChannel()->wasKicked()) {
if (UserData *from = App::userLoaded(peer->asChannel()->inviter)) {
History *h = App::history(peer->id);
h->clear(true);
h->addNewerSlice(QVector<MTPMessage>(), 0);
h->asChannelHistory()->insertJoinedMessage(true);
history.peerMessagesUpdated(h->peer->id);
}
}
}
} else {
History *h = App::historyLoaded(peer->id);
if (h) Local::addSavedPeer(peer, h->lastMsgDate);
}
} else {
History *h = App::historyLoaded(peer->id);
History *h = App::history(peer->id);
if (!h->lastMsg) {
h->addNewMessage((*v)[0], NewMessageLast);
HistoryItem *item = h->addNewMessage((*v)[0], NewMessageLast);
if (collapsed && !collapsed->isEmpty() && collapsed->at(0).type() == mtpc_messageGroup && h->isChannel()) {
if (collapsed->at(0).c_messageGroup().vmax_id.v > item->id) {
if (h->asChannelHistory()->onlyImportant()) {
h->asChannelHistory()->clearOther();
} else {
h->setNotLoadedAtBottom();
}
}
}
}
if (!h->lastMsgDate.isNull() && h->loadedAtBottom()) {
if (peer->isChannel() && peer->asChannel()->inviter > 0 && h->lastMsgDate <= peer->asChannel()->inviteDate) {
if (!peer->asChannel()->isForbidden && !peer->asChannel()->haveLeft() && !peer->asChannel()->wasKicked()) {
if (UserData *from = App::userLoaded(peer->asChannel()->inviter)) {
h->asChannelHistory()->insertJoinedMessage(true);
history.peerMessagesUpdated(h->peer->id);
}
}
}
}
}
}
@ -1168,7 +1210,7 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
media = MTP_messageMediaWebPage(MTP_webPagePending(MTP_long(page->id), MTP_int(page->pendingTill)));
flags |= MTPDmessage::flag_media;
}
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->adminned && broadcast;
bool fromChannelName = hist->peer->isChannel() && hist->peer->asChannel()->canPublish() && (hist->peer->asChannel()->isBroadcast() || broadcast);
if (fromChannelName) {
sendFlags |= MTPmessages_SendMessage_flag_broadcast;
flags |= MTPDmessage::flag_views;
@ -1180,7 +1222,7 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_int(sendFlags), hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId), MTPnullMarkup, localEntities), rpcDone(&MainWidget::sentUpdatesReceived, randomId), rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
}
finishForwarding(hist);
finishForwarding(hist, broadcast);
}
void MainWidget::sendMessage(History *hist, const QString &text, MsgId replyTo, bool broadcast) {
@ -1252,10 +1294,14 @@ void MainWidget::insertBotCommand(const QString &cmd) {
history.insertBotCommand(cmd);
}
void MainWidget::searchMessages(const QString &query) {
void MainWidget::searchMessages(const QString &query, PeerData *inPeer) {
App::wnd()->hideMediaview();
dialogs.searchMessages(query);
if (!cWideMode()) showDialogs();
dialogs.searchMessages(query, inPeer);
if (!cWideMode()) {
showDialogs();
} else {
dialogs.activate();
}
}
void MainWidget::preloadOverviews(PeerData *peer) {
@ -1470,7 +1516,7 @@ void MainWidget::loadMediaBack(PeerData *peer, MediaOverviewType type, bool many
void MainWidget::peerUsernameChanged(PeerData *peer) {
if (profile && profile->peer() == peer) {
profile->update();
profile->peerUsernameChanged();
}
if (App::settings() && peer == App::self()) {
App::settings()->usernameChanged();
@ -2164,12 +2210,84 @@ void MainWidget::setInnerFocus() {
}
}
void MainWidget::scheduleViewIncrement(HistoryItem *item) {
PeerData *peer = item->history()->peer;
ViewsIncrement::iterator i = _viewsIncremented.find(peer);
if (i != _viewsIncremented.cend()) {
if (i.value().contains(item->id)) return;
} else {
i = _viewsIncremented.insert(peer, ViewsIncrementMap());
}
i.value().insert(item->id, true);
ViewsIncrement::iterator j = _viewsToIncrement.find(peer);
if (j == _viewsToIncrement.cend()) {
j = _viewsToIncrement.insert(peer, ViewsIncrementMap());
_viewsIncrementTimer.start(SendViewsTimeout);
}
j.value().insert(item->id, true);
}
void MainWidget::onViewsIncrement() {
for (ViewsIncrement::iterator i = _viewsToIncrement.begin(); i != _viewsToIncrement.cend();) {
if (_viewsIncrementRequests.contains(i.key())) {
++i;
continue;
}
QVector<MTPint> ids;
ids.reserve(i.value().size());
for (ViewsIncrementMap::const_iterator j = i.value().cbegin(), end = i.value().cend(); j != end; ++j) {
ids.push_back(MTP_int(j.key()));
}
mtpRequestId req = MTP::send(MTPmessages_GetMessagesViews(i.key()->input, MTP_vector<MTPint>(ids), MTP_bool(true)), rpcDone(&MainWidget::viewsIncrementDone, ids), rpcFail(&MainWidget::viewsIncrementFail), 0, 5);
_viewsIncrementRequests.insert(i.key(), req);
i = _viewsToIncrement.erase(i);
}
}
void MainWidget::viewsIncrementDone(QVector<MTPint> ids, const MTPVector<MTPint> &result, mtpRequestId req) {
const QVector<MTPint> &v(result.c_vector().v);
if (ids.size() == v.size()) {
for (ViewsIncrementRequests::iterator i = _viewsIncrementRequests.begin(); i != _viewsIncrementRequests.cend(); ++i) {
if (i.value() == req) {
PeerData *peer = i.key();
ChannelId channel = peerToChannel(peer->id);
for (int32 j = 0, l = ids.size(); j < l; ++j) {
if (HistoryItem *item = App::histItemById(channel, ids.at(j).v)) {
item->setViewsCount(v.at(j).v);
}
}
_viewsIncrementRequests.erase(i);
break;
}
}
}
if (!_viewsToIncrement.isEmpty() && !_viewsIncrementTimer.isActive()) {
_viewsIncrementTimer.start(SendViewsTimeout);
}
}
bool MainWidget::viewsIncrementFail(const RPCError &error, mtpRequestId req) {
if (mtpIsFlood(error)) return false;
for (ViewsIncrementRequests::iterator i = _viewsIncrementRequests.begin(); i != _viewsIncrementRequests.cend(); ++i) {
if (i.value() == req) {
_viewsIncrementRequests.erase(i);
break;
}
}
if (!_viewsToIncrement.isEmpty() && !_viewsIncrementTimer.isActive()) {
_viewsIncrementTimer.start(SendViewsTimeout);
}
return false;
}
HistoryItem *MainWidget::atTopImportantMsg(int32 &bottomUnderScrollTop) const {
return history.atTopImportantMsg(bottomUnderScrollTop);
}
void MainWidget::createDialogAtTop(History *history, int32 unreadCount) {
dialogs.createDialogAtTop(history, unreadCount);
void MainWidget::createDialog(History *history) {
dialogs.createDialog(history);
}
void MainWidget::choosePeer(PeerId peerId, MsgId showAtMsgId) {
@ -2274,8 +2392,11 @@ void MainWidget::showPeerHistory(quint64 peerId, qint32 showAtMsgId, bool back)
if (noPeer) {
_topBar.hide();
resizeEvent(0);
} else if (wasActivePeer != activePeer() && activePeer()->isChannel()) {
activePeer()->asChannel()->ptsWaitingForShortPoll(WaitForChannelGetDifference);
} else if (wasActivePeer != activePeer()) {
if (activePeer()->isChannel()) {
activePeer()->asChannel()->ptsWaitingForShortPoll(WaitForChannelGetDifference);
}
_viewsIncremented.remove(activePeer());
}
if (!cWideMode() && !dialogs.isHidden()) dialogs.hide();
if (!animating()) {
@ -2526,7 +2647,7 @@ void MainWidget::windowShown() {
}
void MainWidget::sentUpdatesReceived(uint64 randomId, const MTPUpdates &result) {
handleUpdates(result, randomId);
feedUpdates(result, randomId);
App::emitPeerUpdated();
}
@ -2829,7 +2950,7 @@ void MainWidget::onUpdateNotifySettings() {
}
}
void MainWidget::feedUpdates(const MTPVector<MTPUpdate> &updates, bool skipMessageIds) {
void MainWidget::feedUpdateVector(const MTPVector<MTPUpdate> &updates, bool skipMessageIds) {
const QVector<MTPUpdate> &v(updates.c_vector().v);
for (QVector<MTPUpdate>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
if (skipMessageIds && i->type() == mtpc_updateMessageID) continue;
@ -2868,7 +2989,7 @@ void MainWidget::updSetState(int32 pts, int32 date, int32 qts, int32 seq) {
MTPUpdates v = i.value();
i = _bySeqUpdates.erase(i);
if (s == seq + 1) {
return handleUpdates(v);
return feedUpdates(v);
}
} else {
if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSkippedTimeout);
@ -2915,8 +3036,9 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
}
if (history.peer() == channel) {
history.updateToEndVisibility();
history.onListScroll();
}
// h->asChannelHistory()->getRangeDifference();
h->asChannelHistory()->getRangeDifference();
}
if (d.has_timeout()) timeout = d.vtimeout.v;
@ -2929,9 +3051,56 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
App::feedUsers(d.vusers);
App::feedChats(d.vchats, false);
_handlingChannelDifference = true;
feedMessageIds(d.vother_updates);
App::feedMsgs(d.vnew_messages, NewMessageUnread);
feedUpdates(d.vother_updates, true);
// feed messages and groups, copy from App::feedMsgs
History *h = App::history(channel->id);
const QVector<MTPMessage> &vmsgs(d.vnew_messages.c_vector().v);
QMap<uint64, int32> msgsIds;
for (int32 i = 0, l = vmsgs.size(); i < l; ++i) {
const MTPMessage &msg(vmsgs.at(i));
switch (msg.type()) {
case mtpc_message: {
const MTPDmessage &d(msg.c_message());
msgsIds.insert((uint64(uint32(d.vid.v)) << 32) | uint64(i), i + 1);
App::checkEntitiesAndViewsUpdate(d); // new message, index my forwarded messages to links overview
} break;
case mtpc_messageEmpty: msgsIds.insert((uint64(uint32(msg.c_messageEmpty().vid.v)) << 32) | uint64(i), i + 1); break;
case mtpc_messageService: msgsIds.insert((uint64(uint32(msg.c_messageService().vid.v)) << 32) | uint64(i), i + 1); break;
}
}
const QVector<MTPUpdate> &vother(d.vother_updates.c_vector().v);
for (int32 i = 0, l = vother.size(); i < l; ++i) {
if (vother.at(i).type() == mtpc_updateChannelGroup) {
const MTPDupdateChannelGroup &updateGroup(vother.at(i).c_updateChannelGroup());
if (updateGroup.vgroup.type() == mtpc_messageGroup) {
const MTPDmessageGroup &group(updateGroup.vgroup.c_messageGroup());
if (updateGroup.vchannel_id.v != peerToChannel(channel->id)) {
LOG(("API Error: updateChannelGroup with invalid channel_id returned in channelDifference, channelId: %1, channel_id: %2").arg(peerToChannel(channel->id)).arg(updateGroup.vchannel_id.v));
continue;
}
msgsIds.insert((uint64((uint32(group.vmin_id.v) + uint32(group.vmax_id.v)) / 2) << 32), -i - 1);
}
}
}
for (QMap<uint64, int32>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
if (i.value() > 0) { // add message
const MTPMessage &msg(vmsgs.at(i.value() - 1));
if (channel->id != peerFromMessage(msg)) {
LOG(("API Error: message with invalid peer returned in channelDifference, channelId: %1, peer: %2").arg(peerToChannel(channel->id)).arg(peerFromMessage(msg)));
continue; // wtf
}
h->addNewMessage(msg, NewMessageUnread);
} else { // add group
const MTPDupdateChannelGroup &updateGroup(vother.at(-i.value() - 1).c_updateChannelGroup());
h->asChannelHistory()->addNewGroup(updateGroup.vgroup);
}
}
feedUpdateVector(d.vother_updates, true);
_handlingChannelDifference = false;
if (d.has_timeout()) timeout = d.vtimeout.v;
flags = d.vflags.v;
@ -2951,6 +3120,52 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
App::emitPeerUpdated();
}
void MainWidget::gotRangeDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff) {
int32 flags = 0, nextRequestPts = 0;
switch (diff.type()) {
case mtpc_updates_channelDifferenceEmpty: {
const MTPDupdates_channelDifferenceEmpty &d(diff.c_updates_channelDifferenceEmpty());
flags = d.vflags.v;
nextRequestPts = d.vpts.v;
} break;
case mtpc_updates_channelDifferenceTooLong: {
const MTPDupdates_channelDifferenceTooLong &d(diff.c_updates_channelDifferenceTooLong());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
flags = d.vflags.v;
nextRequestPts = d.vpts.v;
} break;
case mtpc_updates_channelDifference: {
const MTPDupdates_channelDifference &d(diff.c_updates_channelDifference());
App::feedUsers(d.vusers);
App::feedChats(d.vchats, false);
_handlingChannelDifference = true;
feedMessageIds(d.vother_updates);
App::feedMsgs(d.vnew_messages, NewMessageUnread);
feedUpdateVector(d.vother_updates, true);
_handlingChannelDifference = false;
flags = d.vflags.v;
nextRequestPts = d.vpts.v;
} break;
}
if (!(flags & MTPupdates_ChannelDifference_flag_final)) {
if (History *h = App::historyLoaded(channel->id)) {
MTP_LOG(0, ("getChannelDifference { good - after not final channelDifference was received, validating history part }%1").arg(cTestMode() ? " TESTMODE" : ""));
h->asChannelHistory()->getRangeDifferenceNext(nextRequestPts);
}
}
App::emitPeerUpdated();
}
bool MainWidget::failChannelDifference(ChannelData *channel, const RPCError &error) {
if (mtpIsFlood(error)) return false;
@ -2963,7 +3178,6 @@ void MainWidget::gotState(const MTPupdates_State &state) {
const MTPDupdates_state &d(state.c_updates_state());
updSetState(d.vpts.v, d.vdate.v, d.vqts.v, d.vseq.v);
MTP::setGlobalDoneHandler(rpcDone(&MainWidget::updateReceived));
_lastUpdateTime = getms(true);
noUpdatesTimer.start(NoUpdatesTimeout);
_ptsWaiter.setRequesting(false);
@ -2982,7 +3196,6 @@ void MainWidget::gotDifference(const MTPupdates_Difference &diff) {
const MTPDupdates_differenceEmpty &d(diff.c_updates_differenceEmpty());
updSetState(_ptsWaiter.current(), d.vdate.v, updQts, d.vseq.v);
MTP::setGlobalDoneHandler(rpcDone(&MainWidget::updateReceived));
_lastUpdateTime = getms(true);
noUpdatesTimer.start(NoUpdatesTimeout);
@ -3104,7 +3317,7 @@ void MainWidget::feedDifference(const MTPVector<MTPUser> &users, const MTPVector
App::feedChats(chats, false);
feedMessageIds(other);
App::feedMsgs(msgs, NewMessageUnread);
feedUpdates(other, true);
feedUpdateVector(other, true);
history.peerMessagesUpdated();
}
@ -3188,7 +3401,6 @@ void MainWidget::getDifference() {
LOG(("Getting difference for %1, %2").arg(_ptsWaiter.current()).arg(updDate));
_ptsWaiter.setRequesting(true);
MTP::setGlobalDoneHandler(RPCDoneHandlerPtr(0));
MTP::send(MTPupdates_GetDifference(MTP_int(_ptsWaiter.current()), MTP_int(updDate), MTP_int(updQts)), rpcDone(&MainWidget::gotDifference), rpcFail(&MainWidget::failDifference));
}
@ -3213,7 +3425,7 @@ void MainWidget::getChannelDifference(ChannelData *channel, GetChannelDifference
if (activePeer() == channel) {
filter = MTP_channelMessagesFilterEmpty();
} else {
filter = MTP_channelMessagesFilterCollapsed();
filter = MTP_channelMessagesFilterEmpty(); //MTP_channelMessagesFilterCollapsed(); - not supported
if (History *history = App::historyLoaded(channel->id)) {
if (!history->asChannelHistory()->onlyImportant()) {
MsgId fixInScrollMsgId = 0;
@ -3225,7 +3437,7 @@ void MainWidget::getChannelDifference(ChannelData *channel, GetChannelDifference
}
}
}
MTP::send(MTPupdates_GetChannelDifference(channel->inputChannel, filter, MTP_int(channel->pts()), MTP_int(3/*MTPChannelGetDifferenceLimit*/)), rpcDone(&MainWidget::gotChannelDifference, channel), rpcFail(&MainWidget::failChannelDifference, channel));
MTP::send(MTPupdates_GetChannelDifference(channel->inputChannel, filter, MTP_int(channel->pts()), MTP_int(MTPChannelGetDifferenceLimit)), rpcDone(&MainWidget::gotChannelDifference, channel), rpcFail(&MainWidget::failChannelDifference, channel));
}
void MainWidget::mtpPing() {
@ -3327,6 +3539,26 @@ void MainWidget::onStickersInstalled(uint64 setId) {
history.stickersInstalled(setId);
}
void MainWidget::onFullPeerUpdated(PeerData *peer) {
emit peerUpdated(peer);
}
void MainWidget::onSelfParticipantUpdated(ChannelData *channel) {
History *h = App::historyLoaded(channel->id);
if (_updatedChannels.contains(channel)) {
_updatedChannels.remove(channel);
if ((h ? h : App::history(channel->id))->isEmpty()) {
checkPeerHistory(channel);
} else {
h->asChannelHistory()->checkJoinedMessage();
history.peerMessagesUpdated(channel->id);
}
} else if (h) {
h->asChannelHistory()->checkJoinedMessage();
history.peerMessagesUpdated(channel->id);
}
}
void MainWidget::usernameResolveDone(QPair<bool, QString> toProfileStartToken, const MTPcontacts_ResolvedPeer &result) {
App::wnd()->hideLayer();
if (result.type() != mtpc_contacts_resolvedPeer) return;
@ -3370,7 +3602,8 @@ void MainWidget::inviteCheckDone(QString hash, const MTPChatInvite &invite) {
switch (invite.type()) {
case mtpc_chatInvite: {
const MTPDchatInvite &d(invite.c_chatInvite());
ConfirmBox *box = new ConfirmBox(lng_group_invite_want_join(lt_title, qs(d.vtitle)), lang(lng_group_invite_join));
bool isChannel = (d.vflags.v & MTPDchatInvite_flag_is_channel);
ConfirmBox *box = new ConfirmBox((isChannel ? lng_group_invite_want_join_channel : lng_group_invite_want_join)(lt_title, qs(d.vtitle)), lang(lng_group_invite_join));
_inviteHash = hash;
connect(box, SIGNAL(confirmed()), this, SLOT(onInviteImport()));
App::wnd()->showLayer(box);
@ -3380,7 +3613,7 @@ void MainWidget::inviteCheckDone(QString hash, const MTPChatInvite &invite) {
const MTPDchatInviteAlready &d(invite.c_chatInviteAlready());
PeerData *chat = App::feedChats(MTP_vector<MTPChat>(1, d.vchat));
if (chat) {
if ((chat->isChat() && chat->asChat()->left) || (chat->isChannel() && chat->asChannel()->left)) {
if (chat->isChat() && chat->asChat()->haveLeft) {
ConfirmBox *box = new ConfirmBox(lng_group_invite_want_join(lt_title, chat->name), lang(lng_group_invite_join));
_inviteHash = '/' + QString::number(chat->id);
connect(box, SIGNAL(confirmed()), this, SLOT(onInviteImport()));
@ -3431,8 +3664,12 @@ void MainWidget::inviteImportDone(const MTPUpdates &updates) {
case mtpc_updatesTooLong: {
} break;
}
if (v && !v->isEmpty() && v->front().type() == mtpc_chat) {
App::main()->showPeerHistory(peerFromChat(v->front().c_chat().vid.v), ShowAtTheEndMsgId);
if (v && !v->isEmpty()) {
if (v->front().type() == mtpc_chat) {
App::main()->showPeerHistory(peerFromChat(v->front().c_chat().vid.v), ShowAtTheEndMsgId);
} else if (v->front().type() == mtpc_channel) {
App::main()->showPeerHistory(peerFromChannel(v->front().c_channel().vid.v), ShowAtTheEndMsgId);
}
}
}
@ -3453,7 +3690,7 @@ void MainWidget::startFull(const MTPVector<MTPUser> &users) {
start(v[0]);
}
void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history) {
void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *h) {
switch (settings.type()) {
case mtpc_peerNotifySettingsEmpty:
switch (peer.type()) {
@ -3468,8 +3705,11 @@ void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNoti
}
data->notify = EmptyNotifySettings;
App::unregMuted(data);
History *h = App::history(data->id);
if (!h) h = App::history(data->id);
h->setMute(false);
if (history.peer() == data) {
history.updateNotifySettings();
}
}
} break;
}
@ -3499,14 +3739,17 @@ void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNoti
setTo->previews = d.vshow_previews.v;
setTo->events = d.vevents_mask.v;
if (data) {
if (!history) history = App::history(data->id);
if (!h) h = App::history(data->id);
int32 changeIn = 0;
if (isNotifyMuted(setTo, &changeIn)) {
App::wnd()->notifyClear(history);
history->setMute(true);
App::wnd()->notifyClear(h);
h->setMute(true);
App::regMuted(data, changeIn);
} else {
history->setMute(false);
h->setMute(false);
}
if (history.peer() == data) {
history.updateNotifySettings();
}
}
} break;
@ -3528,6 +3771,7 @@ void MainWidget::gotNotifySetting(MTPInputNotifyPeer peer, const MTPPeerNotifySe
case mtpc_inputPeerSelf: applyNotifySetting(MTP_notifyPeer(MTP_peerUser(MTP_int(MTP::authedId()))), settings); break;
case mtpc_inputPeerUser: applyNotifySetting(MTP_notifyPeer(MTP_peerUser(peer.c_inputNotifyPeer().vpeer.c_inputPeerUser().vuser_id)), settings); break;
case mtpc_inputPeerChat: applyNotifySetting(MTP_notifyPeer(MTP_peerChat(peer.c_inputNotifyPeer().vpeer.c_inputPeerChat().vchat_id)), settings); break;
case mtpc_inputPeerChannel: applyNotifySetting(MTP_notifyPeer(MTP_peerChannel(peer.c_inputNotifyPeer().vpeer.c_inputPeerChannel().vchannel_id)), settings); break;
}
break;
}
@ -3563,6 +3807,7 @@ void MainWidget::updateNotifySetting(PeerData *peer, bool enabled) {
App::unregMuted(peer);
}
App::history(peer->id)->setMute(!enabled);
if (history.peer() == peer) history.updateNotifySettings();
updateNotifySettingTimer.start(NotifySettingSaveTimeout);
}
@ -3759,7 +4004,7 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) {
if (end <= from || !MTP::authedId()) return;
App::wnd()->checkAutoLock();
if (mtpTypeId(*from) == mtpc_new_session_created) {
MTPNewSession newSession(from, end);
updSeq = 0;
@ -3771,8 +4016,9 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) {
_lastUpdateTime = getms(true);
noUpdatesTimer.start(NoUpdatesTimeout);
handleUpdates(updates);
if (!_ptsWaiter.requesting()) {
feedUpdates(updates);
}
App::emitPeerUpdated();
} catch (mtpErrorUnexpected &e) { // just some other type
}
@ -3780,7 +4026,7 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) {
update();
}
void MainWidget::handleUpdates(const MTPUpdates &updates, uint64 randomId) {
void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
switch (updates.type()) {
case mtpc_updates: {
const MTPDupdates &d(updates.c_updates());
@ -3794,7 +4040,7 @@ void MainWidget::handleUpdates(const MTPUpdates &updates, uint64 randomId) {
App::feedUsers(d.vusers, false);
App::feedChats(d.vchats, false);
feedUpdates(d.vupdates);
feedUpdateVector(d.vupdates);
updSetState(0, d.vdate.v, updQts, d.vseq.v);
} break;
@ -3811,7 +4057,7 @@ void MainWidget::handleUpdates(const MTPUpdates &updates, uint64 randomId) {
App::feedUsers(d.vusers, false);
App::feedChats(d.vchats, false);
feedUpdates(d.vupdates);
feedUpdateVector(d.vupdates);
updSetState(0, d.vdate.v, updQts, d.vseq.v);
} break;
@ -3891,7 +4137,7 @@ void MainWidget::handleUpdates(const MTPUpdates &updates, uint64 randomId) {
item->setText(text, d.has_entities() ? linksFromMTP(d.ventities.c_vector().v) : LinksInText());
item->initDimensions();
itemResized(item);
if (!was && item->hasTextLinks()) {
if (!was && item->hasTextLinks() && (!item->history()->isChannel() || item->fromChannel())) {
item->history()->addToOverview(item, OverviewLinks);
}
}
@ -4255,15 +4501,40 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
case mtpc_updateChannel: {
const MTPDupdateChannel &d(update.c_updateChannel());
App::markPeerUpdated(App::channelLoaded(d.vchannel_id.v));
if (ChannelData *channel = App::channelLoaded(d.vchannel_id.v)) {
App::markPeerUpdated(channel);
if (channel->isForbidden || channel->wasKicked() || channel->haveLeft()) {
dialogs.removePeer(channel);
if (History *h = App::historyLoaded(channel->id)) {
h->clear(true);
h->asChannelHistory()->clearOther();
}
channel->ptsWaitingForShortPoll(-1);
channel->inviter = 0;
if (activePeer() == channel) {
showDialogs();
}
} else if (!channel->amCreator() && App::history(channel->id)) { // create history
_updatedChannels.insert(channel, true);
if (channel->inviter) {
checkPeerHistory(channel);
} else {
App::api()->requestSelfParticipant(channel);
}
}
}
} break;
case mtpc_updateNewChannelMessage: {
const MTPDupdateNewChannelMessage &d(update.c_updateNewChannelMessage());
ChannelData *channel = App::channelLoaded(peerToChannel(peerFromMessage(d.vmessage)));
if (channel && !channel->ptsUpdated(d.vpts.v, d.vpts_count.v, update)) {
return;
if (channel && !_handlingChannelDifference) {
if (channel->ptsRequesting()) { // skip global updates while getting channel difference
return;
} else if (!channel->ptsUpdated(d.vpts.v, d.vpts_count.v, update)) {
return;
}
}
// update before applying skipped
@ -4275,13 +4546,14 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
history.peerMessagesUpdated(item->history()->peer->id);
}
if (channel) {
if (channel && !_handlingChannelDifference) {
channel->ptsApplySkippedUpdates();
}
} break;
case mtpc_updateReadChannelInbox: {
const MTPDupdateReadChannelInbox &d(update.c_updateReadChannelInbox());
ChannelData *channel = App::channelLoaded(d.vchannel_id.v);
App::feedInboxRead(peerFromChannel(d.vchannel_id.v), d.vmax_id.v);
} break;
@ -4289,27 +4561,26 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
const MTPDupdateDeleteChannelMessages &d(update.c_updateDeleteChannelMessages());
ChannelData *channel = App::channelLoaded(d.vchannel_id.v);
if (channel && !channel->ptsUpdated(d.vpts.v, d.vpts_count.v, update)) {
return;
if (channel && !_handlingChannelDifference) {
if (channel->ptsRequesting()) { // skip global updates while getting channel difference
return;
} else if (!channel->ptsUpdated(d.vpts.v, d.vpts_count.v, update)) {
return;
}
}
// update before applying skipped
App::feedWereDeleted(d.vchannel_id.v, d.vmessages.c_vector().v);
history.peerMessagesUpdated();
if (channel) {
if (channel && !_handlingChannelDifference) {
channel->ptsApplySkippedUpdates();
}
} break;
case mtpc_updateChannelGroup: {
const MTPDupdateChannelGroup &d(update.c_updateChannelGroup());
ChannelData *channel = App::channelLoaded(d.vchannel_id.v);
if (channel) {
if (d.vgroup.type() == mtpc_messageGroup) {
const MTPDmessageGroup &data(d.vgroup.c_messageGroup());
// CHANNELS_FULL
}
if (!_handlingChannelDifference) {
LOG(("API Error: got updateChannelGroup not in channelDifference!"));
}
} break;

View File

@ -217,7 +217,7 @@ public:
void activate();
void createDialogAtTop(History *history, int32 unreadCount);
void createDialog(History *history);
void dlgUpdated(DialogRow *row);
void dlgUpdated(History *row);
@ -324,7 +324,7 @@ public:
void sendBotCommand(const QString &cmd, MsgId msgId);
void insertBotCommand(const QString &cmd);
void searchMessages(const QString &query);
void searchMessages(const QString &query, PeerData *inPeer);
void preloadOverviews(PeerData *peer);
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
void changingMsgId(HistoryItem *row, MsgId newId);
@ -365,7 +365,7 @@ public:
void fillForwardingInfo(Text *&from, Text *&text, bool &serviceColor, ImagePtr &preview);
void updateForwardingTexts();
void cancelForwarding();
void finishForwarding(History *hist); // send them
void finishForwarding(History *hist, bool broadcast); // send them
void audioMarkRead(AudioData *data);
void videoMarkRead(VideoData *data);
@ -383,15 +383,20 @@ public:
void contactsReceived();
void ptsWaiterStartTimerFor(ChannelData *channel, int32 ms); // ms <= 0 - stop timer
void handleUpdates(const MTPUpdates &updates, uint64 randomId = 0);
void feedUpdates(const MTPUpdates &updates, uint64 randomId = 0);
void feedUpdate(const MTPUpdate &update);
void updateAfterDrag();
void ctrlEnterSubmitUpdated();
void setInnerFocus();
void scheduleViewIncrement(HistoryItem *item);
HistoryItem *atTopImportantMsg(int32 &bottomUnderScrollTop) const;
void gotRangeDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff);
void onSelfParticipantUpdated(ChannelData *channel);
~MainWidget();
signals:
@ -459,6 +464,9 @@ public slots:
void onUpdateMuted();
void onStickersInstalled(uint64 setId);
void onFullPeerUpdated(PeerData *peer);
void onViewsIncrement();
private:
@ -503,7 +511,7 @@ private:
bool failChannelDifference(ChannelData *channel, const RPCError &err);
void failDifferenceStartTimerFor(ChannelData *channel);
void feedUpdates(const MTPVector<MTPUpdate> &updates, bool skipMessageIds = false);
void feedUpdateVector(const MTPVector<MTPUpdate> &updates, bool skipMessageIds = false);
void feedMessageIds(const MTPVector<MTPUpdate> &updates);
void updateReceived(const mtpPrime *from, const mtpPrime *end);
@ -591,12 +599,27 @@ private:
SingleTimer _failDifferenceTimer;
uint64 _lastUpdateTime;
bool _handlingChannelDifference;
QPixmap _cachedBackground;
QRect _cachedFor, _willCacheFor;
int _cachedX, _cachedY;
SingleTimer _cacheBackgroundTimer;
typedef QMap<ChannelData*, bool> UpdatedChannels;
UpdatedChannels _updatedChannels;
typedef QMap<MsgId, bool> ViewsIncrementMap;
typedef QMap<PeerData*, ViewsIncrementMap> ViewsIncrement;
ViewsIncrement _viewsIncremented, _viewsToIncrement;
typedef QMap<PeerData*, mtpRequestId> ViewsIncrementRequests;
ViewsIncrementRequests _viewsIncrementRequests;
typedef QMap<mtpRequestId, PeerData*> ViewsIncrementByRequest;
ViewsIncrementByRequest _viewsIncrementByRequest;
SingleTimer _viewsIncrementTimer;
void viewsIncrementDone(QVector<MTPint> ids, const MTPVector<MTPint> &result, mtpRequestId req);
bool viewsIncrementFail(const RPCError &error, mtpRequestId req);
App::WallPaper *_background;
ApiWrap *_api;

View File

@ -661,8 +661,8 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) {
_index = -1;
_msgid = context ? context->id : 0;
_channel = context ? context->channelId() : NoChannel;
_canForward = _msgid > 0 && (_channel == NoChannel);
_canDelete = (_channel == NoChannel) || context->history()->peer->asChannel()->adminned;
_canForward = _msgid > 0;
_canDelete = context ? context->canDelete() : false;
_photo = photo;
if (_history) {
_overview = OverviewPhotos;
@ -734,8 +734,8 @@ void MediaView::showDocument(DocumentData *doc, HistoryItem *context) {
_index = -1;
_msgid = context ? context->id : 0;
_channel = context ? context->channelId() : NoChannel;
_canForward = _msgid > 0 && (_channel == NoChannel);
_canDelete = (_channel == NoChannel) || context->history()->peer->asChannel()->adminned;
_canForward = _msgid > 0;
_canDelete = context ? context->canDelete() : false;
if (_history) {
_overview = OverviewDocuments;
@ -1396,8 +1396,8 @@ void MediaView::moveToNext(int32 delta) {
if (HistoryItem *item = App::histItemById(_history->channelId(), _history->overview[_overview][_index])) {
_msgid = item->id;
_channel = item->channelId();
_canForward = _msgid > 0 && (_channel == NoChannel);
_canDelete = (_channel == NoChannel) || item->history()->peer->asChannel()->adminned;
_canForward = _msgid > 0;
_canDelete = item->canDelete();
if (item->getMedia()) {
switch (item->getMedia()->type()) {
case MediaTypePhoto: displayPhoto(static_cast<HistoryPhoto*>(item->getMedia())->photo(), item); preloadData(delta); break;
@ -1661,10 +1661,21 @@ void MediaView::updateOver(QPoint pos) {
void MediaView::mouseReleaseEvent(QMouseEvent *e) {
updateOver(e->pos());
if (textlnkDown() && textlnkOver() == textlnkDown()) {
textlnkDown()->onClick(e->button());
}
TextLinkPtr lnk = textlnkDown();
textlnkDown(TextLinkPtr());
if (lnk && textlnkOver() == lnk) {
if (reHashtag().match(lnk->encoded()).hasMatch() && _history && _history->isChannel()) {
App::wnd()->hideMediaview();
App::searchByHashtag(lnk->encoded(), _history->peer);
} else {
if (reBotCommand().match(lnk->encoded()).hasMatch() && _history->peer->isUser() && _history->peer->asUser()->botInfo) {
App::wnd()->hideMediaview();
App::main()->showPeerHistory(_history->peer->id, ShowAtTheEndMsgId);
}
lnk->onClick(e->button());
}
return;
}
if (_over == OverName && _down == OverName) {
if (App::wnd() && _from) {
close();
@ -1924,8 +1935,10 @@ void MediaView::updateHeader() {
_headerText = _doc->name.isEmpty() ? lang(lng_mediaview_doc_image) : _doc->name;
} else if (_user) {
_headerText = lang(lng_mediaview_profile_photo);
} else if (_peer) {
} else if (_channel) {
_headerText = lang(lng_mediaview_group_photo);
} else if (_peer) {
_headerText = lang(lng_mediaview_channel_photo);
} else {
_headerText = lang(lng_mediaview_single_photo);
}

View File

@ -54,17 +54,24 @@ enum {
MTPDstickerSet_flag_official = (1 << 2),
MTPDstickerSet_flag_NOT_LOADED = (1 << 31), // client side flag for not yet loaded set
MTPDchannel_flag_am_admin = (1 << 0),
MTPDchannel_flag_am_creator = (1 << 0),
MTPDchannel_flag_was_kicked = (1 << 1),
MTPDchannel_flag_have_left = (1 << 2),
MTPDchannel_flag_am_publisher = (1 << 3),
MTPDchannel_flag_am_editor = (1 << 3),
MTPDchannel_flag_am_moderator = (1 << 4),
MTPDchannel_flag_is_broadcast = (1 << 5),
MTPDchannel_flag_is_verified = (1 << 7),
MTPDchannelFull_flag_can_view_participants = (1 << 3),
MTPDchat_flag_creator = (1 << 0),
MTPDchat_flag_kicked = (1 << 1),
MTPDchat_flag_left = (1 << 2),
MTPDchatInvite_flag_is_channel = (1 << 0),
MTPDchatInvite_flag_is_broadcast = (1 << 1),
MTPDchatInvite_flag_is_public = (1 << 2),
MTPupdates_ChannelDifference_flag_final = (1 << 0),
MTPDchannelMessagesFilter_flag_only_important = (1 << 0),

View File

@ -4202,7 +4202,8 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string); 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(" title: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
@ -5358,6 +5359,21 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
}
break;
case mtpc_channels_reportSpam:
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ channels_reportSpam");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" channel: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" user_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_int); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
case mtpc_channels_editAbout:
if (stage) {
to.add(",\n").addSpaces(lev);
@ -6044,6 +6060,20 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
}
break;
case mtpc_channels_deleteUserHistory:
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ channels_deleteUserHistory");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" channel: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" user_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
case mtpc_messages_deleteMessages:
if (stage) {
to.add(",\n").addSpaces(lev);

View File

@ -371,7 +371,7 @@ enum {
mtpc_chatInviteEmpty = 0x69df3769,
mtpc_chatInviteExported = 0xfc2e05bc,
mtpc_chatInviteAlready = 0x5a686d7c,
mtpc_chatInvite = 0xce917dcd,
mtpc_chatInvite = 0x93e99b60,
mtpc_inputStickerSetEmpty = 0xffb62b95,
mtpc_inputStickerSetID = 0x9de7a269,
mtpc_inputStickerSetShortName = 0x861cc8a0,
@ -547,6 +547,8 @@ enum {
mtpc_channels_getImportantHistory = 0xddb929cb,
mtpc_channels_readHistory = 0xcc104937,
mtpc_channels_deleteMessages = 0x84c1fd4e,
mtpc_channels_deleteUserHistory = 0xd10dd71b,
mtpc_channels_reportSpam = 0xfe087810,
mtpc_channels_getMessages = 0x93d7b347,
mtpc_channels_getParticipants = 0x24d98f92,
mtpc_channels_getParticipant = 0x546dd7a6,
@ -7631,7 +7633,7 @@ private:
explicit MTPchatInvite(MTPDchatInvite *_data);
friend MTPchatInvite MTP_chatInviteAlready(const MTPChat &_chat);
friend MTPchatInvite MTP_chatInvite(const MTPstring &_title);
friend MTPchatInvite MTP_chatInvite(MTPint _flags, const MTPstring &_title);
mtpTypeId _type;
};
@ -11816,9 +11818,10 @@ class MTPDchatInvite : public mtpDataImpl<MTPDchatInvite> {
public:
MTPDchatInvite() {
}
MTPDchatInvite(const MTPstring &_title) : vtitle(_title) {
MTPDchatInvite(MTPint _flags, const MTPstring &_title) : vflags(_flags), vtitle(_title) {
}
MTPint vflags;
MTPstring vtitle;
};
@ -17853,6 +17856,93 @@ public:
}
};
class MTPchannels_deleteUserHistory { // RPC method 'channels.deleteUserHistory'
public:
MTPInputChannel vchannel;
MTPInputUser vuser_id;
MTPchannels_deleteUserHistory() {
}
MTPchannels_deleteUserHistory(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_channels_deleteUserHistory) {
read(from, end, cons);
}
MTPchannels_deleteUserHistory(const MTPInputChannel &_channel, const MTPInputUser &_user_id) : vchannel(_channel), vuser_id(_user_id) {
}
uint32 innerLength() const {
return vchannel.innerLength() + vuser_id.innerLength();
}
mtpTypeId type() const {
return mtpc_channels_deleteUserHistory;
}
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_channels_deleteUserHistory) {
vchannel.read(from, end);
vuser_id.read(from, end);
}
void write(mtpBuffer &to) const {
vchannel.write(to);
vuser_id.write(to);
}
typedef MTPmessages_AffectedHistory ResponseType;
};
class MTPchannels_DeleteUserHistory : public MTPBoxed<MTPchannels_deleteUserHistory> {
public:
MTPchannels_DeleteUserHistory() {
}
MTPchannels_DeleteUserHistory(const MTPchannels_deleteUserHistory &v) : MTPBoxed<MTPchannels_deleteUserHistory>(v) {
}
MTPchannels_DeleteUserHistory(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPchannels_deleteUserHistory>(from, end, cons) {
}
MTPchannels_DeleteUserHistory(const MTPInputChannel &_channel, const MTPInputUser &_user_id) : MTPBoxed<MTPchannels_deleteUserHistory>(MTPchannels_deleteUserHistory(_channel, _user_id)) {
}
};
class MTPchannels_reportSpam { // RPC method 'channels.reportSpam'
public:
MTPInputChannel vchannel;
MTPInputUser vuser_id;
MTPVector<MTPint> vid;
MTPchannels_reportSpam() {
}
MTPchannels_reportSpam(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_channels_reportSpam) {
read(from, end, cons);
}
MTPchannels_reportSpam(const MTPInputChannel &_channel, const MTPInputUser &_user_id, const MTPVector<MTPint> &_id) : vchannel(_channel), vuser_id(_user_id), vid(_id) {
}
uint32 innerLength() const {
return vchannel.innerLength() + vuser_id.innerLength() + vid.innerLength();
}
mtpTypeId type() const {
return mtpc_channels_reportSpam;
}
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_channels_reportSpam) {
vchannel.read(from, end);
vuser_id.read(from, end);
vid.read(from, end);
}
void write(mtpBuffer &to) const {
vchannel.write(to);
vuser_id.write(to);
vid.write(to);
}
typedef MTPBool ResponseType;
};
class MTPchannels_ReportSpam : public MTPBoxed<MTPchannels_reportSpam> {
public:
MTPchannels_ReportSpam() {
}
MTPchannels_ReportSpam(const MTPchannels_reportSpam &v) : MTPBoxed<MTPchannels_reportSpam>(v) {
}
MTPchannels_ReportSpam(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPchannels_reportSpam>(from, end, cons) {
}
MTPchannels_ReportSpam(const MTPInputChannel &_channel, const MTPInputUser &_user_id, const MTPVector<MTPint> &_id) : MTPBoxed<MTPchannels_reportSpam>(MTPchannels_reportSpam(_channel, _user_id, _id)) {
}
};
class MTPchannels_getMessages { // RPC method 'channels.getMessages'
public:
MTPInputChannel vchannel;
@ -27150,7 +27240,7 @@ inline uint32 MTPchatInvite::innerLength() const {
}
case mtpc_chatInvite: {
const MTPDchatInvite &v(c_chatInvite());
return v.vtitle.innerLength();
return v.vflags.innerLength() + v.vtitle.innerLength();
}
}
return 0;
@ -27170,6 +27260,7 @@ inline void MTPchatInvite::read(const mtpPrime *&from, const mtpPrime *end, mtpT
case mtpc_chatInvite: _type = cons; {
if (!data) setData(new MTPDchatInvite());
MTPDchatInvite &v(_chatInvite());
v.vflags.read(from, end);
v.vtitle.read(from, end);
} break;
default: throw mtpErrorUnexpected(cons, "MTPchatInvite");
@ -27183,6 +27274,7 @@ inline void MTPchatInvite::write(mtpBuffer &to) const {
} break;
case mtpc_chatInvite: {
const MTPDchatInvite &v(c_chatInvite());
v.vflags.write(to);
v.vtitle.write(to);
} break;
}
@ -27201,8 +27293,8 @@ inline MTPchatInvite::MTPchatInvite(MTPDchatInvite *_data) : mtpDataOwner(_data)
inline MTPchatInvite MTP_chatInviteAlready(const MTPChat &_chat) {
return MTPchatInvite(new MTPDchatInviteAlready(_chat));
}
inline MTPchatInvite MTP_chatInvite(const MTPstring &_title) {
return MTPchatInvite(new MTPDchatInvite(_title));
inline MTPchatInvite MTP_chatInvite(MTPint _flags, const MTPstring &_title) {
return MTPchatInvite(new MTPDchatInvite(_flags, _title));
}
inline uint32 MTPinputStickerSet::innerLength() const {

View File

@ -544,7 +544,7 @@ chatInviteEmpty#69df3769 = ExportedChatInvite;
chatInviteExported#fc2e05bc link:string = ExportedChatInvite;
chatInviteAlready#5a686d7c chat:Chat = ChatInvite;
chatInvite#ce917dcd title:string = ChatInvite;
chatInvite#93e99b60 flags:# title:string = ChatInvite;
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
@ -754,6 +754,8 @@ channels.getDialogs#a9d3d249 offset:int limit:int = messages.Dialogs;
channels.getImportantHistory#ddb929cb channel:InputChannel offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
channels.deleteUserHistory#d10dd71b channel:InputChannel user_id:InputUser = messages.AffectedHistory;
channels.reportSpam#fe087810 channel:InputChannel user_id:InputUser id:Vector<int> = Bool;
channels.getMessages#93d7b347 channel:InputChannel id:Vector<int> = messages.Messages;
channels.getParticipants#24d98f92 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int = channels.ChannelParticipants;
channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant;

View File

@ -1799,7 +1799,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (App::hoveredLinkItem()->toHistoryMessage()) {
_menu->addAction(lang(lng_context_forward_msg), this, SLOT(forwardMessage()))->setEnabled(true);
}
if (!_peer->isChannel() || _peer->asChannel()->adminned || App::hoveredLinkItem()->out()) {
if (App::hoveredLinkItem()->canDelete()) {
_menu->addAction(lang(lng_context_delete_msg), this, SLOT(deleteMessage()))->setEnabled(true);
}
}
@ -1839,7 +1839,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (App::mousedItem()->toHistoryMessage()) {
_menu->addAction(lang(lng_context_forward_msg), this, SLOT(forwardMessage()))->setEnabled(true);
}
if (!_peer->isChannel() || _peer->asChannel()->adminned || App::mousedItem()->out()) {
if (App::mousedItem()->canDelete()) {
_menu->addAction(lang(lng_context_delete_msg), this, SLOT(deleteMessage()))->setEnabled(true);
}
}
@ -2100,10 +2100,8 @@ void OverviewInner::getSelectionState(int32 &selectedForForward, int32 &selected
selectedForForward = selectedForDelete = 0;
for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) {
if (i.value() == FullItemSel) {
if (!_peer || !_peer->isChannel() || _peer->asChannel()->adminned) {
++selectedForDelete;
} else if (HistoryItem *item = App::histItemById(_channel, i.key())) {
if (item->out()) {
if (HistoryItem *item = App::histItemById(_channel, i.key())) {
if (item->canDelete()) {
++selectedForDelete;
}
}

View File

@ -31,7 +31,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const PeerData *peer) : TWidget(0),
_profile(profile), _scroll(scroll), _peer(App::peer(peer->id)),
_peerUser(_peer->asUser()), _peerChat(_peer->asChat()), _peerChannel(_peer->asChannel()), _hist(App::history(peer->id)),
_isAdmin(_peerChat ? (_peerChat->admin == MTP::authedId()) : (_peerChannel ? _peerChannel->adminned : false)),
_amCreator(_peerChat ? (_peerChat->creator == MTP::authedId()) : (_peerChannel ? _peerChannel->amCreator() : false)),
_width(0), _left(0), _addToHeight(0),
@ -47,6 +47,8 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
_invitationLink(this, qsl("telegram.me/joinchat/")),
_botSettings(this, lang(lng_profile_bot_settings)),
_botHelp(this, lang(lng_profile_bot_help)),
_editLink(this, lang((_peerChannel && _peerChannel->isPublic()) ? lng_profile_edit_public_link : lng_profile_create_public_link)),
_username(this, qsl("https://telegram.me/") + (_peerChannel ? _peerChannel->username : QString())),
// about
_about(st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right()),
@ -64,10 +66,11 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
// actions
_searchInPeer(this, lang(lng_profile_search_messages)),
_clearHistory(this, lang(lng_profile_clear_history)),
_deleteConversation(this, lang(_peer->isUser() ? lng_profile_delete_conversation : lng_profile_clear_and_exit)),
_deleteConversation(this, lang(_peer->isUser() ? lng_profile_delete_conversation : (_peer->isChat() ? lng_profile_clear_and_exit : lng_profile_leave_channel))),
_wasBlocked(_peerUser ? _peerUser->blocked : UserBlockUnknown),
_blockRequest(0),
_blockUser(this, lang((_peerUser && _peerUser->botInfo) ? lng_profile_block_bot : lng_profile_block_user), st::btnRedLink),
_deleteChannel(this, lang(lng_profile_delete_channel), st::btnRedLink),
// participants
_pHeight(st::profileListPhotoSize + st::profileListPadding.height() * 2),
@ -104,9 +107,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
if (chatPhoto && chatPhoto->date) {
_photoLink = TextLinkPtr(new PhotoLink(chatPhoto, _peer));
}
if (_peerChannel->photoId == UnknownPeerPhotoId || (_peerChannel->invitationUrl.isEmpty() && _peerChannel->adminned)) {
App::api()->requestFullPeer(_peer);
}
_peerChannel->updateFull();
}
// profile
@ -119,7 +120,9 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
connect(&_cancelPhoto, SIGNAL(clicked()), this, SLOT(onUpdatePhotoCancel()));
connect(&_createInvitationLink, SIGNAL(clicked()), this, SLOT(onCreateInvitationLink()));
connect(&_invitationLink, SIGNAL(clicked()), this, SLOT(onInvitationLink()));
connect(&_username, SIGNAL(clicked()), this, SLOT(onPublicLink()));
_invitationLink.setAcceptBoth(true);
_username.setAcceptBoth(true);
updateInvitationLink();
if (_peerChat) {
@ -142,6 +145,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
connect(&_botSettings, SIGNAL(clicked()), this, SLOT(onBotSettings()));
connect(&_botHelp, SIGNAL(clicked()), this, SLOT(onBotHelp()));
connect(&_editLink, SIGNAL(clicked()), this, SLOT(onEditPublicLink()));
connect(App::app(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUpdateDone(PeerId)));
connect(App::app(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUpdateFail(PeerId)));
@ -157,6 +161,9 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
}
updateBotLinksVisibility();
} else {
if (_peerChannel && !_peerChannel->about.isEmpty()) {
_about.setText(st::linkFont, _peerChannel->about, _historyTextOptions);
}
_botSettings.hide();
_botHelp.hide();
}
@ -177,6 +184,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
connect(&_clearHistory, SIGNAL(clicked()), this, SLOT(onClearHistory()));
connect(&_deleteConversation, SIGNAL(clicked()), this, SLOT(onDeleteConversation()));
connect(&_blockUser, SIGNAL(clicked()), this, SLOT(onBlockUser()));
connect(&_deleteChannel, SIGNAL(clicked()), this, SLOT(onDeleteChannel()));
App::contextItem(0);
@ -263,6 +271,7 @@ void ProfileInner::onUpdatePhoto() {
}
void ProfileInner::onClearHistory() {
if (_peerChannel) return;
ConfirmBox *box = new ConfirmBox(_peer->isUser() ? lng_sure_delete_history(lt_contact, _peer->name) : lng_sure_delete_group_history(lt_group, _peer->name));
connect(box, SIGNAL(confirmed()), this, SLOT(onClearHistorySure()));
App::wnd()->showLayer(box);
@ -274,7 +283,7 @@ void ProfileInner::onClearHistorySure() {
}
void ProfileInner::onDeleteConversation() {
ConfirmBox *box = new ConfirmBox(_peer->isUser() ? lng_sure_delete_history(lt_contact, _peer->name) : lng_sure_delete_and_exit(lt_group, _peer->name));
ConfirmBox *box = new ConfirmBox(_peer->isUser() ? lng_sure_delete_history(lt_contact, _peer->name) : (_peer->isChat() ? lng_sure_delete_and_exit(lt_group, _peer->name) : lang(lng_sure_leave_channel)));
connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteConversationSure()));
App::wnd()->showLayer(box);
}
@ -286,10 +295,27 @@ void ProfileInner::onDeleteConversationSure() {
App::wnd()->hideLayer();
App::main()->showDialogs();
MTP::send(MTPmessages_DeleteChatUser(_peerChat->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, _peer), App::main()->rpcFail(&MainWidget::leaveChatFailed, _peer));
} else if (_peerChannel) { // CHANNELS_UX
} else if (_peerChannel) {
App::wnd()->hideLayer();
App::main()->showDialogs();
MTP::send(MTPchannels_LeaveChannel(_peerChannel->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived));
}
}
void ProfileInner::onDeleteChannel() {
if (!_peerChannel) return;
ConfirmBox *box = new ConfirmBox(lang(lng_sure_delete_channel), lang(lng_selected_delete_confirm), QString(), st::btnRedDone);
connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteChannelSure()));
App::wnd()->showLayer(box);
}
void ProfileInner::onDeleteChannelSure() {
if (_peerChannel) {
App::wnd()->hideLayer();
App::main()->showDialogs();
MTP::send(MTPchannels_DeleteChannel(_peerChannel->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived));
}
}
void ProfileInner::onBlockUser() {
if (!_peerUser || _blockRequest) return;
if (_peerUser->blocked == UserIsBlocked) {
@ -371,6 +397,13 @@ void ProfileInner::onInvitationLink() {
App::wnd()->showLayer(new ConfirmBox(lang(lng_group_invite_copied), true));
}
void ProfileInner::onPublicLink() {
if (!_peerChannel || !_peerChannel->isPublic()) return;
QApplication::clipboard()->setText(qsl("https://telegram.me/") + _peerChannel->username);
App::wnd()->showLayer(new ConfirmBox(lang(lng_channel_public_link_copied), true));
}
void ProfileInner::onCreateInvitationLink() {
if (!_peerChat && !_peerChannel) return;
@ -426,6 +459,11 @@ void ProfileInner::onFullPeerUpdated(PeerData *peer) {
resizeEvent(0);
} else if (_peerChannel) {
updateInvitationLink();
if (_peerChannel->about.isEmpty()) {
_about = Text(st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right());
} else {
_about.setText(st::linkFont, _peerChannel->about, _historyTextOptions);
}
showAll();
resizeEvent(0);
}
@ -459,6 +497,10 @@ void ProfileInner::onBotHelp() {
updateBotLinksVisibility();
}
void ProfileInner::onEditPublicLink() {
App::wnd()->showLayer(new SetupChannelBox(_peerChannel, true), true);
}
void ProfileInner::peerUpdated(PeerData *data) {
if (data == _peer) {
PhotoData *photo = 0;
@ -473,6 +515,9 @@ void ProfileInner::peerUpdated(PeerData *data) {
if (_peerChat->photoId && _peerChat->photoId != UnknownPeerPhotoId) photo = App::photo(_peerChat->photoId);
} else if (_peerChannel) {
if (_peerChannel->photoId && _peerChannel->photoId != UnknownPeerPhotoId) photo = App::photo(_peerChannel->photoId);
if (_peerChannel->isPublic() != _invitationLink.isHidden()) {
peerUsernameChanged();
}
}
_photoLink = (photo && photo->date) ? TextLinkPtr(new PhotoLink(photo, _peer)) : TextLinkPtr();
if (_peer->name != _nameCache) {
@ -509,7 +554,7 @@ void ProfileInner::updateOnlineDisplayTimer() {
void ProfileInner::reorderParticipants() {
int32 was = _participants.size(), t = unixtime(), onlineCount = 0;
if (_peerChat && !_peerChat->forbidden) {
if (_peerChat && !_peerChat->isForbidden) {
if (_peerChat->count <= 0 || !_peerChat->participants.isEmpty()) {
_participants.clear();
for (ParticipantsData::iterator i = _participantsData.begin(), e = _participantsData.end(); i != e; ++i) {
@ -555,7 +600,7 @@ void ProfileInner::reorderParticipants() {
if (_peerUser) {
_onlineText = App::onlineText(_peerUser, t, true);
} else if (_peerChannel) {
_onlineText = lang(lng_channel_status);
_onlineText = _peerChannel->count ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(lng_channel_status);
} else {
_onlineText = lang(lng_chat_status_unaccessible);
}
@ -568,6 +613,20 @@ void ProfileInner::reorderParticipants() {
void ProfileInner::start() {
}
void ProfileInner::peerUsernameChanged() {
if (_peerChannel) {
if (_peerChannel->isPublic()) {
_username.setText(qsl("https://telegram.me/") + _peerChannel->username);
_editLink.setText(lang(lng_profile_edit_public_link));
} else {
_editLink.setText(lang(lng_profile_create_public_link));
}
resizeEvent(0);
showAll();
}
update();
}
bool ProfileInner::event(QEvent *e) {
if (e->type() == QEvent::MouseMove) {
_lastPos = static_cast<QMouseEvent*>(e)->globalPos();
@ -586,7 +645,7 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
// profile
top += st::profilePadding.top();
if (_photoLink || !_peerChat || _peerChat->forbidden) {
if (_photoLink || !_peerChat || _peerChat->isForbidden) {
p.drawPixmap(_left, top, _peer->photo->pix(st::profilePhotoSize));
} else {
if (a_photo.current() < 1) {
@ -610,9 +669,11 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
}
p.setPen((_peerUser && App::onlineColorUse(_peerUser, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p);
p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop + st::linkFont->ascent, _onlineText);
if (_isAdmin && ((_peerChat && !_peerChat->invitationUrl.isEmpty()) || (_peerChannel && !_peerChannel->invitationUrl.isEmpty()))) {
p.setPen(st::black->p);
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _createInvitationLink.y() + st::linkFont->ascent, lang(lng_group_invite_link));
if (_amCreator && ((_peerChat && !_peerChat->invitationUrl.isEmpty()) || (_peerChannel && !_peerChannel->invitationUrl.isEmpty()))) {
if (!_peerChannel || !_peerChannel->isPublic()) {
p.setPen(st::black->p);
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _createInvitationLink.y() + st::linkFont->ascent, lang(lng_group_invite_link));
}
}
if (!_cancelPhoto.isHidden()) {
p.setPen(st::profileOfflineColor->p);
@ -632,15 +693,23 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
top += st::profilePhotoSize;
top += st::profileButtonTop;
if (_peerChat && _peerChat->forbidden) {
if (_peerChat && _peerChat->isForbidden) {
int32 w = st::btnShareContact.font->m.width(lang(lng_profile_chat_unaccessible));
p.setFont(st::btnShareContact.font->f);
p.setPen(st::profileOfflineColor->p);
p.drawText(_left + (_width - w) / 2, top + st::btnShareContact.textTop + st::btnShareContact.font->ascent, lang(lng_profile_chat_unaccessible));
}
if (!_peerChannel || _isAdmin) {
if (!_peerChannel || _amCreator) {
top += _shareContact.height();
}
if (_peerChannel && (_amCreator || _peerChannel->isPublic())) {
if (!_amCreator) {
top += st::setLittleSkip;
} else {
top += st::setSectionSkip;
}
top += _editLink.height();
}
// about
if (!_about.isEmpty()) {
@ -691,8 +760,18 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_actions_section));
top += st::profileHeaderSkip;
top += _searchInPeer.height() + st::setLittleSkip + _clearHistory.height() + st::setLittleSkip + _deleteConversation.height();
if (_peerUser && peerToUser(_peerUser->id) != MTP::authedId()) top += st::setSectionSkip + _blockUser.height();
top += _searchInPeer.height();
if (_peerUser || _peerChat) {
top += st::setLittleSkip + _clearHistory.height();
}
if (_peerUser || _peerChat || !_amCreator) {
top += st::setLittleSkip + _deleteConversation.height();
}
if (_peerUser && peerToUser(_peerUser->id) != MTP::authedId()) {
top += st::setSectionSkip + _blockUser.height();
} else if (_peerChannel && _amCreator) {
top += st::setSectionSkip + _deleteChannel.height();
}
// participants
if (_peerChat && (_peerChat->count > 0 || !_participants.isEmpty())) {
@ -729,7 +808,7 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
} else {
data->online = App::onlineText(user, l_time);
}
data->cankick = (user != App::self()) && (_isAdmin || (_peerChat->cankick.constFind(user) != _peerChat->cankick.cend()));
data->cankick = (user != App::self()) && (_amCreator || (_peerChat->cankick.constFind(user) != _peerChat->cankick.cend()));
}
p.setPen(st::profileListNameColor->p);
p.setFont(st::linkFont->f);
@ -764,12 +843,12 @@ void ProfileInner::mouseMoveEvent(QMouseEvent *e) {
bool photoOver = QRect(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize).contains(e->pos());
if (photoOver != _photoOver) {
_photoOver = photoOver;
if (!_photoLink && _peerChat && !_peerChat->forbidden) {
if (!_photoLink && _peerChat && !_peerChat->isForbidden) {
a_photo.start(_photoOver ? 1 : 0);
anim::start(this);
}
}
if (!_photoLink && (!_peerChat || _peerChat->forbidden)) {
if (!_photoLink && (!_peerChat || _peerChat->isForbidden)) {
setCursor((_kickOver || _kickDown || textlnkOver()) ? style::cur_pointer : style::cur_default);
} else {
setCursor((_kickOver || _kickDown || _photoOver || textlnkOver()) ? style::cur_pointer : style::cur_default);
@ -791,7 +870,14 @@ void ProfileInner::updateSelected() {
update(QRect(_left, _aboutTop, _width, _aboutHeight));
}
int32 partfrom = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip;
int32 partfrom = _searchInPeer.y() + _searchInPeer.height();
if (_peerUser || _peerChat) {
partfrom = _clearHistory.y() + _clearHistory.height();
}
if (_peerUser || _peerChat || !_amCreator) {
partfrom = _deleteConversation.y() + _deleteConversation.height();
}
partfrom += st::profileHeaderSkip;
int32 newSelected = (lp.x() >= _left - st::profileListPadding.width() && lp.x() < _left + _width + st::profileListPadding.width() && lp.y() >= partfrom) ? (lp.y() - partfrom) / _pHeight : -1;
UserData *newKickOver = 0;
@ -828,7 +914,7 @@ void ProfileInner::mousePressEvent(QMouseEvent *e) {
} else if (QRect(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize).contains(e->pos())) {
if (_photoLink) {
_photoLink->onClick(e->button());
} else if (_peerChat && !_peerChat->forbidden) {
} else if (_peerChat && !_peerChat->isForbidden) {
onUpdatePhoto();
}
}
@ -849,10 +935,14 @@ void ProfileInner::mouseReleaseEvent(QMouseEvent *e) {
TextLinkPtr lnk = textlnkDown();
textlnkDown(TextLinkPtr());
if (lnk == textlnkOver()) {
if (reBotCommand().match(lnk->encoded()).hasMatch()) {
App::main()->showPeerHistory(_peer->id, ShowAtTheEndMsgId);
if (reHashtag().match(lnk->encoded()).hasMatch() && _peerChannel) {
App::searchByHashtag(lnk->encoded(), _peerChannel);
} else {
if (reBotCommand().match(lnk->encoded()).hasMatch()) {
App::main()->showPeerHistory(_peer->id, ShowAtTheEndMsgId);
}
lnk->onClick(e->button());
}
lnk->onClick(e->button());
}
}
_kickDown = 0;
@ -981,7 +1071,7 @@ void ProfileInner::resizeEvent(QResizeEvent *e) {
// profile
top += st::profilePadding.top();
if (_isAdmin) {
if (_amCreator) {
if ((_peerChat && _peerChat->invitationUrl.isEmpty()) || (_peerChannel && _peerChannel->invitationUrl.isEmpty())) {
_createInvitationLink.move(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profilePhoneTop);
} else {
@ -1008,9 +1098,23 @@ void ProfileInner::resizeEvent(QResizeEvent *e) {
_shareContact.setGeometry(_left + _width - btnWidth, top, btnWidth, _shareContact.height());
_inviteToGroup.setGeometry(_left + _width - btnWidth, top, btnWidth, _inviteToGroup.height());
if (!_peerChannel || _isAdmin) {
if (!_peerChannel || _amCreator) {
top += _shareContact.height();
}
if (_peerChannel && (_amCreator || _peerChannel->isPublic())) {
if (!_amCreator) {
top += st::setLittleSkip;
} else {
top += st::setSectionSkip;
}
if (_peerChannel->isPublic()) {
_username.move(_left, top);
_editLink.move(_left + _width - _editLink.width(), top);
} else {
_editLink.move(_left, top);
}
top += _editLink.height();
}
// about
if (!_about.isEmpty()) {
@ -1046,11 +1150,18 @@ void ProfileInner::resizeEvent(QResizeEvent *e) {
// actions
top += st::profileHeaderSkip;
_searchInPeer.move(_left, top); top += _searchInPeer.height() + st::setLittleSkip;
_clearHistory.move(_left, top); top += _clearHistory.height() + st::setLittleSkip;
_deleteConversation.move(_left, top); top += _deleteConversation.height();
if (_peerUser || _peerChat) {
_clearHistory.move(_left, top); top += _clearHistory.height() + st::setLittleSkip;
}
if (_peerUser || _peerChat || !_amCreator) {
_deleteConversation.move(_left, top); top += _deleteConversation.height();
}
if (_peerUser && peerToUser(_peerUser->id) != MTP::authedId()) {
top += st::setSectionSkip;
_blockUser.move(_left, top); top += _blockUser.height();
} else if (_peerChannel && _amCreator) {
top += st::setSectionSkip;
_deleteChannel.move(_left, top); top += _deleteChannel.height();
}
// participants
@ -1140,6 +1251,7 @@ int32 ProfileInner::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type)
int32 result = 0;
if (peer == _peer) {
if (updateMediaLinks(&result)) {
showAll();
resizeEvent(0);
update();
}
@ -1154,6 +1266,31 @@ void ProfileInner::requestHeight(int32 newHeight) {
}
}
int32 ProfileInner::countMinHeight() {
int32 h = 0;
if (_peerUser) {
if (peerToUser(_peerUser->id) == MTP::authedId()) {
h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip;
} else {
h = _blockUser.y() + _blockUser.height() + st::profileHeaderSkip;
}
} else if (_peerChat) {
h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip;
if (!_participants.isEmpty()) {
h += st::profileHeaderSkip + _participants.size() * _pHeight;
} else if (_peerChat->count > 0) {
h += st::profileHeaderSkip;
}
} else if (_peerChannel) {
if (_amCreator) {
h = _deleteChannel.y() + _deleteChannel.height() + st::profileHeaderSkip;
} else {
h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip;
}
}
return h;
}
void ProfileInner::allowDecreaseHeight(int32 decreaseBy) {
if (decreaseBy > 0 && _addToHeight > 0) {
_addToHeight -= qMin(decreaseBy, _addToHeight);
@ -1163,8 +1300,16 @@ void ProfileInner::allowDecreaseHeight(int32 decreaseBy) {
void ProfileInner::showAll() {
_searchInPeer.show();
_clearHistory.show();
_deleteConversation.show();
if (_peerUser || _peerChat) {
_clearHistory.show();
} else {
_clearHistory.hide();
}
if (_peerUser || _peerChat || !_amCreator) {
_deleteConversation.show();
} else {
_deleteConversation.hide();
}
if (_peerUser) {
_uploadPhoto.hide();
_cancelPhoto.hide();
@ -1189,11 +1334,14 @@ void ProfileInner::showAll() {
} else {
_blockUser.hide();
}
_deleteChannel.hide();
_editLink.hide();
_username.hide();
} else if (_peerChat) {
_sendMessage.hide();
_shareContact.hide();
_inviteToGroup.hide();
if (_peerChat->forbidden) {
if (_peerChat->isForbidden) {
_uploadPhoto.hide();
_cancelPhoto.hide();
_addParticipant.hide();
@ -1207,9 +1355,9 @@ void ProfileInner::showAll() {
_uploadPhoto.show();
_cancelPhoto.hide();
}
if (_isAdmin) {
if (_amCreator) {
_createInvitationLink.show();
if (_peerChat && _peerChat->invitationUrl.isEmpty()) {
if (_peerChat->invitationUrl.isEmpty()) {
_invitationLink.hide();
} else {
_invitationLink.show();
@ -1225,11 +1373,14 @@ void ProfileInner::showAll() {
}
}
_blockUser.hide();
_deleteChannel.hide();
_editLink.hide();
_username.hide();
} else if (_peerChannel) {
_sendMessage.hide();
_shareContact.hide();
_inviteToGroup.hide();
if (_peerChannel->forbidden) {
if (_peerChannel->isForbidden) {
_uploadPhoto.hide();
_cancelPhoto.hide();
_addParticipant.hide();
@ -1240,14 +1391,14 @@ void ProfileInner::showAll() {
_uploadPhoto.hide();
_cancelPhoto.show();
} else {
if (_isAdmin) {
if (_amCreator) {
_uploadPhoto.show();
} else {
_uploadPhoto.hide();
}
_cancelPhoto.hide();
}
if (_isAdmin) {
if (_amCreator && !_peerChannel->isPublic()) {
_createInvitationLink.show();
if (_peerChannel->invitationUrl.isEmpty()) {
_invitationLink.hide();
@ -1261,30 +1412,25 @@ void ProfileInner::showAll() {
_addParticipant.hide();
}
_blockUser.hide();
if (_amCreator) {
_deleteChannel.show();
_editLink.show();
} else {
_deleteChannel.hide();
_editLink.hide();
}
if (_peerChannel->isPublic()) {
_username.show();
} else {
_username.hide();
}
}
_enableNotifications.show();
updateNotifySettings();
// participants
reorderParticipants();
int32 h;
if (_peerUser) {
if (peerToUser(_peerUser->id) == MTP::authedId()) {
h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip;
} else {
h = _blockUser.y() + _blockUser.height() + st::profileHeaderSkip;
}
} else if (_peerChat) {
h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip;
if (!_participants.isEmpty()) {
h += st::profileHeaderSkip + _participants.size() * _pHeight;
} else if (_peerChat->count > 0) {
h += st::profileHeaderSkip;
}
} else if (_peerChannel) {
h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip;
}
resize(width(), h + _addToHeight);
resize(width(), countMinHeight() + _addToHeight);
}
void ProfileInner::updateInvitationLink() {
@ -1398,7 +1544,7 @@ void ProfileWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth)
p.drawPixmap(QPoint(st::topBarBackPadding.left(), (st::topBarHeight - st::topBarBackImg.pxHeight()) / 2), App::sprite(), st::topBarBackImg);
p.setFont(st::topBarBackFont->f);
p.setPen(st::topBarBackColor->p);
p.drawText(st::topBarBackPadding.left() + st::topBarBackImg.pxWidth() + st::topBarBackPadding.right(), (st::topBarHeight - st::topBarBackFont->height) / 2 + st::topBarBackFont->ascent, lang(peer()->isUser() ? lng_profile_info : lng_profile_group_info));
p.drawText(st::topBarBackPadding.left() + st::topBarBackImg.pxWidth() + st::topBarBackPadding.right(), (st::topBarHeight - st::topBarBackFont->height) / 2 + st::topBarBackFont->ascent, lang(peer()->isUser() ? lng_profile_info : (peer()->isChat() ? lng_profile_group_info : lng_profile_channel_info)));
}
}
@ -1477,6 +1623,10 @@ void ProfileWidget::updateOnlineDisplayTimer() {
_inner.updateOnlineDisplayTimer();
}
void ProfileWidget::peerUsernameChanged() {
_inner.peerUsernameChanged();
}
void ProfileWidget::updateNotifySettings() {
_inner.updateNotifySettings();
}

View File

@ -27,6 +27,8 @@ public:
void start();
void peerUsernameChanged();
bool event(QEvent *e);
void paintEvent(QPaintEvent *e);
void mouseMoveEvent(QMouseEvent *e);
@ -56,6 +58,7 @@ public:
int32 mediaOverviewUpdated(PeerData *peer, MediaOverviewType type); // returns scroll shift
void requestHeight(int32 newHeight);
int32 countMinHeight();
void allowDecreaseHeight(int32 decreaseBy);
~ProfileInner();
@ -78,6 +81,8 @@ public slots:
void onClearHistorySure();
void onDeleteConversation();
void onDeleteConversationSure();
void onDeleteChannel();
void onDeleteChannelSure();
void onBlockUser();
void onAddParticipant();
@ -103,11 +108,13 @@ public slots:
void onInvitationLink();
void onCreateInvitationLink();
void onCreateInvitationLinkSure();
void onPublicLink();
void onFullPeerUpdated(PeerData *peer);
void onBotSettings();
void onBotHelp();
void onEditPublicLink();
private:
@ -126,7 +133,7 @@ private:
ChatData *_peerChat;
ChannelData *_peerChannel;
History *_hist;
bool _isAdmin;
bool _amCreator;
int32 _width, _left, _addToHeight;
@ -139,7 +146,7 @@ private:
FlatButton _sendMessage, _shareContact, _inviteToGroup;
LinkButton _cancelPhoto, _createInvitationLink, _invitationLink;
QString _invitationText;
LinkButton _botSettings, _botHelp;
LinkButton _botSettings, _botHelp, _username, _editLink;
Text _about;
int32 _aboutTop, _aboutHeight;
@ -161,7 +168,7 @@ private:
LinkButton _searchInPeer, _clearHistory, _deleteConversation;
UserBlockedStatus _wasBlocked;
mtpRequestId _blockRequest;
LinkButton _blockUser;
LinkButton _blockUser, _deleteChannel;
// participants
int32 _pHeight;
@ -215,6 +222,8 @@ public:
void updateOnlineDisplay();
void updateOnlineDisplayTimer();
void peerUsernameChanged();
void updateNotifySettings();
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);

View File

@ -347,6 +347,7 @@ void PsMainWindow::psFirstShow() {
psAddContact = window->addAction(lang(lng_mac_menu_add_contact), App::wnd(), SLOT(onShowAddContact()));
window->addSeparator();
psNewGroup = window->addAction(lang(lng_mac_menu_new_group), App::wnd(), SLOT(onShowNewGroup()));
psNewChannel = window->addAction(lang(lng_mac_menu_new_channel), App::wnd(), SLOT(onShowNewChannel()));
window->addSeparator();
psShowTelegram = window->addAction(lang(lng_mac_menu_show), App::wnd(), SLOT(showFromTray()));

View File

@ -26,6 +26,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "window.h"
#include "gui/filedialog.h"
#include "boxes/confirmbox.h"
#include "audio.h"
#include "localstorage.h"
@ -372,12 +374,28 @@ void ChannelData::setName(const QString &newName, const QString &usern) {
updateName(newName.isEmpty() ? name : newName, QString(), usern);
if (updUsername) {
if (usern.isEmpty()) {
flags &= ~MTPDchannel::flag_username;
} else {
flags |= MTPDchannel::flag_username;
}
if (App::main()) {
App::main()->peerUsernameChanged(this);
}
}
}
void ChannelData::updateFull() {
if (!_lastFullUpdate || getms(true) > _lastFullUpdate + UpdateFullChannelTimeout) {
App::api()->requestFullPeer(this);
if (!amCreator() && !inviter) App::api()->requestSelfParticipant(this);
}
}
void ChannelData::fullUpdated() {
_lastFullUpdate = getms(true);
}
uint64 PtsWaiter::ptsKey(PtsSkippedQueue queue) {
return _queue.insert(uint64(uint32(_last)) << 32 | uint64(uint32(_count)), queue).key();
}
@ -423,7 +441,7 @@ void PtsWaiter::applySkippedUpdates(ChannelData *channel) {
for (QMap<uint64, PtsSkippedQueue>::const_iterator i = _queue.cbegin(), e = _queue.cend(); i != e; ++i) {
switch (i.value()) {
case SkippedUpdate: App::main()->feedUpdate(_updateQueue.value(i.key())); break;
case SkippedUpdates: App::main()->handleUpdates(_updatesQueue.value(i.key())); break;
case SkippedUpdates: App::main()->feedUpdates(_updatesQueue.value(i.key())); break;
}
}
--_applySkippedLevel;
@ -437,13 +455,45 @@ void PtsWaiter::clearSkippedUpdates() {
_applySkippedLevel = 0;
}
bool PtsWaiter::updated(ChannelData *channel, int32 pts, int32 count) { // return false if need to save that update and apply later
if (_requesting || _applySkippedLevel) return true;
bool PtsWaiter::updated(ChannelData *channel, int32 pts, int32 count) {
if (_requesting || _applySkippedLevel) {
return true;
} else if (pts <= _good) {
return false;
}
return check(channel, pts, count);
}
bool PtsWaiter::updated(ChannelData *channel, int32 pts, int32 ptsCount, const MTPUpdates &updates) {
if (_requesting || _applySkippedLevel) {
return true;
} else if (pts <= _good) {
return false;
} else if (check(channel, pts, ptsCount)) {
return true;
}
_updatesQueue.insert(ptsKey(SkippedUpdates), updates);
return false;
}
bool PtsWaiter::updated(ChannelData *channel, int32 pts, int32 ptsCount, const MTPUpdate &update) {
if (_requesting || _applySkippedLevel) {
return true;
} else if (pts <= _good) {
return false;
} else if (check(channel, pts, ptsCount)) {
return true;
}
_updateQueue.insert(ptsKey(SkippedUpdate), update);
return false;
}
bool PtsWaiter::check(ChannelData *channel, int32 pts, int32 count) { // return false if need to save that update and apply later
if (!inited()) {
init(pts);
return true;
}
_last = qMax(_last, pts);
_count += count;
if (_last == _count) {
@ -457,22 +507,6 @@ bool PtsWaiter::updated(ChannelData *channel, int32 pts, int32 count) { // retur
return !count;
}
bool PtsWaiter::updated(ChannelData *channel, int32 pts, int32 ptsCount, const MTPUpdates &updates) {
if (!updated(channel, pts, ptsCount)) {
_updatesQueue.insert(ptsKey(SkippedUpdates), updates);
return false;
}
return true;
}
bool PtsWaiter::updated(ChannelData *channel, int32 pts, int32 ptsCount, const MTPUpdate &update) {
if (!updated(channel, pts, ptsCount)) {
_updateQueue.insert(ptsKey(SkippedUpdate), update);
return false;
}
return true;
}
void PhotoLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton) {
App::wnd()->showPhoto(this, App::hoveredLinkItem());
@ -915,7 +949,11 @@ id(id), type(type), url(url), displayUrl(displayUrl), siteName(siteName), title(
void PeerLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton && App::main()) {
if (peer() && peer()->isChannel() && App::main()->historyPeer() != peer()) {
App::main()->showPeerHistory(peer()->id, ShowAtUnreadMsgId);
if (!peer()->asChannel()->isPublic() && (peer()->asChannel()->isForbidden || peer()->asChannel()->haveLeft() || peer()->asChannel()->wasKicked())) {
App::wnd()->showLayer(new ConfirmBox(lang(lng_channel_not_accessible), true));
} else {
App::main()->showPeerHistory(peer()->id, ShowAtUnreadMsgId);
}
} else {
App::main()->showPeerProfile(peer());
}

View File

@ -107,6 +107,14 @@ inline int32 flagsFromMessage(const MTPmessage &msg) {
}
return 0;
}
inline int32 idFromMessage(const MTPmessage &msg) {
switch (msg.type()) {
case mtpc_messageEmpty: return msg.c_messageEmpty().vid.v;
case mtpc_message: return msg.c_message().vid.v;
case mtpc_messageService: return msg.c_messageService().vid.v;
}
return 0;
}
typedef uint64 PhotoId;
typedef uint64 VideoId;
@ -351,7 +359,7 @@ public:
class ChatData : public PeerData {
public:
ChatData(const PeerId &id) : PeerData(id), inputChat(MTP_int(bareId())), count(0), date(0), version(0), admin(0), inviterForSpamReport(0), left(false), forbidden(true), botStatus(0) {
ChatData(const PeerId &id) : PeerData(id), inputChat(MTP_int(bareId())), count(0), date(0), version(0), creator(0), inviterForSpamReport(0), isForbidden(false), haveLeft(true), botStatus(0) {
}
void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = UnknownPeerPhotoId);
@ -360,10 +368,11 @@ public:
int32 count;
int32 date;
int32 version;
int32 admin;
int32 creator;
int32 inviterForSpamReport; // > 0 - user who invited me to chat in unread service msg, < 0 - have outgoing message
bool left;
bool forbidden;
bool isForbidden;
bool haveLeft;
typedef QMap<UserData*, int32> Participants;
Participants participants;
typedef QMap<UserData*, bool> CanKick;
@ -427,6 +436,7 @@ public:
void clearSkippedUpdates();
private:
bool check(ChannelData *channel, int32 pts, int32 count); // return false if need to save that update and apply later
uint64 ptsKey(PtsSkippedQueue queue);
void checkForWaiting(ChannelData *channel);
QMap<uint64, PtsSkippedQueue> _queue;
@ -440,28 +450,61 @@ private:
class ChannelData : public PeerData {
public:
ChannelData(const PeerId &id) : PeerData(id), access(0), inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))), date(0), version(0), isBroadcast(false), isPublic(false), adminned(false), left(false), forbidden(true), botStatus(-1) {
ChannelData(const PeerId &id) : PeerData(id), access(0), inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))), count(0), date(0), version(0), isForbidden(true), botStatus(-1), inviter(0), _lastFullUpdate(0) {
setName(QString(), QString());
}
void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = UnknownPeerPhotoId);
void setName(const QString &name, const QString &username);
void updateFull();
void fullUpdated();
uint64 access;
MTPinputChannel inputChannel;
QString username;
QString username, about;
int32 count;
int32 date;
int32 version;
bool isBroadcast, isPublic;
bool adminned;
bool left;
bool forbidden;
int32 flags;
bool isBroadcast() const {
return flags & MTPDchannel_flag_is_broadcast;
}
bool isPublic() const {
return flags & MTPDchannel::flag_username;
}
bool amCreator() const {
return flags & MTPDchannel_flag_am_creator;
}
bool amEditor() const {
return flags & MTPDchannel_flag_am_editor;
}
bool amModerator() const {
return flags & MTPDchannel_flag_am_moderator;
}
bool haveLeft() const {
return flags & MTPDchannel_flag_have_left;
}
bool wasKicked() const {
return flags & MTPDchannel_flag_was_kicked;
}
bool canPublish() const {
return amCreator() || amEditor();
}
bool amParticipant() const {
return canPublish() || (!haveLeft() && !wasKicked());
}
bool isForbidden;
int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other
// ImagePtr photoFull;
QString invitationUrl;
int32 inviter; // > 0 - user who invited me to channel, < 0 - not in channel
QDateTime inviteDate;
void ptsInit(int32 pts) {
_ptsWaiter.init(pts);
}
@ -498,6 +541,7 @@ public:
private:
PtsWaiter _ptsWaiter;
uint64 _lastFullUpdate;
};
inline UserData *PeerData::asUser() {

View File

@ -30,6 +30,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "layerwidget.h"
#include "settingswidget.h"
#include "boxes/confirmbox.h"
#include "boxes/contactsbox.h"
#include "mediaview.h"
#include "localstorage.h"
@ -1030,7 +1031,13 @@ void Window::onShowAddContact() {
void Window::onShowNewGroup() {
if (isHidden()) showFromTray();
if (main) main->showNewGroup();
if (main) replaceLayer(new GroupInfoBox(CreatingGroupGroup, false));
}
void Window::onShowNewChannel() {
if (isHidden()) showFromTray();
if (main) replaceLayer(new GroupInfoBox(CreatingGroupChannel, false));
}
void Window::onLogout() {

View File

@ -264,6 +264,7 @@ public slots:
void onShowAddContact();
void onShowNewGroup();
void onShowNewChannel();
void onLogout();
void onLogoutSure();
void updateGlobalMenu(); // for OS X top menu