From 98e270076485c2efb8396701f9b476298c3de5cb Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 10 Mar 2016 18:42:01 +0300 Subject: [PATCH] rich delete all + ban almost done in supergroups (need to clear messages on the client side as well) --- Telegram/Resources/lang.strings | 2 + Telegram/SourceFiles/apiwrap.cpp | 6 +- Telegram/SourceFiles/app.cpp | 8 +- Telegram/SourceFiles/boxes/confirmbox.cpp | 172 +++++++++++++++++++++ Telegram/SourceFiles/boxes/confirmbox.h | 44 +++++- Telegram/SourceFiles/boxes/contactsbox.cpp | 12 +- Telegram/SourceFiles/history.cpp | 17 +- Telegram/SourceFiles/historywidget.cpp | 3 + Telegram/SourceFiles/mainwidget.cpp | 9 ++ Telegram/SourceFiles/profilewidget.cpp | 16 ++ Telegram/SourceFiles/structs.h | 12 +- 11 files changed, 279 insertions(+), 22 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 07d99da2dd..268ce1880e 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -668,6 +668,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_in_dlg_sticker" = "Sticker"; "lng_in_dlg_sticker_emoji" = "{emoji} (sticker)"; +"lng_ban_user" = "Ban User"; +"lng_delete_all_from" = "Delete all from this user"; "lng_report_spam" = "Report Spam"; "lng_report_spam_hide" = "Hide"; "lng_report_spam_thanks" = "Thank you for your report!"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 3f1ded1739..3eecaf9eed 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -512,7 +512,7 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP UserData *u = App::user(userId); if (bots) { if (u->botInfo) { - peer->mgInfo->bots.insert(u, true); + peer->mgInfo->bots.insert(u); botStatus = 2;// (botStatus > 0/* || !i.key()->botInfo->readsAllHistory*/) ? 2 : 1; if (!u->botInfo->inited) { needBotsInfos = true; @@ -524,9 +524,9 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP } else { if (peer->mgInfo->lastParticipants.indexOf(u) < 0) { peer->mgInfo->lastParticipants.push_back(u); - if (admin) peer->mgInfo->lastAdmins.insert(u, true); + if (admin) peer->mgInfo->lastAdmins.insert(u); if (u->botInfo) { - peer->mgInfo->bots.insert(u, true); + peer->mgInfo->bots.insert(u); if (peer->mgInfo->botStatus != 0 && peer->mgInfo->botStatus < 2) { peer->mgInfo->botStatus = 2; } diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 5543956a54..9426a3e822 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -664,10 +664,10 @@ namespace App { if (user) { chat->participants[user] = pversion; if (inviter == MTP::authedId()) { - chat->invitedByMe[user] = true; + chat->invitedByMe.insert(user); } if (i->type() == mtpc_chatParticipantAdmin) { - chat->admins[user] = true; + chat->admins.insert(user); if (user->isSelf()) { chat->flags |= MTPDchat::flag_admin; } @@ -736,7 +736,7 @@ namespace App { } else if (chat->participants.find(user) == chat->participants.end()) { chat->participants[user] = (chat->participants.isEmpty() ? 1 : chat->participants.begin().value()); if (d.vinviter_id.v == MTP::authedId()) { - chat->invitedByMe[user] = true; + chat->invitedByMe.insert(user); } else { chat->invitedByMe.remove(user); } @@ -876,7 +876,7 @@ namespace App { if (chat->noParticipantInfo()) { App::api()->requestFullPeer(chat); } else { - chat->admins.insert(user, true); + chat->admins.insert(user); } } else { if (user->isSelf()) { diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index fc0eb42370..79cb4efca8 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -434,3 +434,175 @@ bool PinMessageBox::pinFail(const RPCError &error) { Ui::hideLayer(); return true; } + +RichDeleteMessageBox::RichDeleteMessageBox(ChannelData *channel, UserData *from, MsgId msgId) : AbstractBox(st::boxWidth) +, _channel(channel) +, _from(from) +, _msgId(msgId) +, _text(this, lang(lng_selected_delete_sure_this), st::boxLabel) +, _banUser(this, lang(lng_ban_user), false) +, _reportSpam(this, lang(lng_report_spam), false) +, _deleteAll(this, lang(lng_delete_all_from), false) +, _delete(this, lang(lng_box_delete), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _deleteRequestId(0) +, _banRequestId(0) +, _reportRequestId(0) +, _deleteAllRequestId(0) { + _text.resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()); + setMaxHeight(st::boxPadding.top() + _text.height() + st::boxMediumSkip + _banUser.height() + st::boxLittleSkip + _reportSpam.height() + st::boxLittleSkip + _deleteAll.height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _delete.height() + st::boxButtonPadding.bottom()); + + connect(&_delete, SIGNAL(clicked()), this, SLOT(onDelete())); + connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); +} + +void RichDeleteMessageBox::resizeEvent(QResizeEvent *e) { + _text.moveToLeft(st::boxPadding.left(), st::boxPadding.top()); + _banUser.moveToLeft(st::boxPadding.left(), _text.y() + _text.height() + st::boxMediumSkip); + _reportSpam.moveToLeft(st::boxPadding.left(), _banUser.y() + _banUser.height() + st::boxLittleSkip); + _deleteAll.moveToLeft(st::boxPadding.left(), _reportSpam.y() + _reportSpam.height() + st::boxLittleSkip); + _delete.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _delete.height()); + _cancel.moveToRight(st::boxButtonPadding.right() + _delete.width() + st::boxButtonPadding.left(), _delete.y()); +} + +void RichDeleteMessageBox::onDelete() { + if (_deleteRequestId || _banRequestId || _reportRequestId || _deleteAllRequestId) return; + + HistoryItem *item = App::histItemById(_channel ? peerToChannel(_channel->id) : 0, _msgId); + if (!item || item->type() != HistoryItemMsg) { + Ui::hideLayer(); + return; + } + + QVector toDelete(1, MTP_int(item->id)); + History *h = item->history(); + bool wasOnServer = (item->id > 0), wasLast = (h->lastMsg == item); + bool banUser = _banUser.checked(), reportSpam = _reportSpam.checked(), deleteAll = _deleteAll.checked(); + + item->destroy(); + if (!wasOnServer && wasLast && !h->lastMsg) { + App::main()->checkPeerHistory(h->peer); + } + + Notify::historyItemsResized(); + if (wasOnServer) { + _deleteRequestId = MTP::send(MTPchannels_DeleteMessages(_channel->inputChannel, MTP_vector(1, MTP_int(item->id))), rpcDone(&RichDeleteMessageBox::deleteDone), rpcFail(&RichDeleteMessageBox::deleteFail)); + } + if (banUser) { + _banRequestId = MTP::send(MTPchannels_KickFromChannel(_channel->inputChannel, _from->inputUser, MTP_boolTrue()), rpcDone(&RichDeleteMessageBox::banDone), rpcFail(&RichDeleteMessageBox::deleteFail)); + } + if (reportSpam) { + _reportRequestId = MTP::send(MTPchannels_ReportSpam(_channel->inputChannel, _from->inputUser, MTP_vector(1, MTP_int(item->id))), rpcDone(&RichDeleteMessageBox::reportDone), rpcFail(&RichDeleteMessageBox::deleteFail)); + } + if (deleteAll) { + _deleteAllRequestId = MTP::send(MTPchannels_DeleteUserHistory(_channel->inputChannel, _from->inputUser), rpcDone(&RichDeleteMessageBox::deleteAllPart), rpcFail(&RichDeleteMessageBox::deleteFail)); + } +} + +void RichDeleteMessageBox::showAll() { + _text.show(); + _banUser.show(); + _reportSpam.show(); + _deleteAll.show(); + _delete.show(); + _cancel.show(); +} + +void RichDeleteMessageBox::hideAll() { + _text.hide(); + _banUser.hide(); + _reportSpam.hide(); + _deleteAll.hide(); + _delete.hide(); + _cancel.hide(); +} + +void RichDeleteMessageBox::deleteDone(const MTPmessages_AffectedMessages &result, mtpRequestId req) { + const MTPDmessages_affectedMessages &d(result.c_messages_affectedMessages()); + if (_channel->ptsUpdated(d.vpts.v, d.vpts_count.v)) { + _channel->ptsApplySkippedUpdates(); + App::emitPeerUpdated(); + } + if (History *h = App::historyLoaded(_channel->id)) { + if (!h->lastMsg && App::main()) { + App::main()->checkPeerHistory(_channel); + } + } + if (req == _deleteRequestId) { + _deleteRequestId = 0; + } else if (req == _banRequestId) { + _banRequestId = 0; + } else if (req == _reportRequestId) { + _reportRequestId = 0; + } else if (req == _deleteAllRequestId) { + _deleteAllRequestId = 0; + } + checkFinished(); +} + +void RichDeleteMessageBox::banDone(const MTPUpdates &result, mtpRequestId req) { + if (App::main()) { + App::main()->sentUpdatesReceived(result); + } + if (req == _banRequestId) { + _banRequestId = 0; + } + checkFinished(); +} + +void RichDeleteMessageBox::reportDone(const MTPBool &result, mtpRequestId req) { + if (req == _reportRequestId) { + _reportRequestId = 0; + } + checkFinished(); +} + +void RichDeleteMessageBox::deleteAllPart(const MTPmessages_AffectedHistory &result, mtpRequestId req) { + const MTPDmessages_affectedHistory &d(result.c_messages_affectedHistory()); + if (_channel->ptsUpdated(d.vpts.v, d.vpts_count.v)) { + _channel->ptsApplySkippedUpdates(); + App::emitPeerUpdated(); + } + if (req == _deleteRequestId) { + _deleteRequestId = 0; + } else if (req == _banRequestId) { + _banRequestId = 0; + } else if (req == _reportRequestId) { + _reportRequestId = 0; + } else if (req == _deleteAllRequestId) { + _deleteAllRequestId = 0; + } + + int32 offset = d.voffset.v; + if (offset <= 0) { + if (History *h = App::historyLoaded(_channel->id)) { + if (!h->lastMsg && App::main()) { + App::main()->checkPeerHistory(_channel); + } + } + checkFinished(); + return; + } + + _deleteAllRequestId = MTP::send(MTPchannels_DeleteUserHistory(_channel->inputChannel, _from->inputUser), rpcDone(&RichDeleteMessageBox::deleteAllPart), rpcFail(&RichDeleteMessageBox::deleteFail)); +} + +bool RichDeleteMessageBox::deleteFail(const RPCError &error, mtpRequestId req) { + if (mtpIsFlood(error)) return false; + if (req == _deleteRequestId) { + _deleteRequestId = 0; + } else if (req == _banRequestId) { + _banRequestId = 0; + } else if (req == _reportRequestId) { + _reportRequestId = 0; + } else if (req == _deleteAllRequestId) { + _deleteAllRequestId = 0; + } + checkFinished(); + return true; +} + +void RichDeleteMessageBox::checkFinished() { + if (_deleteRequestId || _banRequestId || _reportRequestId || _deleteAllRequestId) return; + Ui::hideLayer(); +} \ No newline at end of file diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index b2b1d2e989..6937b2c107 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -198,4 +198,46 @@ private: mtpRequestId _requestId; -}; \ No newline at end of file +}; + +class RichDeleteMessageBox : public AbstractBox, public RPCSender { + Q_OBJECT + +public: + + RichDeleteMessageBox(ChannelData *channel, UserData *from, MsgId msgId); + + void resizeEvent(QResizeEvent *e); + +public slots: + + void onDelete(); + +protected: + + void showAll(); + void hideAll(); + +private: + + void deleteDone(const MTPmessages_AffectedMessages &result, mtpRequestId req); + void banDone(const MTPUpdates &result, mtpRequestId req); + void reportDone(const MTPBool &result, mtpRequestId req); + void deleteAllPart(const MTPmessages_AffectedHistory &result, mtpRequestId req); + + bool deleteFail(const RPCError &error, mtpRequestId req); + + void checkFinished(); + + ChannelData *_channel; + UserData *_from; + MsgId _msgId; + + FlatLabel _text; + Checkbox _banUser, _reportSpam, _deleteAll; + + BoxButton _delete, _cancel; + + mtpRequestId _deleteRequestId, _banRequestId, _reportRequestId, _deleteAllRequestId; + +}; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 80eca99357..ebaf4018e8 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -1669,7 +1669,7 @@ void ContactsBox::setAdminDone(UserData *user, const MTPBool &result) { if (_inner.chat()->noParticipantInfo()) { App::api()->requestFullPeer(_inner.chat()); } else { - _inner.chat()->admins.insert(user, true); + _inner.chat()->admins.insert(user); } } --_saveRequestId; @@ -2182,6 +2182,16 @@ void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result _datas.push_back(0); } } + + // update admins if we got all of them + if (_filter == MembersFilterAdmins && _channel->isMegagroup() && _rows.size() < Global::ChatSizeMax()) { + _channel->mgInfo->lastAdmins.clear(); + for (int32 i = 0, l = _rows.size(); i != l; ++i) { + if (_roles.at(i) == MemberRoleCreator || _roles.at(i) == MemberRoleEditor) { + _channel->mgInfo->lastAdmins.insert(_rows.at(i)); + } + } + } } if (_rows.isEmpty()) { _rows.push_back(App::self()); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 25442fe91b..46a42e4387 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -1409,7 +1409,7 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated; } if (user->botInfo) { - peer->asChannel()->mgInfo->bots.insert(user, true); + peer->asChannel()->mgInfo->bots.insert(user); if (peer->asChannel()->mgInfo->botStatus != 0 && peer->asChannel()->mgInfo->botStatus < 2) { peer->asChannel()->mgInfo->botStatus = 2; } @@ -1427,7 +1427,7 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo peer->asChannel()->mgInfo->lastParticipants.push_front(result->from()->asUser()); } if (result->from()->asUser()->botInfo) { - peer->asChannel()->mgInfo->bots.insert(result->from()->asUser(), true); + peer->asChannel()->mgInfo->bots.insert(result->from()->asUser()); if (peer->asChannel()->mgInfo->botStatus != 0 && peer->asChannel()->mgInfo->botStatus < 2) { peer->asChannel()->mgInfo->botStatus = 2; } @@ -1742,7 +1742,7 @@ HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *a } else if (peer->isMegagroup()) { lastAuthors = &peer->asChannel()->mgInfo->lastParticipants; if (adding->from()->asUser()->botInfo) { - peer->asChannel()->mgInfo->bots.insert(adding->from()->asUser(), true); + peer->asChannel()->mgInfo->bots.insert(adding->from()->asUser()); if (peer->asChannel()->mgInfo->botStatus != 0 && peer->asChannel()->mgInfo->botStatus < 2) { peer->asChannel()->mgInfo->botStatus = 2; } @@ -1763,14 +1763,14 @@ HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *a if (adding->hasReplyMarkup()) { int32 markupFlags = App::replyMarkup(channelId(), adding->id).flags; if (!(markupFlags & MTPDreplyKeyboardMarkup::flag_selective) || adding->mentionsMe()) { - QMap *markupSenders = 0; + OrderedSet *markupSenders = 0; if (peer->isChat()) { markupSenders = &peer->asChat()->markupSenders; } else if (peer->isMegagroup()) { markupSenders = &peer->asChannel()->mgInfo->markupSenders; } if (markupSenders) { - markupSenders->insert(adding->from(), true); + markupSenders->insert(adding->from()); } if (markupFlags & MTPDreplyKeyboardMarkup_flag_ZERO) { // zero markup means replyKeyboardHide if (lastKeyboardFrom == adding->from()->id || (!lastKeyboardInited && !peer->isChat() && !peer->isMegagroup() && !adding->out())) { @@ -1977,7 +1977,7 @@ void History::addOlderSlice(const QVector &slice, const QVector *lastAuthors = 0; - QMap *markupSenders = 0; + OrderedSet *markupSenders = 0; if (peer->isChat()) { lastAuthors = &peer->asChat()->lastAuthors; markupSenders = &peer->asChat()->markupSenders; @@ -2007,7 +2007,7 @@ void History::addOlderSlice(const QVector &slice, const QVectormentionsMe()) { bool wasKeyboardHide = markupSenders->contains(item->author()); if (!wasKeyboardHide) { - markupSenders->insert(item->author(), true); + markupSenders->insert(item->author()); } if (!(markupFlags & MTPDreplyKeyboardMarkup_flag_ZERO)) { if (!lastKeyboardInited) { @@ -2931,6 +2931,9 @@ void HistoryItem::destroy() { detach(); if (history()->isChannel()) { history()->asChannelHistory()->messageDeleted(this); + if (history()->peer->isMegagroup() && history()->peer->asChannel()->mgInfo->pinnedMsgId == id) { + history()->peer->asChannel()->mgInfo->pinnedMsgId = 0; + } } if (history()->lastMsg == this) { history()->fixLastMessage(wasAtBottom); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 57b6a2493d..f41f80d01a 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -6258,6 +6258,9 @@ void HistoryWidget::itemRemoved(HistoryItem *item) { if (item == _replyReturn) { calcNextReplyReturn(); } + if (_pinnedBar && item->id == _pinnedBar->msgId) { + pinnedMsgVisibilityUpdated(); + } if (_kbReplyTo && item == _kbReplyTo) { onKbToggle(); _kbReplyTo = 0; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index e8854ebc3b..be48886d13 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -907,6 +907,15 @@ void MainWidget::forwardLayer(int32 forwardSelected) { } void MainWidget::deleteLayer(int32 selectedCount) { + if (selectedCount == -1 && !overview) { + if (HistoryItem *item = App::contextItem()) { + ChannelData *channel = item->history()->peer->asChannel(); + if (channel && !item->isPost() && !item->out() && item->from()->isUser() && (channel->amCreator() || channel->amEditor())) { + Ui::showLayer(new RichDeleteMessageBox(channel, item->from()->asUser(), item->id)); + return; + } + } + } QString str((selectedCount < 0) ? lang(selectedCount < -1 ? lng_selected_cancel_sure_this : lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, selectedCount)); QString btn(lang((selectedCount < -1) ? lng_selected_upload_stop : lng_box_delete)), cancel(lang((selectedCount < -1) ? lng_continue : lng_cancel)); ConfirmBox *box = new ConfirmBox(str, btn, st::defaultBoxButton, cancel); diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 2ce22da176..08e49d6ba9 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -992,6 +992,22 @@ void ProfileInner::paintEvent(QPaintEvent *e) { int32 partfrom = top; if (!_participants.isEmpty()) { + if (App::self()) { + if (_peerChat) { + if (_peerChat->amAdmin() && _peerChat->admins.constFind(App::self()) == _peerChat->admins.cend()) { + _peerChat->admins.insert(App::self()); + } else if (!_peerChat->amAdmin() && _peerChat->admins.constFind(App::self()) != _peerChat->admins.cend()) { + _peerChat->admins.remove(App::self()); + } + } else if (_peerChannel && _peerChannel->isMegagroup()) { + if ((_peerChannel->amCreator() || _peerChannel->amEditor()) && _peerChannel->mgInfo->lastAdmins.constFind(App::self()) == _peerChannel->mgInfo->lastAdmins.cend()) { + _peerChannel->mgInfo->lastAdmins.insert(App::self()); + } else if (!_peerChannel->amCreator() && !_peerChannel->amEditor() && _peerChannel->mgInfo->lastAdmins.constFind(App::self()) != _peerChannel->mgInfo->lastAdmins.cend()) { + _peerChannel->mgInfo->lastAdmins.remove(App::self()); + } + } + } + int32 cnt = 0, fullCnt = _participants.size(); for (Participants::const_iterator i = _participants.cbegin(), e = _participants.cend(); i != e; ++i, ++cnt) { int32 top = partfrom + cnt * _pHeight; diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 7d73b7c60e..f03d57fe60 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -457,13 +457,13 @@ public: } typedef QMap Participants; Participants participants; - typedef QMap InvitedByMe; + typedef OrderedSet InvitedByMe; InvitedByMe invitedByMe; - typedef QMap Admins; + typedef OrderedSet Admins; Admins admins; typedef QList LastAuthors; LastAuthors lastAuthors; - typedef QMap MarkupSenders; + typedef OrderedSet MarkupSenders; MarkupSenders markupSenders; int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other // ImagePtr photoFull; @@ -542,11 +542,11 @@ struct MegagroupInfo { } typedef QList LastParticipants; LastParticipants lastParticipants; - typedef QMap LastAdmins; + typedef OrderedSet LastAdmins; LastAdmins lastAdmins; - typedef QMap MarkupSenders; + typedef OrderedSet MarkupSenders; MarkupSenders markupSenders; - typedef QMap Bots; + typedef OrderedSet Bots; Bots bots; int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other