mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support "Delete all files" menu in Downloads section.
This commit is contained in:
parent
32d09f189b
commit
1bc438ed01
7 changed files with 163 additions and 15 deletions
|
@ -1845,6 +1845,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_downloads_view_in_chat" = "View in chat";
|
"lng_downloads_view_in_chat" = "View in chat";
|
||||||
"lng_downloads_view_in_section" = "View in downloads";
|
"lng_downloads_view_in_section" = "View in downloads";
|
||||||
"lng_downloads_delete_sure_one" = "Do you want to delete this file?";
|
"lng_downloads_delete_sure_one" = "Do you want to delete this file?";
|
||||||
|
"lng_downloads_delete_sure_all" = "Do you want to delete all files?";
|
||||||
"lng_downloads_delete_sure#one" = "Do you want to delete {count} file?";
|
"lng_downloads_delete_sure#one" = "Do you want to delete {count} file?";
|
||||||
"lng_downloads_delete_sure#other" = "Do you want to delete {count} files?";
|
"lng_downloads_delete_sure#other" = "Do you want to delete {count} files?";
|
||||||
"lng_downloads_delete_in_cloud_one" = "It will be deleted from your disk, but will remain accessible in the cloud.";
|
"lng_downloads_delete_in_cloud_one" = "It will be deleted from your disk, but will remain accessible in the cloud.";
|
||||||
|
|
|
@ -63,8 +63,19 @@ constexpr auto ByDocument = [](const auto &entry) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DocumentDescriptor {
|
||||||
|
uint64 sessionUniqueId = 0;
|
||||||
|
DocumentId documentId = 0;
|
||||||
|
FullMsgId itemId;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
struct DownloadManager::DeleteFilesDescriptor {
|
||||||
|
base::flat_set<not_null<Main::Session*>> sessions;
|
||||||
|
base::flat_map<QString, DocumentDescriptor> files;
|
||||||
|
};
|
||||||
|
|
||||||
DownloadManager::DownloadManager()
|
DownloadManager::DownloadManager()
|
||||||
: _clearLoadingTimer([=] { clearLoading(); }) {
|
: _clearLoadingTimer([=] { clearLoading(); }) {
|
||||||
}
|
}
|
||||||
|
@ -118,7 +129,8 @@ void DownloadManager::itemVisibilitiesUpdated(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const auto &id : i->second.downloading) {
|
for (const auto &id : i->second.downloading) {
|
||||||
if (!session->data().queryItemVisibility(id.object.item)) {
|
if (!id.done
|
||||||
|
&& !session->data().queryItemVisibility(id.object.item)) {
|
||||||
for (auto &id : i->second.downloading) {
|
for (auto &id : i->second.downloading) {
|
||||||
id.hiddenByView = false;
|
id.hiddenByView = false;
|
||||||
}
|
}
|
||||||
|
@ -308,13 +320,7 @@ void DownloadManager::clearIfFinished() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadManager::deleteFiles(const std::vector<GlobalMsgId> &ids) {
|
void DownloadManager::deleteFiles(const std::vector<GlobalMsgId> &ids) {
|
||||||
struct DocumentDescriptor {
|
auto descriptor = DeleteFilesDescriptor();
|
||||||
uint64 sessionUniqueId = 0;
|
|
||||||
DocumentId documentId = 0;
|
|
||||||
FullMsgId itemId;
|
|
||||||
};
|
|
||||||
auto sessions = base::flat_set<not_null<Main::Session*>>();
|
|
||||||
auto files = base::flat_map<QString, DocumentDescriptor>();
|
|
||||||
for (const auto &id : ids) {
|
for (const auto &id : ids) {
|
||||||
if (const auto item = MessageByGlobalId(id)) {
|
if (const auto item = MessageByGlobalId(id)) {
|
||||||
const auto session = &item->history()->session();
|
const auto session = &item->history()->session();
|
||||||
|
@ -334,7 +340,7 @@ void DownloadManager::deleteFiles(const std::vector<GlobalMsgId> &ids) {
|
||||||
const auto k = ranges::find(data.downloaded, item, ByItem);
|
const auto k = ranges::find(data.downloaded, item, ByItem);
|
||||||
if (k != end(data.downloaded)) {
|
if (k != end(data.downloaded)) {
|
||||||
const auto document = k->object->document;
|
const auto document = k->object->document;
|
||||||
files.emplace(k->path, DocumentDescriptor{
|
descriptor.files.emplace(k->path, DocumentDescriptor{
|
||||||
.sessionUniqueId = id.sessionUniqueId,
|
.sessionUniqueId = id.sessionUniqueId,
|
||||||
.documentId = document ? document->id : DocumentId(),
|
.documentId = document ? document->id : DocumentId(),
|
||||||
.itemId = id.itemId,
|
.itemId = id.itemId,
|
||||||
|
@ -347,14 +353,54 @@ void DownloadManager::deleteFiles(const std::vector<GlobalMsgId> &ids) {
|
||||||
data.downloaded.erase(k);
|
data.downloaded.erase(k);
|
||||||
_loadedRemoved.fire_copy(item);
|
_loadedRemoved.fire_copy(item);
|
||||||
|
|
||||||
sessions.emplace(session);
|
descriptor.sessions.emplace(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto &session : sessions) {
|
finishFilesDelete(std::move(descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::deleteAll() {
|
||||||
|
auto descriptor = DeleteFilesDescriptor();
|
||||||
|
for (auto &[session, data] : _sessions) {
|
||||||
|
if (!data.downloaded.empty()) {
|
||||||
|
descriptor.sessions.emplace(session);
|
||||||
|
} else if (data.downloading.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto sessionUniqueId = session->uniqueId();
|
||||||
|
while (!data.downloading.empty()) {
|
||||||
|
cancel(data, data.downloading.end() - 1);
|
||||||
|
}
|
||||||
|
for (auto &id : base::take(data.downloaded)) {
|
||||||
|
const auto object = id.object.get();
|
||||||
|
const auto document = object ? object->document : nullptr;
|
||||||
|
descriptor.files.emplace(id.path, DocumentDescriptor{
|
||||||
|
.sessionUniqueId = sessionUniqueId,
|
||||||
|
.documentId = document ? document->id : DocumentId(),
|
||||||
|
.itemId = id.itemId,
|
||||||
|
});
|
||||||
|
if (document) {
|
||||||
|
_generatedDocuments.remove(document);
|
||||||
|
}
|
||||||
|
if (const auto item = object ? object->item.get() : nullptr) {
|
||||||
|
_loaded.remove(item);
|
||||||
|
_generated.remove(item);
|
||||||
|
_loadedRemoved.fire_copy(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &session : descriptor.sessions) {
|
||||||
writePostponed(session);
|
writePostponed(session);
|
||||||
}
|
}
|
||||||
crl::async([files = std::move(files)] {
|
finishFilesDelete(std::move(descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::finishFilesDelete(DeleteFilesDescriptor &&descriptor) {
|
||||||
|
for (const auto &session : descriptor.sessions) {
|
||||||
|
writePostponed(session);
|
||||||
|
}
|
||||||
|
crl::async([files = std::move(descriptor.files)]{
|
||||||
for (const auto &file : files) {
|
for (const auto &file : files) {
|
||||||
QFile(file.first).remove();
|
QFile(file.first).remove();
|
||||||
crl::on_main([descriptor = file.second] {
|
crl::on_main([descriptor = file.second] {
|
||||||
|
@ -374,6 +420,19 @@ void DownloadManager::deleteFiles(const std::vector<GlobalMsgId> &ids) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DownloadManager::loadedHasNonCloudFile() const {
|
||||||
|
for (const auto &[session, data] : _sessions) {
|
||||||
|
for (const auto &id : data.downloaded) {
|
||||||
|
if (const auto object = id.object.get()) {
|
||||||
|
if (!object->item->isHistoryEntry()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto DownloadManager::loadingList() const
|
auto DownloadManager::loadingList() const
|
||||||
-> ranges::any_view<const DownloadingId*, ranges::category::input> {
|
-> ranges::any_view<const DownloadingId*, ranges::category::input> {
|
||||||
return ranges::views::all(
|
return ranges::views::all(
|
||||||
|
@ -515,9 +574,17 @@ auto DownloadManager::loadedList()
|
||||||
}) | ranges::views::join;
|
}) | ranges::views::join;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> DownloadManager::loadedResolveDone() const {
|
||||||
|
using namespace rpl::mappers;
|
||||||
|
return _loadedResolveDone.value() | rpl::filter(_1) | rpl::to_empty;
|
||||||
|
}
|
||||||
|
|
||||||
void DownloadManager::resolve(
|
void DownloadManager::resolve(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
SessionData &data) {
|
SessionData &data) {
|
||||||
|
const auto guard = gsl::finally([&] {
|
||||||
|
checkFullResolveDone();
|
||||||
|
});
|
||||||
if (data.resolveSentTotal >= data.resolveNeeded
|
if (data.resolveSentTotal >= data.resolveNeeded
|
||||||
|| data.resolveSentTotal >= kMaxResolvePerAttempt) {
|
|| data.resolveSentTotal >= kMaxResolvePerAttempt) {
|
||||||
return;
|
return;
|
||||||
|
@ -622,6 +689,19 @@ void DownloadManager::resolveRequestsFinished(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DownloadManager::checkFullResolveDone() {
|
||||||
|
if (_loadedResolveDone.current()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const auto &[session, data] : _sessions) {
|
||||||
|
if (data.resolveSentTotal < data.resolveNeeded
|
||||||
|
|| data.resolveSentRequests > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_loadedResolveDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
void DownloadManager::generateEntry(
|
void DownloadManager::generateEntry(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
DownloadedId &id) {
|
DownloadedId &id) {
|
||||||
|
|
|
@ -93,6 +93,8 @@ public:
|
||||||
|
|
||||||
void clearIfFinished();
|
void clearIfFinished();
|
||||||
void deleteFiles(const std::vector<GlobalMsgId> &ids);
|
void deleteFiles(const std::vector<GlobalMsgId> &ids);
|
||||||
|
void deleteAll();
|
||||||
|
[[nodiscard]] bool loadedHasNonCloudFile() const;
|
||||||
|
|
||||||
[[nodiscard]] auto loadingList() const
|
[[nodiscard]] auto loadingList() const
|
||||||
-> ranges::any_view<const DownloadingId*, ranges::category::input>;
|
-> ranges::any_view<const DownloadingId*, ranges::category::input>;
|
||||||
|
@ -113,8 +115,10 @@ public:
|
||||||
-> rpl::producer<not_null<const DownloadedId*>>;
|
-> rpl::producer<not_null<const DownloadedId*>>;
|
||||||
[[nodiscard]] auto loadedRemoved() const
|
[[nodiscard]] auto loadedRemoved() const
|
||||||
-> rpl::producer<not_null<const HistoryItem*>>;
|
-> rpl::producer<not_null<const HistoryItem*>>;
|
||||||
|
[[nodiscard]] rpl::producer<> loadedResolveDone() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct DeleteFilesDescriptor;
|
||||||
struct SessionData {
|
struct SessionData {
|
||||||
std::vector<DownloadedId> downloaded;
|
std::vector<DownloadedId> downloaded;
|
||||||
std::vector<DownloadingId> downloading;
|
std::vector<DownloadingId> downloading;
|
||||||
|
@ -152,6 +156,8 @@ private:
|
||||||
void resolveRequestsFinished(
|
void resolveRequestsFinished(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
SessionData &data);
|
SessionData &data);
|
||||||
|
void checkFullResolveDone();
|
||||||
|
|
||||||
[[nodiscard]] not_null<HistoryItem*> regenerateItem(
|
[[nodiscard]] not_null<HistoryItem*> regenerateItem(
|
||||||
const DownloadObject &previous);
|
const DownloadObject &previous);
|
||||||
[[nodiscard]] not_null<HistoryItem*> generateFakeItem(
|
[[nodiscard]] not_null<HistoryItem*> generateFakeItem(
|
||||||
|
@ -166,6 +172,7 @@ private:
|
||||||
Main::Session *onlyInSession) const;
|
Main::Session *onlyInSession) const;
|
||||||
void loadingStop(Main::Session *onlyInSession);
|
void loadingStop(Main::Session *onlyInSession);
|
||||||
|
|
||||||
|
void finishFilesDelete(DeleteFilesDescriptor &&descriptor);
|
||||||
void writePostponed(not_null<Main::Session*> session);
|
void writePostponed(not_null<Main::Session*> session);
|
||||||
[[nodiscard]] Fn<std::optional<QByteArray>()> serializator(
|
[[nodiscard]] Fn<std::optional<QByteArray>()> serializator(
|
||||||
not_null<Main::Session*> session) const;
|
not_null<Main::Session*> session) const;
|
||||||
|
@ -188,6 +195,7 @@ private:
|
||||||
|
|
||||||
rpl::event_stream<not_null<const DownloadedId*>> _loadedAdded;
|
rpl::event_stream<not_null<const DownloadedId*>> _loadedAdded;
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _loadedRemoved;
|
rpl::event_stream<not_null<const HistoryItem*>> _loadedRemoved;
|
||||||
|
rpl::variable<bool> _loadedResolveDone;
|
||||||
|
|
||||||
base::Timer _clearLoadingTimer;
|
base::Timer _clearLoadingTimer;
|
||||||
|
|
||||||
|
|
|
@ -85,9 +85,10 @@ void Provider::checkPreload(
|
||||||
}
|
}
|
||||||
|
|
||||||
void Provider::refreshViewer() {
|
void Provider::refreshViewer() {
|
||||||
if (_fullCount) {
|
if (_started) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_started = true;
|
||||||
auto &manager = Core::App().downloadManager();
|
auto &manager = Core::App().downloadManager();
|
||||||
rpl::single(rpl::empty) | rpl::then(
|
rpl::single(rpl::empty) | rpl::then(
|
||||||
manager.loadingListChanges() | rpl::to_empty
|
manager.loadingListChanges() | rpl::to_empty
|
||||||
|
@ -138,6 +139,13 @@ void Provider::refreshViewer() {
|
||||||
}
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
|
manager.loadedResolveDone(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
if (!_fullCount.has_value()) {
|
||||||
|
_fullCount = 0;
|
||||||
|
}
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
performAdd();
|
performAdd();
|
||||||
performRefresh();
|
performRefresh();
|
||||||
}
|
}
|
||||||
|
@ -211,7 +219,9 @@ void Provider::performRefresh() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_postponedRefresh = false;
|
_postponedRefresh = false;
|
||||||
_fullCount = _elements.size();
|
if (!_elements.empty() || _fullCount.has_value()) {
|
||||||
|
_fullCount = _elements.size();
|
||||||
|
}
|
||||||
if (base::take(_postponedRefreshSort)) {
|
if (base::take(_postponedRefreshSort)) {
|
||||||
ranges::sort(_elements, ranges::less(), &Element::started);
|
ranges::sort(_elements, ranges::less(), &Element::started);
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,7 @@ private:
|
||||||
base::flat_map<not_null<Main::Session*>, rpl::lifetime> _trackedSessions;
|
base::flat_map<not_null<Main::Session*>, rpl::lifetime> _trackedSessions;
|
||||||
bool _postponedRefreshSort = false;
|
bool _postponedRefreshSort = false;
|
||||||
bool _postponedRefresh = false;
|
bool _postponedRefresh = false;
|
||||||
|
bool _started = false;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "mtproto/mtproto_config.h"
|
#include "mtproto/mtproto_config.h"
|
||||||
|
#include "data/data_download_manager.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
@ -397,6 +398,29 @@ void WrapWidget::createTopBar() {
|
||||||
&& (section.settingsType() == Section::SettingsType::Main
|
&& (section.settingsType() == Section::SettingsType::Main
|
||||||
|| section.settingsType() == Section::SettingsType::Chat)) {
|
|| section.settingsType() == Section::SettingsType::Chat)) {
|
||||||
addTopBarMenuButton();
|
addTopBarMenuButton();
|
||||||
|
} else if (section.type() == Section::Type::Downloads) {
|
||||||
|
auto &manager = Core::App().downloadManager();
|
||||||
|
rpl::merge(
|
||||||
|
rpl::single(false),
|
||||||
|
manager.loadingListChanges() | rpl::map_to(false),
|
||||||
|
manager.loadedAdded() | rpl::map_to(true),
|
||||||
|
manager.loadedRemoved() | rpl::map_to(false)
|
||||||
|
) | rpl::start_with_next([=, &manager](bool definitelyHas) {
|
||||||
|
const auto has = [&] {
|
||||||
|
for (const auto id : manager.loadingList()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (const auto id : manager.loadedList()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (!definitelyHas && !has()) {
|
||||||
|
_topBarMenuToggle = nullptr;
|
||||||
|
} else if (!_topBarMenuToggle) {
|
||||||
|
addTopBarMenuButton();
|
||||||
|
}
|
||||||
|
}, _topBar->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
_topBar->lower();
|
_topBar->lower();
|
||||||
|
@ -495,7 +519,12 @@ void WrapWidget::showTopBarMenu() {
|
||||||
const style::icon *icon) {
|
const style::icon *icon) {
|
||||||
return _topBarMenu->addAction(text, std::move(callback), icon);
|
return _topBarMenu->addAction(text, std::move(callback), icon);
|
||||||
};
|
};
|
||||||
if (const auto peer = key().peer()) {
|
if (key().isDownloads()) {
|
||||||
|
addAction(
|
||||||
|
tr::lng_context_delete_all_files(tr::now),
|
||||||
|
[=] { deleteAllDownloads(); },
|
||||||
|
&st::menuIconDelete);
|
||||||
|
} else if (const auto peer = key().peer()) {
|
||||||
Window::FillDialogsEntryMenu(
|
Window::FillDialogsEntryMenu(
|
||||||
_controller->parentController(),
|
_controller->parentController(),
|
||||||
Dialogs::EntryState{
|
Dialogs::EntryState{
|
||||||
|
@ -525,6 +554,24 @@ void WrapWidget::showTopBarMenu() {
|
||||||
_topBarMenu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
|
_topBarMenu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WrapWidget::deleteAllDownloads() {
|
||||||
|
auto &manager = Core::App().downloadManager();
|
||||||
|
const auto phrase = tr::lng_downloads_delete_sure_all(tr::now);
|
||||||
|
const auto added = manager.loadedHasNonCloudFile()
|
||||||
|
? QString()
|
||||||
|
: tr::lng_downloads_delete_in_cloud(tr::now);
|
||||||
|
const auto deleteSure = [=, &manager](Fn<void()> close) {
|
||||||
|
Ui::PostponeCall(this, close);
|
||||||
|
manager.deleteAll();
|
||||||
|
};
|
||||||
|
_controller->parentController()->show(Ui::MakeConfirmBox({
|
||||||
|
.text = phrase + (added.isEmpty() ? QString() : "\n\n" + added),
|
||||||
|
.confirmed = deleteSure,
|
||||||
|
.confirmText = tr::lng_box_delete(tr::now),
|
||||||
|
.confirmStyle = &st::attentionBoxButton,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
bool WrapWidget::requireTopBarSearch() const {
|
bool WrapWidget::requireTopBarSearch() const {
|
||||||
if (!_controller->searchFieldController()) {
|
if (!_controller->searchFieldController()) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -197,6 +197,7 @@ private:
|
||||||
void addTopBarMenuButton();
|
void addTopBarMenuButton();
|
||||||
void addProfileCallsButton();
|
void addProfileCallsButton();
|
||||||
void showTopBarMenu();
|
void showTopBarMenu();
|
||||||
|
void deleteAllDownloads();
|
||||||
|
|
||||||
rpl::variable<Wrap> _wrap;
|
rpl::variable<Wrap> _wrap;
|
||||||
std::unique_ptr<Controller> _controller;
|
std::unique_ptr<Controller> _controller;
|
||||||
|
|
Loading…
Add table
Reference in a new issue