From eb1825defdfe724e8fc4fcf04bc36eb701497c55 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 21 Mar 2019 17:48:40 +0400 Subject: [PATCH] Add revoking of full history. --- Telegram/SourceFiles/apiwrap.cpp | 24 +++++ Telegram/SourceFiles/apiwrap.h | 4 + Telegram/SourceFiles/boxes/confirm_box.cpp | 100 ++++++++++++++++-- Telegram/SourceFiles/boxes/confirm_box.h | 4 +- Telegram/SourceFiles/data/data_peer.cpp | 6 ++ Telegram/SourceFiles/data/data_peer.h | 1 + Telegram/SourceFiles/mainwidget.cpp | 35 ------ Telegram/SourceFiles/mainwidget.h | 9 -- .../SourceFiles/window/window_peer_menu.cpp | 60 +---------- 9 files changed, 130 insertions(+), 113 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 38dfb5b107..19a7326152 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2446,6 +2446,30 @@ void ApiWrap::applyAffectedMessages( App::main()->ptsUpdateAndApply(data.vpts.v, data.vpts_count.v); } +void ApiWrap::deleteMessages( + not_null peer, + const QVector &ids, + bool revoke) { + const auto done = [=](const MTPmessages_AffectedMessages & result) { + applyAffectedMessages(peer, result); + if (const auto history = peer->owner().historyLoaded(peer)) { + history->requestChatListMessage(); + } + }; + if (const auto channel = peer->asChannel()) { + request(MTPchannels_DeleteMessages( + channel->inputChannel, + MTP_vector(ids) + )).done(done).send(); + } else { + using Flag = MTPmessages_DeleteMessages::Flag; + request(MTPmessages_DeleteMessages( + MTP_flags(revoke ? Flag::f_revoke : Flag(0)), + MTP_vector(ids) + )).done(done).send(); + } +} + void ApiWrap::saveDraftsToCloud() { for (auto i = _draftsSaveRequestIds.begin(), e = _draftsSaveRequestIds.end(); i != e; ++i) { if (i->second) continue; // sent already diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index d261a89dfe..0c00072d88 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -213,6 +213,10 @@ public: void clearHistory(not_null peer, bool revoke); void deleteConversation(not_null peer, bool revoke); + void deleteMessages( + not_null peer, + const QVector &ids, + bool revoke); base::Observable &fullPeerUpdated() { return _fullPeerUpdated; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 07ebb2879a..dda4aa691d 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/empty_userpic.h" #include "core/click_handler_types.h" +#include "window/window_controller.h" #include "storage/localstorage.h" #include "data/data_session.h" #include "data/data_channel.h" @@ -473,13 +474,52 @@ DeleteMessagesBox::DeleteMessagesBox( Expects(!_ids.empty()); } +DeleteMessagesBox::DeleteMessagesBox( + QWidget*, + not_null peer, + bool justClear) +: _wipeHistoryPeer(peer) +, _wipeHistoryJustClear(justClear) { +} + void DeleteMessagesBox::prepare() { auto details = TextWithEntities(); const auto appendDetails = [&](TextWithEntities &&text) { TextUtilities::Append(details, { "\n\n" }); TextUtilities::Append(details, std::move(text)); }; - if (_moderateFrom) { + auto deleteKey = lng_box_delete; + auto deleteStyle = &st::defaultBoxButton; + if (const auto peer = _wipeHistoryPeer) { + if (_wipeHistoryJustClear) { + details.text = peer->isSelf() + ? lang(lng_sure_delete_saved_messages) + : peer->isUser() + ? lng_sure_delete_history(lt_contact, peer->name) + : lng_sure_delete_group_history(lt_group, peer->name); + deleteStyle = &st::attentionBoxButton; + } else { + details.text = peer->isSelf() + ? lang(lng_sure_delete_saved_messages) + : peer->isUser() + ? lng_sure_delete_history(lt_contact, peer->name) + : peer->isChat() + ? lng_sure_delete_and_exit(lt_group, peer->name) + : lang(peer->isMegagroup() + ? lng_sure_leave_group + : lng_sure_leave_channel); + deleteKey = _wipeHistoryPeer->isUser() + ? lng_box_delete + : lng_box_leave; + deleteStyle = &(peer->isChannel() + ? st::defaultBoxButton + : st::attentionBoxButton); + } + if (auto revoke = revokeText(peer)) { + _revoke.create(this, revoke->checkbox, false, st::defaultBoxCheckbox); + appendDetails(std::move(revoke->description)); + } + } else if (_moderateFrom) { Assert(_moderateInChannel != nullptr); details.text = lang(lng_selected_delete_sure_this); if (_moderateBan) { @@ -511,8 +551,11 @@ void DeleteMessagesBox::prepare() { } _text.create(this, rpl::single(std::move(details)), st::boxLabel); - addButton(langFactory(lng_box_delete), [this] { deleteAndClear(); }); - addButton(langFactory(lng_cancel), [this] { closeBox(); }); + addButton( + langFactory(deleteKey), + [=] { deleteAndClear(); }, + *deleteStyle); + addButton(langFactory(lng_cancel), [=] { closeBox(); }); auto fullHeight = st::boxPadding.top() + _text->height() + st::boxPadding.bottom(); if (_moderateFrom) { @@ -547,6 +590,20 @@ PeerData *DeleteMessagesBox::checkFromSinglePeer() const { auto DeleteMessagesBox::revokeText(not_null peer) const -> std::optional { + auto result = RevokeConfig(); + if (peer == _wipeHistoryPeer) { + if (!peer->canRevokeFullHistory()) { + return std::nullopt; + } else if (const auto user = peer->asUser()) { + result.checkbox = lng_delete_for_other_check( + lt_user, + user->firstName); + } else { + result.checkbox = lang(lng_delete_for_everyone_check); + } + return result; + } + const auto items = ranges::view::all( _ids ) | ranges::view::transform([](FullMsgId id) { @@ -554,6 +611,7 @@ auto DeleteMessagesBox::revokeText(not_null peer) const }) | ranges::view::filter([](HistoryItem *item) { return (item != nullptr); }) | ranges::to_vector; + if (items.size() != _ids.size()) { // We don't have information about all messages. return std::nullopt; @@ -575,13 +633,14 @@ auto DeleteMessagesBox::revokeText(not_null peer) const ? -1 : ranges::count_if(outgoing, canRevoke); - auto result = RevokeConfig(); if (canRevokeAll) { - result.checkbox = peer->isUser() - ? lng_delete_for_other_check( + if (const auto user = peer->asUser()) { + result.checkbox = lng_delete_for_other_check( lt_user, - peer->asUser()->firstName) - : lang(lng_delete_for_everyone_check); + user->firstName); + } else { + result.checkbox = lang(lng_delete_for_everyone_check); + } return result; } else if (canRevokeOutgoingCount > 0) { result.checkbox = lang(lng_delete_for_other_my); @@ -645,6 +704,28 @@ void DeleteMessagesBox::keyPressEvent(QKeyEvent *e) { } void DeleteMessagesBox::deleteAndClear() { + const auto revoke = _revoke ? _revoke->checked() : false; + if (const auto peer = _wipeHistoryPeer) { + const auto justClear = _wipeHistoryJustClear; + closeBox(); + + if (justClear) { + peer->session().api().clearHistory(peer, revoke); + } else { + const auto controller = App::wnd()->controller(); + if (controller->activeChatCurrent().peer() == peer) { + Ui::showChatsList(); + } + // Don't delete old history by default, + // because Android app doesn't. + // + //if (const auto from = peer->migrateFrom()) { + // peer->session().api().deleteConversation(from, false); + //} + peer->session().api().deleteConversation(peer, revoke); + } + return; + } if (_moderateFrom) { if (_banUser && _banUser->checked()) { _moderateInChannel->session().api().kickParticipant( @@ -688,9 +769,8 @@ void DeleteMessagesBox::deleteAndClear() { } } - const auto revoke = _revoke ? _revoke->checked() : false; for (const auto &[peer, ids] : idsByPeer) { - App::main()->deleteMessages(peer, ids, revoke); + peer->session().api().deleteMessages(peer, ids, revoke); } Ui::hideLayer(); Auth().data().sendHistoryChangeNotifications(); diff --git a/Telegram/SourceFiles/boxes/confirm_box.h b/Telegram/SourceFiles/boxes/confirm_box.h index ced2de6d7b..ab7ae21059 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.h +++ b/Telegram/SourceFiles/boxes/confirm_box.h @@ -151,7 +151,7 @@ public: not_null item, bool suggestModerateActions); DeleteMessagesBox(QWidget*, MessageIdsList &&selected); - DeleteMessagesBox(QWidget*, not_null peer, bool deleteChat); + DeleteMessagesBox(QWidget*, not_null peer, bool justClear); void setDeleteConfirmedCallback(Fn callback) { _deleteConfirmedCallback = std::move(callback); @@ -172,6 +172,8 @@ private: PeerData *checkFromSinglePeer() const; std::optional revokeText(not_null peer) const; + PeerData * const _wipeHistoryPeer = nullptr; + const bool _wipeHistoryJustClear = false; const MessageIdsList _ids; UserData *_moderateFrom = nullptr; ChannelData *_moderateInChannel = nullptr; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index b8b161a8fa..4d7722e09d 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -648,6 +648,12 @@ Data::RestrictionCheckResult PeerData::amRestricted( return Result::Allowed(); } +bool PeerData::canRevokeFullHistory() const { + return isUser() + && Global::RevokePrivateInbox() + && (Global::RevokePrivateTimeLimit() == 0x7FFFFFFF); +} + namespace Data { std::vector ListOfRestrictions() { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 700b9eb0a9..20864dc15b 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -160,6 +160,7 @@ public: [[nodiscard]] bool canWrite() const; [[nodiscard]] Data::RestrictionCheckResult amRestricted( ChatRestriction right) const; + [[nodiscard]] bool canRevokeFullHistory() const; [[nodiscard]] UserData *asUser(); [[nodiscard]] const UserData *asUser() const; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 660f80e476..93a95b4c1b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -899,26 +899,6 @@ void MainWidget::dialogsActivate() { _dialogs->activate(); } -void MainWidget::deleteMessages( - not_null peer, - const QVector &ids, - bool revoke) { - if (const auto channel = peer->asChannel()) { - MTP::send( - MTPchannels_DeleteMessages( - channel->inputChannel, - MTP_vector(ids)), - rpcDone(&MainWidget::messagesAffected, peer)); - } else { - using Flag = MTPmessages_DeleteMessages::Flag; - MTP::send( - MTPmessages_DeleteMessages( - MTP_flags(revoke ? Flag::f_revoke : Flag(0)), - MTP_vector(ids)), - rpcDone(&MainWidget::messagesAffected, peer)); - } -} - void MainWidget::removeDialog(Dialogs::Key key) { _dialogs->removeDialog(key); } @@ -1055,21 +1035,6 @@ void MainWidget::checkLastUpdate(bool afterSleep) { } } -void MainWidget::messagesAffected( - not_null peer, - const MTPmessages_AffectedMessages &result) { - const auto &data = result.c_messages_affectedMessages(); - if (const auto channel = peer->asChannel()) { - channel->ptsUpdateAndApply(data.vpts.v, data.vpts_count.v); - } else { - ptsUpdateAndApply(data.vpts.v, data.vpts_count.v); - } - - if (const auto history = session().data().historyLoaded(peer)) { - history->requestChatListMessage(); - } -} - void MainWidget::handleAudioUpdate(const Media::Player::TrackState &state) { using State = Media::Player::State; const auto document = state.id.audio(); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index a559631ff0..e5afee9576 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -195,11 +195,6 @@ public: void deletePhotoLayer(PhotoData *photo); - void deleteMessages( - not_null peer, - const QVector &ids, - bool revoke); - bool sendMessageFail(const RPCError &error); Dialogs::IndexedList *contactsList(); @@ -371,10 +366,6 @@ private: void destroyExportTopBar(); void exportTopBarHeightUpdated(); - void messagesAffected( - not_null peer, - const MTPmessages_AffectedMessages &result); - Window::SectionSlideParams prepareShowAnimation( bool willHaveTopBarShadow); void showNewSection( diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index e28a4358c2..e4f44faf6a 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -788,69 +788,13 @@ void PeerMenuAddMuteAction( Fn ClearHistoryHandler(not_null peer) { return [=] { - const auto weak = std::make_shared>(); - const auto text = peer->isSelf() - ? lang(lng_sure_delete_saved_messages) - : peer->isUser() - ? lng_sure_delete_history(lt_contact, peer->name) - : lng_sure_delete_group_history(lt_group, peer->name); - const auto callback = [=] { - if (const auto strong = *weak) { - strong->closeBox(); - } - peer->session().api().clearHistory(peer, false); - }; - *weak = Ui::show( - Box( - text, - lang(lng_box_delete), - st::attentionBoxButton, - callback), - LayerOption::KeepOther); + Ui::show(Box(peer, true), LayerOption::KeepOther); }; } Fn DeleteAndLeaveHandler(not_null peer) { return [=] { - const auto weak = std::make_shared>(); - const auto text = peer->isSelf() - ? lang(lng_sure_delete_saved_messages) - : peer->isUser() - ? lng_sure_delete_history(lt_contact, peer->name) - : peer->isChat() - ? lng_sure_delete_and_exit(lt_group, peer->name) - : lang(peer->isMegagroup() - ? lng_sure_leave_group - : lng_sure_leave_channel); - const auto confirm = lang(peer->isUser() - ? lng_box_delete - : lng_box_leave); - const auto &confirmStyle = peer->isChannel() - ? st::defaultBoxButton - : st::attentionBoxButton; - const auto callback = [=] { - if (const auto strong = *weak) { - strong->closeBox(); - } - const auto controller = App::wnd()->controller(); - if (controller->activeChatCurrent().peer() == peer) { - Ui::showChatsList(); - } - // Don't delete old history by default, - // because Android app doesn't. - // - //if (const auto from = peer->migrateFrom()) { - // peer->session().api().deleteConversation(from, false); - //} - peer->session().api().deleteConversation(peer, false); - }; - *weak = Ui::show( - Box( - text, - confirm, - confirmStyle, - callback), - LayerOption::KeepOther); + Ui::show(Box(peer, false), LayerOption::KeepOther); }; }