Delete history for specific days in private chats.

This commit is contained in:
John Preston 2021-11-16 11:26:35 +04:00
parent aa0a9b2db9
commit 80fcffcc40
18 changed files with 211 additions and 59 deletions

View file

@ -81,6 +81,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_calendar_select_days" = "Select days"; "lng_calendar_select_days" = "Select days";
"lng_calendar_start_tip" = "Press and hold to jump to the start."; "lng_calendar_start_tip" = "Press and hold to jump to the start.";
"lng_calendar_end_tip" = "Press and hold to jump to the end."; "lng_calendar_end_tip" = "Press and hold to jump to the end.";
"lng_calendar_days#one" = "{count} day";
"lng_calendar_days#other" = "{count} days";
"lng_box_ok" = "OK"; "lng_box_ok" = "OK";
"lng_box_done" = "Done"; "lng_box_done" = "Done";
@ -1134,6 +1136,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sure_delete_saved_messages" = "Are you sure, you want to delete all your saved messages?\n\nThis action cannot be undone."; "lng_sure_delete_saved_messages" = "Are you sure, you want to delete all your saved messages?\n\nThis action cannot be undone.";
"lng_no_clear_history_channel" = "In channels you can enable auto-delete for messages."; "lng_no_clear_history_channel" = "In channels you can enable auto-delete for messages.";
"lng_no_clear_history_group" = "In public groups you can enable auto-delete for messages."; "lng_no_clear_history_group" = "In public groups you can enable auto-delete for messages.";
"lng_sure_delete_by_date_one" = "Are you sure you want to delete all messages for **{date}**?\n\nThis action cannot be undone.";
"lng_sure_delete_by_date_many" = "Are you sure you want to delete all messages for the **{days}**?\n\nThis action cannot be undone.";
"lng_sure_delete_selected_days#one" = "{count} selected day";
"lng_sure_delete_selected_days#other" = "{count} selected days";
"lng_message_empty" = "Empty Message"; "lng_message_empty" = "Empty Message";
"lng_message_unsupported" = "This message is not supported by your version of Telegram Desktop. Please update to the latest version in Settings, or install it from {link}"; "lng_message_unsupported" = "This message is not supported by your version of Telegram Desktop. Please update to the latest version in Settings, or install it from {link}";

View file

