| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "boxes/delete_messages_box.h"
- #include "apiwrap.h"
- #include "api/api_chat_participants.h"
- #include "api/api_messages_search.h"
- #include "base/unixtime.h"
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "data/data_channel.h"
- #include "data/data_chat.h"
- #include "data/data_histories.h"
- #include "data/data_session.h"
- #include "data/data_user.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "lang/lang_keys.h"
- #include "main/main_session.h"
- #include "menu/menu_ttl_validator.h"
- #include "ui/layers/generic_box.h"
- #include "ui/text/text_utilities.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/checkbox.h"
- #include "ui/widgets/labels.h"
- #include "ui/wrap/slide_wrap.h"
- #include "styles/style_layers.h"
- #include "styles/style_boxes.h"
- DeleteMessagesBox::DeleteMessagesBox(
- QWidget*,
- not_null<HistoryItem*> item,
- bool suggestModerateActions)
- : _session(&item->history()->session())
- , _ids(1, item->fullId()) {
- if (suggestModerateActions) {
- _moderateBan = item->suggestBanReport();
- _moderateDeleteAll = item->suggestDeleteAllReport();
- if (_moderateBan || _moderateDeleteAll) {
- _moderateFrom = item->from();
- _moderateInChannel = item->history()->peer->asChannel();
- }
- }
- }
- DeleteMessagesBox::DeleteMessagesBox(
- QWidget*,
- not_null<Main::Session*> session,
- MessageIdsList &&selected)
- : _session(session)
- , _ids(std::move(selected)) {
- Expects(!_ids.empty());
- }
- DeleteMessagesBox::DeleteMessagesBox(
- QWidget*,
- not_null<PeerData*> peer,
- QDate firstDayToDelete,
- QDate lastDayToDelete)
- : _session(&peer->session())
- , _wipeHistoryPeer(peer)
- , _wipeHistoryJustClear(true)
- , _wipeHistoryFirstToDelete(firstDayToDelete)
- , _wipeHistoryLastToDelete(lastDayToDelete) {
- }
- DeleteMessagesBox::DeleteMessagesBox(
- QWidget*,
- not_null<PeerData*> peer,
- bool justClear)
- : _session(&peer->session())
- , _wipeHistoryPeer(peer)
- , _wipeHistoryJustClear(justClear) {
- }
- void DeleteMessagesBox::prepare() {
- auto details = TextWithEntities();
- const auto appendDetails = [&](TextWithEntities &&text) {
- details.append(u"\n\n"_q).append(std::move(text));
- };
- auto deleteText = lifetime().make_state<rpl::variable<QString>>();
- *deleteText = tr::lng_box_delete();
- auto deleteStyle = &st::defaultBoxButton;
- auto canDelete = true;
- if (const auto peer = _wipeHistoryPeer) {
- if (!_wipeHistoryFirstToDelete.isNull()) {
- details = (_wipeHistoryFirstToDelete
- == _wipeHistoryLastToDelete)
- ? tr::lng_sure_delete_by_date_one(
- tr::now,
- lt_date,
- TextWithEntities{
- langDayOfMonthFull(_wipeHistoryFirstToDelete) },
- Ui::Text::RichLangValue)
- : tr::lng_sure_delete_by_date_many(
- tr::now,
- lt_days,
- tr::lng_sure_delete_selected_days(
- tr::now,
- lt_count,
- _wipeHistoryFirstToDelete.daysTo(
- _wipeHistoryLastToDelete) + 1,
- Ui::Text::WithEntities),
- Ui::Text::RichLangValue);
- deleteStyle = &st::attentionBoxButton;
- } else if (_wipeHistoryJustClear) {
- const auto isChannel = peer->isBroadcast();
- const auto isPublicGroup = peer->isMegagroup()
- && peer->asChannel()->isPublic();
- if (isChannel || isPublicGroup) {
- canDelete = false;
- }
- details.text = isChannel
- ? tr::lng_no_clear_history_channel(tr::now)
- : isPublicGroup
- ? tr::lng_no_clear_history_group(tr::now)
- : peer->isSelf()
- ? tr::lng_sure_delete_saved_messages(tr::now)
- : peer->isUser()
- ? tr::lng_sure_delete_history(
- tr::now,
- lt_contact,
- peer->name())
- : tr::lng_sure_delete_group_history(
- tr::now,
- lt_group,
- peer->name());
- details = Ui::Text::RichLangValue(details.text);
- deleteStyle = &st::attentionBoxButton;
- } else {
- details.text = peer->isSelf()
- ? tr::lng_sure_delete_saved_messages(tr::now)
- : peer->isUser()
- ? tr::lng_sure_delete_history(
- tr::now,
- lt_contact,
- peer->name())
- : peer->isChat()
- ? tr::lng_sure_delete_and_exit(
- tr::now,
- lt_group,
- peer->name())
- : peer->isMegagroup()
- ? tr::lng_sure_leave_group(tr::now)
- : tr::lng_sure_leave_channel(tr::now);
- details = Ui::Text::RichLangValue(details.text);
- if (!peer->isUser()) {
- *deleteText = tr::lng_box_leave();
- }
- deleteStyle = &st::attentionBoxButton;
- }
- if (auto revoke = revokeText(peer)) {
- _revoke.create(
- this,
- revoke->checkbox,
- false,
- st::defaultBoxCheckbox);
- appendDetails(std::move(revoke->description));
- if (!peer->isUser() && !_wipeHistoryJustClear) {
- _revoke->checkedValue(
- ) | rpl::start_with_next([=](bool revokeForAll) {
- *deleteText = revokeForAll
- ? tr::lng_box_delete()
- : tr::lng_box_leave();
- }, _revoke->lifetime());
- }
- } else if (canDelete
- && _wipeHistoryJustClear
- && (peer->isMegagroup() || peer->isChat())) {
- appendDetails({
- tr::lng_delete_clear_for_me(tr::now)
- });
- }
- } else if (_moderateFrom) {
- Assert(_moderateInChannel != nullptr);
- details.text = tr::lng_selected_delete_sure_this(tr::now);
- if (_moderateBan) {
- _banUser.create(
- this,
- tr::lng_ban_user(tr::now),
- false,
- st::defaultBoxCheckbox);
- }
- _reportSpam.create(
- this,
- tr::lng_report_spam(tr::now),
- false,
- st::defaultBoxCheckbox);
- if (_moderateDeleteAll) {
- const auto search = lifetime().make_state<Api::MessagesSearch>(
- _session->data().message(_ids.front())->history());
- _deleteAll.create(
- this,
- tr::lng_delete_all_from_user(
- tr::now,
- lt_user,
- Ui::Text::Bold(_moderateFrom->name()),
- Ui::Text::WithEntities),
- false,
- st::defaultBoxCheckbox);
- *deleteText = rpl::combine(
- rpl::single(
- 0
- ) | rpl::then(
- search->messagesFounds(
- ) | rpl::map([](const Api::FoundMessages &found) {
- return found.total;
- })
- ),
- _deleteAll->checkedValue()
- ) | rpl::map([](int total, bool checked) {
- return tr::lng_box_delete(tr::now)
- + ((total <= 0 || !checked)
- ? QString()
- : QString(" (%1)").arg(total));
- });
- search->searchMessages({ .from = _moderateFrom });
- }
- } else {
- details.text = (_ids.size() == 1)
- ? tr::lng_selected_delete_sure_this(tr::now)
- : tr::lng_selected_delete_sure(tr::now, lt_count, _ids.size());
- if (const auto peer = checkFromSinglePeer()) {
- auto count = int(_ids.size());
- if (hasScheduledMessages()) {
- } else if (auto revoke = revokeText(peer)) {
- const auto &settings = Core::App().settings();
- const auto revokeByDefault
- = !settings.rememberedDeleteMessageOnlyForYou();
- _revoke.create(
- this,
- revoke->checkbox,
- revokeByDefault,
- st::defaultBoxCheckbox);
- _revokeRemember.create(
- this,
- object_ptr<Ui::Checkbox>(
- this,
- tr::lng_remember(),
- false,
- st::defaultBoxCheckbox));
- _revokeRemember->hide(anim::type::instant);
- _revoke->checkedValue(
- ) | rpl::start_with_next([=](bool checked) {
- _revokeRemember->toggle(
- checked != revokeByDefault,
- anim::type::normal);
- }, _revokeRemember->lifetime());
- _revokeRemember->heightValue(
- ) | rpl::start_with_next([=](int h) {
- setDimensions(st::boxWidth, _fullHeight + h);
- }, lifetime());
- appendDetails(std::move(revoke->description));
- } else if (peer->isChannel()) {
- if (peer->isMegagroup()) {
- appendDetails({
- tr::lng_delete_for_everyone_hint(
- tr::now,
- lt_count,
- count)
- });
- }
- } else if (peer->isChat()) {
- appendDetails({
- tr::lng_delete_for_me_chat_hint(tr::now, lt_count, count)
- });
- } else if (!peer->isSelf()
- && (!peer->isUser() || !peer->asUser()->isInaccessible())) {
- if (const auto user = peer->asUser(); user && user->isBot()) {
- _revokeForBot = true;
- }
- appendDetails({
- tr::lng_delete_for_me_hint(tr::now, lt_count, count)
- });
- }
- }
- }
- _text.create(this, rpl::single(std::move(details)), st::boxLabel);
- if (_wipeHistoryJustClear && _wipeHistoryPeer) {
- const auto validator = TTLMenu::TTLValidator(
- uiShow(),
- _wipeHistoryPeer);
- if (validator.can()) {
- _wipeHistoryPeer->updateFull();
- _autoDeleteSettings.create(
- this,
- (_wipeHistoryPeer->messagesTTL()
- ? tr::lng_edit_auto_delete_settings(tr::now)
- : tr::lng_enable_auto_delete(tr::now)),
- st::boxLinkButton);
- _autoDeleteSettings->setClickedCallback([=] {
- validator.showBox();
- });
- }
- }
- if (canDelete) {
- addButton(
- deleteText->value(),
- [=] { deleteAndClear(); },
- *deleteStyle);
- addButton(tr::lng_cancel(), [=] { closeBox(); });
- } else {
- addButton(tr::lng_about_done(), [=] { closeBox(); });
- }
- auto fullHeight = st::boxPadding.top()
- + _text->height()
- + st::boxPadding.bottom();
- if (_moderateFrom) {
- fullHeight += st::boxMediumSkip;
- if (_banUser) {
- fullHeight += _banUser->heightNoMargins() + st::boxLittleSkip;
- }
- fullHeight += _reportSpam->heightNoMargins();
- if (_deleteAll) {
- fullHeight += st::boxLittleSkip + _deleteAll->heightNoMargins();
- }
- } else if (_revoke) {
- fullHeight += st::boxMediumSkip + _revoke->heightNoMargins();
- }
- if (_autoDeleteSettings) {
- fullHeight += st::boxMediumSkip
- + _autoDeleteSettings->height()
- + st::boxLittleSkip;
- }
- setDimensions(st::boxWidth, fullHeight);
- _fullHeight = fullHeight;
- }
- bool DeleteMessagesBox::hasScheduledMessages() const {
- for (const auto &fullId : _ids) {
- if (const auto item = _session->data().message(fullId)) {
- if (item->isScheduled()) {
- return true;
- }
- }
- }
- return false;
- }
- PeerData *DeleteMessagesBox::checkFromSinglePeer() const {
- auto result = (PeerData*)nullptr;
- for (const auto &fullId : _ids) {
- if (const auto item = _session->data().message(fullId)) {
- const auto peer = item->history()->peer;
- if (!result) {
- result = peer;
- } else if (result != peer) {
- return nullptr;
- }
- }
- }
- return result;
- }
- auto DeleteMessagesBox::revokeText(not_null<PeerData*> peer) const
- -> std::optional<RevokeConfig> {
- auto result = RevokeConfig();
- if (peer == _wipeHistoryPeer) {
- if (!peer->canRevokeFullHistory()) {
- return std::nullopt;
- } else if (const auto user = peer->asUser()) {
- result.checkbox = tr::lng_delete_for_other_check(
- tr::now,
- lt_user,
- { user->firstName },
- Ui::Text::RichLangValue);
- } else {
- result.checkbox.text = tr::lng_delete_for_everyone_check(tr::now);
- }
- return result;
- }
- const auto items = peer->owner().idsToItems(_ids);
- if (items.size() != _ids.size()) {
- // We don't have information about all messages.
- return std::nullopt;
- }
- const auto now = base::unixtime::now();
- const auto canRevoke = [&](HistoryItem * item) {
- return item->canDeleteForEveryone(now);
- };
- const auto cannotRevoke = [&](HistoryItem *item) {
- return !item->canDeleteForEveryone(now);
- };
- const auto canRevokeAll = ranges::none_of(items, cannotRevoke);
- auto outgoing = items | ranges::views::filter(&HistoryItem::out);
- const auto canRevokeOutgoingCount = canRevokeAll
- ? -1
- : ranges::count_if(outgoing, canRevoke);
- if (canRevokeAll) {
- if (const auto user = peer->asUser()) {
- result.checkbox = tr::lng_delete_for_other_check(
- tr::now,
- lt_user,
- { user->firstName },
- Ui::Text::RichLangValue);
- } else {
- result.checkbox.text = tr::lng_delete_for_everyone_check(tr::now);
- }
- return result;
- } else if (canRevokeOutgoingCount > 0) {
- result.checkbox.text = tr::lng_delete_for_other_my(tr::now);
- if (const auto user = peer->asUser()) {
- if (canRevokeOutgoingCount == 1) {
- result.description = tr::lng_selected_unsend_about_user_one(
- tr::now,
- lt_user,
- Ui::Text::Bold(user->shortName()),
- Ui::Text::WithEntities);
- } else {
- result.description = tr::lng_selected_unsend_about_user(
- tr::now,
- lt_count,
- canRevokeOutgoingCount,
- lt_user,
- Ui::Text::Bold(user->shortName()),
- Ui::Text::WithEntities);
- }
- } else if (canRevokeOutgoingCount == 1) {
- result.description = tr::lng_selected_unsend_about_group_one(
- tr::now,
- Ui::Text::WithEntities);
- } else {
- result.description = tr::lng_selected_unsend_about_group(
- tr::now,
- lt_count,
- canRevokeOutgoingCount,
- Ui::Text::WithEntities);
- }
- return result;
- }
- return std::nullopt;
- }
- void DeleteMessagesBox::resizeEvent(QResizeEvent *e) {
- BoxContent::resizeEvent(e);
- const auto &padding = st::boxPadding;
- _text->moveToLeft(padding.left(), padding.top());
- auto top = _text->bottomNoMargins() + st::boxMediumSkip;
- if (_moderateFrom) {
- if (_banUser) {
- _banUser->moveToLeft(padding.left(), top);
- top += _banUser->heightNoMargins() + st::boxLittleSkip;
- }
- _reportSpam->moveToLeft(padding.left(), top);
- top += _reportSpam->heightNoMargins() + st::boxLittleSkip;
- if (_deleteAll) {
- const auto availableWidth = width() - 2 * padding.left();
- _deleteAll->resizeToNaturalWidth(availableWidth);
- _deleteAll->moveToLeft(padding.left(), top);
- top += _deleteAll->heightNoMargins() + st::boxLittleSkip;
- }
- } else if (_revoke) {
- const auto availableWidth = width() - 2 * padding.left();
- _revoke->resizeToNaturalWidth(availableWidth);
- _revoke->moveToLeft(padding.left(), top);
- top += _revoke->heightNoMargins() + st::boxLittleSkip;
- if (_revokeRemember) {
- _revokeRemember->resizeToNaturalWidth(availableWidth);
- _revokeRemember->moveToLeft(padding.left(),top);
- top += _revokeRemember->heightNoMargins();
- }
- }
- if (_autoDeleteSettings) {
- top += st::boxMediumSkip - st::boxLittleSkip;
- _autoDeleteSettings->moveToLeft(padding.left(), top);
- }
- }
- void DeleteMessagesBox::keyPressEvent(QKeyEvent *e) {
- if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
- // Don't make the clearing history so easy.
- if (!_wipeHistoryPeer) {
- deleteAndClear();
- }
- } else {
- BoxContent::keyPressEvent(e);
- }
- }
- void DeleteMessagesBox::deleteAndClear() {
- if (_revoke
- && _revokeRemember
- && _revokeRemember->toggled()
- && _revokeRemember->entity()->checked()) {
- Core::App().settings().setRememberedDeleteMessageOnlyForYou(
- !_revoke->checked());
- Core::App().saveSettingsDelayed();
- }
- const auto revoke = _revoke ? _revoke->checked() : _revokeForBot;
- const auto session = _session;
- const auto invokeCallbackAndClose = [&] {
- // deleteMessages can initiate closing of the current section,
- // which will cause this box to be destroyed.
- const auto weak = Ui::MakeWeak(this);
- if (const auto callback = _deleteConfirmedCallback) {
- callback();
- }
- if (const auto strong = weak.data()) {
- strong->closeBox();
- }
- };
- if (!_wipeHistoryFirstToDelete.isNull()) {
- const auto peer = _wipeHistoryPeer;
- const auto firstDayToDelete = _wipeHistoryFirstToDelete;
- const auto lastDayToDelete = _wipeHistoryLastToDelete;
- invokeCallbackAndClose();
- session->data().histories().deleteMessagesByDates(
- session->data().history(peer),
- firstDayToDelete,
- lastDayToDelete,
- revoke);
- session->data().sendHistoryChangeNotifications();
- return;
- } else if (const auto peer = _wipeHistoryPeer) {
- const auto justClear = _wipeHistoryJustClear;
- invokeCallbackAndClose();
- if (justClear) {
- session->api().clearHistory(peer, revoke);
- } else {
- Core::App().closeChatFromWindows(peer);
- // Don't delete old history by default,
- // because Android app doesn't.
- //
- //if (const auto from = peer->migrateFrom()) {
- // peer->session().api().deleteConversation(from, false);
- //}
- session->api().deleteConversation(peer, revoke);
- }
- return;
- }
- if (_moderateFrom) {
- if (_banUser && _banUser->checked()) {
- _moderateInChannel->session().api().chatParticipants().kick(
- _moderateInChannel,
- _moderateFrom,
- ChatRestrictionsInfo());
- }
- if (_reportSpam->checked()) {
- _moderateInChannel->session().api().request(
- MTPchannels_ReportSpam(
- _moderateInChannel->inputChannel,
- _moderateFrom->input,
- MTP_vector<MTPint>(1, MTP_int(_ids[0].msg)))
- ).send();
- }
- if (_deleteAll && _deleteAll->checked()) {
- _moderateInChannel->session().api().deleteAllFromParticipant(
- _moderateInChannel,
- _moderateFrom);
- }
- }
- const auto ids = _ids;
- invokeCallbackAndClose();
- session->data().histories().deleteMessages(ids, revoke);
- session->data().sendHistoryChangeNotifications();
- }
|