mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 15:47:11 +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_section" = "View in downloads";
|
||||
"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#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.";
|
||||
|
|
|
@ -63,8 +63,19 @@ constexpr auto ByDocument = [](const auto &entry) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct DocumentDescriptor {
|
||||
uint64 sessionUniqueId = 0;
|
||||
DocumentId documentId = 0;
|
||||
FullMsgId itemId;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
struct DownloadManager::DeleteFilesDescriptor {
|
||||
base::flat_set<not_null<Main::Session*>> sessions;
|
||||
base::flat_map<QString, DocumentDescriptor> files;
|
||||
};
|
||||
|
||||
DownloadManager::DownloadManager()
|
||||
: _clearLoadingTimer([=] { clearLoading(); }) {
|
||||
}
|
||||
|
@ -118,7 +129,8 @@ void DownloadManager::itemVisibilitiesUpdated(
|
|||
return;
|
||||
}
|
||||
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) {
|
||||
id.hiddenByView = false;
|
||||
}
|
||||
|
@ -308,13 +320,7 @@ void DownloadManager::clearIfFinished() {
|
|||
}
|
||||
|
||||
void DownloadManager::deleteFiles(const std::vector<GlobalMsgId> &ids) {
|
||||
struct DocumentDescriptor {
|
||||
uint64 sessionUniqueId = 0;
|
||||
DocumentId documentId = 0;
|
||||
FullMsgId itemId;
|
||||
};
|
||||
auto sessions = base::flat_set<not_null<Main::Session*>>();
|
||||
auto files = base::flat_map<QString, DocumentDescriptor>();
|
||||
auto descriptor = DeleteFilesDescriptor();
|
||||
for (const auto &id : ids) {
|
||||
if (const auto item = MessageByGlobalId(id)) {
|
||||
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);
|
||||
if (k != end(data.downloaded)) {
|
||||
const auto document = k->object->document;
|
||||
files.emplace(k->path, DocumentDescriptor{
|
||||
descriptor.files.emplace(k->path, DocumentDescriptor{
|
||||
.sessionUniqueId = id.sessionUniqueId,
|
||||
.documentId = document ? document->id : DocumentId(),
|
||||
.itemId = id.itemId,
|
||||
|
@ -347,14 +353,54 @@ void DownloadManager::deleteFiles(const std::vector<GlobalMsgId> &ids) {
|
|||
data.downloaded.erase(k);
|
||||
_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);
|
||||
}
|
||||
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) {
|
||||
QFile(file.first).remove();
|
||||
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
|
||||
-> ranges::any_view<const DownloadingId*, ranges::category::input> {
|
||||
return ranges::views::all(
|
||||
|
@ -515,9 +574,17 @@ auto DownloadManager::loadedList()
|
|||
}) | ranges::views::join;
|
||||
}
|
||||
|
||||
rpl::producer<> DownloadManager::loadedResolveDone() const {
|
||||
using namespace rpl::mappers;
|
||||
return _loadedResolveDone.value() | rpl::filter(_1) | rpl::to_empty;
|
||||
}
|
||||
|
||||
void DownloadManager::resolve(
|
||||
not_null<Main::Session*> session,
|
||||
SessionData &data) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
checkFullResolveDone();
|
||||
});
|
||||
if (data.resolveSentTotal >= data.resolveNeeded
|
||||
|| data.resolveSentTotal >= kMaxResolvePerAttempt) {
|
||||
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(
|
||||
not_null<Main::Session*> session,
|
||||
DownloadedId &id) {
|
||||
|
|
|
@ -93,6 +93,8 @@ public:
|
|||
|
||||
void clearIfFinished();
|
||||
void deleteFiles(const std::vector<GlobalMsgId> &ids);
|
||||
void deleteAll();
|
||||
[[nodiscard]] bool loadedHasNonCloudFile() const;
|
||||
|
||||
[[nodiscard]] auto loadingList() const
|
||||
-> ranges::any_view<const DownloadingId*, ranges::category::input>;
|
||||
|
@ -113,8 +115,10 @@ public:
|
|||
-> rpl::producer<not_null<const DownloadedId*>>;
|
||||
[[nodiscard]] auto loadedRemoved() const
|
||||
-> rpl::producer<not_null<const HistoryItem*>>;
|
||||
[[nodiscard]] rpl::producer<> loadedResolveDone() const;
|
||||
|
||||
private:
|
||||
struct DeleteFilesDescriptor;
|
||||
struct SessionData {
|
||||
std::vector<DownloadedId> downloaded;
|
||||
std::vector<DownloadingId> downloading;
|
||||
|
@ -152,6 +156,8 @@ private:
|
|||
void resolveRequestsFinished(
|
||||
not_null<Main::Session*> session,
|
||||
SessionData &data);
|
||||
void checkFullResolveDone();
|
||||
|
||||
[[nodiscard]] not_null<HistoryItem*> regenerateItem(
|
||||
const DownloadObject &previous);
|
||||
[[nodiscard]] not_null<HistoryItem*> generateFakeItem(
|
||||
|
@ -166,6 +172,7 @@ private:
|
|||
Main::Session *onlyInSession) const;
|
||||
void loadingStop(Main::Session *onlyInSession);
|
||||
|
||||
void finishFilesDelete(DeleteFilesDescriptor &&descriptor);
|
||||
void writePostponed(not_null<Main::Session*> session);
|
||||
[[nodiscard]] Fn<std::optional<QByteArray>()> serializator(
|
||||
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 HistoryItem*>> _loadedRemoved;
|
||||
rpl::variable<bool> _loadedResolveDone;
|
||||
|
||||
base::Timer _clearLoadingTimer;
|
||||
|
||||
|
|
|
@ -85,9 +85,10 @@ void Provider::checkPreload(
|
|||
}
|
||||
|
||||
void Provider::refreshViewer() {
|
||||
if (_fullCount) {
|
||||
if (_started) {
|
||||
return;
|
||||
}
|
||||
_started = true;
|
||||
auto &manager = Core::App().downloadManager();
|
||||
rpl::single(rpl::empty) | rpl::then(
|
||||
manager.loadingListChanges() | rpl::to_empty
|
||||
|
@ -138,6 +139,13 @@ void Provider::refreshViewer() {
|
|||
}
|
||||
}, _lifetime);
|
||||
|
||||
manager.loadedResolveDone(
|
||||
) | rpl::start_with_next([=] {
|
||||
if (!_fullCount.has_value()) {
|
||||
_fullCount = 0;
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
performAdd();
|
||||
performRefresh();
|
||||
}
|
||||
|
@ -211,7 +219,9 @@ void Provider::performRefresh() {
|
|||
return;
|
||||
}
|
||||
_postponedRefresh = false;
|
||||
_fullCount = _elements.size();
|
||||
if (!_elements.empty() || _fullCount.has_value()) {
|
||||
_fullCount = _elements.size();
|
||||
}
|
||||
if (base::take(_postponedRefreshSort)) {
|
||||
ranges::sort(_elements, ranges::less(), &Element::started);
|
||||
}
|
||||
|
|
|
@ -130,6 +130,7 @@ private:
|
|||
base::flat_map<not_null<Main::Session*>, rpl::lifetime> _trackedSessions;
|
||||
bool _postponedRefreshSort = false;
|
||||
bool _postponedRefresh = false;
|
||||
bool _started = false;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/boxes/confirm_box.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "data/data_download_manager.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_user.h"
|
||||
|
@ -397,6 +398,29 @@ void WrapWidget::createTopBar() {
|
|||
&& (section.settingsType() == Section::SettingsType::Main
|
||||
|| section.settingsType() == Section::SettingsType::Chat)) {
|
||||
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();
|
||||
|
@ -495,7 +519,12 @@ void WrapWidget::showTopBarMenu() {
|
|||
const style::icon *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(
|
||||
_controller->parentController(),
|
||||
Dialogs::EntryState{
|
||||
|
@ -525,6 +554,24 @@ void WrapWidget::showTopBarMenu() {
|
|||
_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 {
|
||||
if (!_controller->searchFieldController()) {
|
||||
return false;
|
||||
|
|
|
@ -197,6 +197,7 @@ private:
|
|||
void addTopBarMenuButton();
|
||||
void addProfileCallsButton();
|
||||
void showTopBarMenu();
|
||||
void deleteAllDownloads();
|
||||
|
||||
rpl::variable<Wrap> _wrap;
|
||||
std::unique_ptr<Controller> _controller;
|
||||
|
|
Loading…
Add table
Reference in a new issue