@ -54,6 +54,18 @@ DeleteMessagesBox::DeleteMessagesBox(
Expects(!_ids.empty()); 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( DeleteMessagesBox::DeleteMessagesBox(
QWidget*, QWidget*,
not_null<PeerData*> peer, not_null<PeerData*> peer,
@ -73,7 +85,27 @@ void DeleteMessagesBox::prepare() {
auto deleteStyle = &st::defaultBoxButton; auto deleteStyle = &st::defaultBoxButton;
auto canDelete = true; auto canDelete = true;
if (const auto peer = _wipeHistoryPeer) { if (const auto peer = _wipeHistoryPeer) {
if (_wipeHistoryJustClear) { 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 isChannel = peer->isBroadcast();
const auto isPublicGroup = peer->isMegagroup() const auto isPublicGroup = peer->isMegagroup()
&& peer->asChannel()->isPublic(); && peer->asChannel()->isPublic();
@ -397,16 +429,41 @@ void DeleteMessagesBox::keyPressEvent(QKeyEvent *e) {
void DeleteMessagesBox::deleteAndClear() { void DeleteMessagesBox::deleteAndClear() {
const auto revoke = _revoke ? _revoke->checked() : false; const auto revoke = _revoke ? _revoke->checked() : false;
if (const auto peer = _wipeHistoryPeer) { 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; const auto justClear = _wipeHistoryJustClear;
closeBox(); invokeCallbackAndClose();
if (justClear) { if (justClear) {
peer->session().api().clearHistory(peer, revoke); session->api().clearHistory(peer, revoke);
} else { } else {
for (const auto &controller : peer->session().windows()) { for (const auto &controller : session->windows()) {
if (controller->activeChatCurrent().peer() == peer) { if (controller->activeChatCurrent().peer() == peer) {
Ui::showChatsList(&peer->session()); Ui::showChatsList(session);
} }
} }
// Don't delete old history by default, // Don't delete old history by default,
@ -415,7 +472,7 @@ void DeleteMessagesBox::deleteAndClear() {
//if (const auto from = peer->migrateFrom()) { //if (const auto from = peer->migrateFrom()) {
// peer->session().api().deleteConversation(from, false); // peer->session().api().deleteConversation(from, false);
//} //}
peer->session().api().deleteConversation(peer, revoke); session->api().deleteConversation(peer, revoke);
} }
return; return;
} }
@ -441,19 +498,8 @@ void DeleteMessagesBox::deleteAndClear() {
} }
} }
if (_deleteConfirmedCallback) { const auto ids = _ids;
_deleteConfirmedCallback(); invokeCallbackAndClose();
} session->data().histories().deleteMessages(ids, revoke);
// deleteMessages can initiate closing of the current section,
// which will cause this box to be destroyed.
const auto session = _session;
const auto weak = Ui::MakeWeak(this);
session->data().histories().deleteMessages(_ids, revoke);
if (const auto strong = weak.data()) {
strong->closeBox();
}
session->data().sendHistoryChangeNotifications(); session->data().sendHistoryChangeNotifications();
} }

View file

@ -29,6 +29,11 @@ public:
QWidget*, QWidget*,
not_null<Main::Session*> session, not_null<Main::Session*> session,
MessageIdsList &&selected); MessageIdsList &&selected);
DeleteMessagesBox(
QWidget*,
not_null<PeerData*> peer,
QDate firstDayToDelete,
QDate lastDayToDelete);
DeleteMessagesBox(QWidget*, not_null<PeerData*> peer, bool justClear); DeleteMessagesBox(QWidget*, not_null<PeerData*> peer, bool justClear);
void setDeleteConfirmedCallback(Fn<void()> callback) { void setDeleteConfirmedCallback(Fn<void()> callback) {
@ -56,6 +61,8 @@ private:
PeerData * const _wipeHistoryPeer = nullptr; PeerData * const _wipeHistoryPeer = nullptr;
const bool _wipeHistoryJustClear = false; const bool _wipeHistoryJustClear = false;
const QDate _wipeHistoryFirstToDelete;
const QDate _wipeHistoryLastToDelete;
const MessageIdsList _ids; const MessageIdsList _ids;
UserData *_moderateFrom = nullptr; UserData *_moderateFrom = nullptr;
ChannelData *_moderateInChannel = nullptr; ChannelData *_moderateInChannel = nullptr;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_scheduled_messages.h" #include "data/data_scheduled_messages.h"
#include "base/unixtime.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "history/history.h" #include "history/history.h"
@ -708,6 +709,58 @@ void Histories::deleteAllMessages(
}); });
} }
void Histories::deleteMessagesByDates(
not_null<History*> history,
QDate firstDayToDelete,
QDate lastDayToDelete,
bool revoke) {
const auto firstSecondToDelete = base::unixtime::serialize(
{ firstDayToDelete, QTime(0, 0) }
);
const auto lastSecondToDelete = base::unixtime::serialize(
{ lastDayToDelete, QTime(23, 59, 59) }
);
deleteMessagesByDates(
history,
firstSecondToDelete - 1,
lastSecondToDelete + 1,
revoke);
}
void Histories::deleteMessagesByDates(
not_null<History*> history,
TimeId minDate,
TimeId maxDate,
bool revoke) {
sendRequest(history, RequestType::Delete, [=](Fn<void()> finish) {
const auto peer = history->peer;
const auto fail = [=](const MTP::Error &error) {
finish();
};
using Flag = MTPmessages_DeleteHistory::Flag;
const auto flags = Flag::f_just_clear
| Flag::f_min_date
| Flag::f_max_date
| (revoke ? Flag::f_revoke : Flag(0));
return session().api().request(MTPmessages_DeleteHistory(
MTP_flags(flags),
peer->input,
MTP_int(0),
MTP_int(minDate),
MTP_int(maxDate)
)).done([=](const MTPmessages_AffectedHistory &result) {
const auto offset = session().api().applyAffectedHistory(
peer,
result);
if (offset > 0) {
deleteMessagesByDates(history, minDate, maxDate, revoke);
}
finish();
}).fail(fail).send();
});
history->destroyMessagesByDates(minDate, maxDate);
}
void Histories::deleteMessages(const MessageIdsList &ids, bool revoke) { void Histories::deleteMessages(const MessageIdsList &ids, bool revoke) {
auto remove = std::vector<not_null<HistoryItem*>>(); auto remove = std::vector<not_null<HistoryItem*>>();
remove.reserve(ids.size()); remove.reserve(ids.size());

View file

@ -71,6 +71,17 @@ public:
bool justClear, bool justClear,
bool revoke); bool revoke);
void deleteMessagesByDates(
not_null<History*> history,
QDate firstDayToDelete,
QDate lastDayToDelete,
bool revoke);
void deleteMessagesByDates(
not_null<History*> history,
TimeId minDate,
TimeId maxDate,
bool revoke);
void deleteMessages(const MessageIdsList &ids, bool revoke); void deleteMessages(const MessageIdsList &ids, bool revoke);
int sendRequest( int sendRequest(

View file

@ -284,7 +284,7 @@ Widget::Widget(
}, lifetime()); }, lifetime());
_cancelSearch->setClickedCallback([this] { onCancelSearch(); }); _cancelSearch->setClickedCallback([this] { onCancelSearch(); });
_jumpToDate->entity()->setClickedCallback([this] { showJumpToDate(); }); _jumpToDate->entity()->setClickedCallback([this] { showCalendar(); });
_chooseFromUser->entity()->setClickedCallback([this] { showSearchFrom(); }); _chooseFromUser->entity()->setClickedCallback([this] { showSearchFrom(); });
rpl::single( rpl::single(
rpl::empty_value() rpl::empty_value()
@ -1453,9 +1453,9 @@ void Widget::clearSearchCache() {
cancelSearchRequest(); cancelSearchRequest();
} }
void Widget::showJumpToDate() { void Widget::showCalendar() {
if (_searchInChat) { if (_searchInChat) {
controller()->showJumpToDate(_searchInChat, QDate()); controller()->showCalendar(_searchInChat, QDate());
} }
} }

View file

@ -150,7 +150,7 @@ private:
void setupMainMenuToggle(); void setupMainMenuToggle();
bool searchForPeersRequired(const QString &query) const; bool searchForPeersRequired(const QString &query) const;
void setSearchInChat(Key chat, PeerData *from = nullptr); void setSearchInChat(Key chat, PeerData *from = nullptr);
void showJumpToDate(); void showCalendar();
void showSearchFrom(); void showSearchFrom();
void showMainMenu(); void showMainMenu();
void clearSearchCache(); void clearSearchCache();

View file

@ -462,6 +462,20 @@ void History::destroyMessage(not_null<HistoryItem*> item) {
} }
} }
void History::destroyMessagesByDates(TimeId minDate, TimeId maxDate) {
auto toDestroy = std::vector<not_null<HistoryItem*>>();
for (const auto &message : _messages) {
if (message->isRegular()
&& message->date() > minDate
&& message->date() < maxDate) {
toDestroy.push_back(message.get());
}
}
for (const auto item : toDestroy) {
item->destroy();
}
}
void History::unpinAllMessages() { void History::unpinAllMessages() {
session().storage().remove( session().storage().remove(
Storage::SharedMediaRemoveAll( Storage::SharedMediaRemoveAll(

View file

@ -132,6 +132,7 @@ public:
std::forward<Args>(args)...)).get()); std::forward<Args>(args)...)).get());
} }
void destroyMessage(not_null<HistoryItem*> item); void destroyMessage(not_null<HistoryItem*> item);
void destroyMessagesByDates(TimeId minDate, TimeId maxDate);
void unpinAllMessages(); void unpinAllMessages();

View file

@ -6782,13 +6782,10 @@ void HistoryWidget::confirmDeleteSelected() {
if (items.empty()) { if (items.empty()) {
return; return;
} }
const auto weak = Ui::MakeWeak(this);
auto box = Box<DeleteMessagesBox>(&session(), std::move(items)); auto box = Box<DeleteMessagesBox>(&session(), std::move(items));
box->setDeleteConfirmedCallback([=] { box->setDeleteConfirmedCallback(crl::guard(this, [=] {
if (const auto strong = weak.data()) { clearSelected();
strong->clearSelected(); }));
}
});
controller()->show(std::move(box)); controller()->show(std::move(box));
} }

View file

@ -689,16 +689,13 @@ bool AddDeleteSelectedAction(
} }
menu->addAction(tr::lng_context_delete_selected(tr::now), [=] { menu->addAction(tr::lng_context_delete_selected(tr::now), [=] {
const auto weak = Ui::MakeWeak(list);
auto items = ExtractIdsList(request.selectedItems); auto items = ExtractIdsList(request.selectedItems);
auto box = Box<DeleteMessagesBox>( auto box = Box<DeleteMessagesBox>(
&request.navigation->session(), &request.navigation->session(),
std::move(items)); std::move(items));
box->setDeleteConfirmedCallback([=] { box->setDeleteConfirmedCallback(crl::guard(list, [=] {
if (const auto strong = weak.data()) { list->cancelSelection();
strong->cancelSelection(); }));
}
});
request.navigation->parentController()->show(std::move(box)); request.navigation->parentController()->show(std::move(box));
}); });
return true; return true;

View file

@ -3027,15 +3027,12 @@ void ConfirmDeleteSelectedItems(not_null<ListWidget*> widget) {
return; return;
} }
} }
const auto weak = Ui::MakeWeak(widget);
auto box = Box<DeleteMessagesBox>( auto box = Box<DeleteMessagesBox>(
&widget->controller()->session(), &widget->controller()->session(),
widget->getSelectedIds()); widget->getSelectedIds());
box->setDeleteConfirmedCallback([=] { box->setDeleteConfirmedCallback(crl::guard(widget, [=] {
if (const auto strong = weak.data()) { widget->cancelSelection();
strong->cancelSelection(); }));
}
});
widget->controller()->show(std::move(box)); widget->controller()->show(std::move(box));
} }

View file

@ -544,11 +544,9 @@ void TopBar::performDelete() {
auto box = Box<DeleteMessagesBox>( auto box = Box<DeleteMessagesBox>(
&_navigation->session(), &_navigation->session(),
std::move(items)); std::move(items));
box->setDeleteConfirmedCallback([weak = Ui::MakeWeak(this)] { box->setDeleteConfirmedCallback(crl::guard(this, [=] {
if (weak) { _cancelSelectionClicks.fire({});
weak->_cancelSelectionClicks.fire({}); }));
}
});
_navigation->parentController()->show(std::move(box)); _navigation->parentController()->show(std::move(box));
} }
} }

View file

@ -1715,12 +1715,9 @@ void ListWidget::forwardItems(MessageIdsList &&items) {
void ListWidget::deleteSelected() { void ListWidget::deleteSelected() {
if (const auto box = deleteItems(collectSelectedIds())) { if (const auto box = deleteItems(collectSelectedIds())) {
const auto weak = Ui::MakeWeak(this); box->setDeleteConfirmedCallback(crl::guard(this, [=]{
box->setDeleteConfirmedCallback([=]{ clearSelected();
if (const auto strong = weak.data()) { }));
strong->clearSelected();
}
});
} }
} }

View file

@ -693,7 +693,10 @@ CalendarBox::Title::Title(
} else if (!_context->selectedMin()) { } else if (!_context->selectedMin()) {
setText(tr::lng_calendar_select_days(tr::now)); setText(tr::lng_calendar_select_days(tr::now));
} else { } else {
setText(QString::number(1 + *_context->selectedMax() - *_context->selectedMin())); // #TODO calendar setText(tr::lng_calendar_days(
tr::now,
lt_count,
(1 + *_context->selectedMax() - *_context->selectedMin())));
} }
}, lifetime()); }, lifetime());
} }
@ -803,6 +806,16 @@ void CalendarBox::toggleSelectionMode(bool enabled) {
_context->toggleSelectionMode(enabled); _context->toggleSelectionMode(enabled);
} }
QDate CalendarBox::selectedFirstDate() const {
const auto min = _context->selectedMin();
return min.has_value() ? _context->dateFromIndex(*min) : QDate();
}
QDate CalendarBox::selectedLastDate() const {
const auto max = _context->selectedMax();
return max.has_value() ? _context->dateFromIndex(*max) : QDate();
}
void CalendarBox::showJumpTooltip(not_null<IconButton*> button) { void CalendarBox::showJumpTooltip(not_null<IconButton*> button) {
_tooltipButton = button; _tooltipButton = button;
Ui::Tooltip::Show(kTooltipDelay, this); Ui::Tooltip::Show(kTooltipDelay, this);

View file

@ -50,6 +50,9 @@ public:
void toggleSelectionMode(bool enabled); void toggleSelectionMode(bool enabled);
[[nodiscard]] QDate selectedFirstDate() const;
[[nodiscard]] QDate selectedLastDate() const;
protected: protected:
void prepare() override; void prepare() override;

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/add_contact_box.h" #include "boxes/add_contact_box.h"
#include "boxes/peers/edit_peer_info_box.h" #include "boxes/peers/edit_peer_info_box.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "boxes/delete_messages_box.h"
#include "window/window_adaptive.h" #include "window/window_adaptive.h"
#include "window/window_controller.h" #include "window/window_controller.h"
#include "window/main_window.h" #include "window/main_window.h"
@ -151,7 +152,7 @@ void DateClickHandler::setDate(QDate date) {
void DateClickHandler::onClick(ClickContext context) const { void DateClickHandler::onClick(ClickContext context) const {
const auto my = context.other.value<ClickHandlerContext>(); const auto my = context.other.value<ClickHandlerContext>();
if (const auto window = my.sessionWindow.get()) { if (const auto window = my.sessionWindow.get()) {
window->showJumpToDate(_chat, _date); window->showCalendar(_chat, _date);
} }
} }
@ -1146,7 +1147,7 @@ void SessionController::startOrJoinGroupCall(
} }
} }
void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) {
const auto history = chat.history(); const auto history = chat.history();
if (!history) { if (!history) {
return; return;
@ -1248,7 +1249,18 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) {
}); });
auto text = tr::lng_profile_clear_history(); auto text = tr::lng_profile_clear_history();
const auto button = box->addLeftButton(std::move(text), [=] { const auto button = box->addLeftButton(std::move(text), [=] {
const auto firstDate = box->selectedFirstDate();
const auto lastDate = box->selectedLastDate();
if (!firstDate.isNull()) {
auto confirm = Box<DeleteMessagesBox>(
history->peer,
firstDate,
lastDate);
confirm->setDeleteConfirmedCallback(crl::guard(box, [=] {
box->closeBox();
}));
box->getDelegate()->show(std::move(confirm));
}
}, (*selected > 0) ? st::attentionBoxButton : buttonState->disabled); }, (*selected > 0) ? st::attentionBoxButton : buttonState->disabled);
if (!*selected) { if (!*selected) {
button->setPointerCursor(false); button->setPointerCursor(false);

View file

@ -363,7 +363,7 @@ public:
} }
void removeLayerBlackout(); void removeLayerBlackout();
void showJumpToDate( void showCalendar(
Dialogs::Key chat, Dialogs::Key chat,
QDate requestedDate); QDate requestedDate);