mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 15:43:55 +02:00
parent
12db51fe75
commit
03a868a6e3
11 changed files with 150 additions and 22 deletions
|
@ -2474,6 +2474,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_export_state_chats_list" = "Processing chats...";
|
"lng_export_state_chats_list" = "Processing chats...";
|
||||||
"lng_export_state_chats" = "Chats";
|
"lng_export_state_chats" = "Chats";
|
||||||
"lng_export_state_ready_progress" = "{ready} / {total}";
|
"lng_export_state_ready_progress" = "{ready} / {total}";
|
||||||
|
"lng_export_skip_file" = "Skip this file";
|
||||||
"lng_export_progress" = "You can close this window now. Please don't quit Telegram until the data export is completed.";
|
"lng_export_progress" = "You can close this window now. Please don't quit Telegram until the data export is completed.";
|
||||||
"lng_export_stop" = "Stop";
|
"lng_export_stop" = "Stop";
|
||||||
"lng_export_sure_stop" = "Are you sure you want to stop exporting your data?\n\nIf you do, you'll need to start over.";
|
"lng_export_sure_stop" = "Are you sure you want to stop exporting your data?\n\nIf you do, you'll need to start over.";
|
||||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "mtproto/mtproto_response.h"
|
#include "mtproto/mtproto_response.h"
|
||||||
#include "base/value_ordering.h"
|
#include "base/value_ordering.h"
|
||||||
#include "base/bytes.h"
|
#include "base/bytes.h"
|
||||||
|
#include "base/openssl_help.h"
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
|
@ -182,6 +183,7 @@ struct ApiWrap::FileProcess {
|
||||||
Fn<bool(FileProgress)> progress;
|
Fn<bool(FileProgress)> progress;
|
||||||
FnMut<void(const QString &relativePath)> done;
|
FnMut<void(const QString &relativePath)> done;
|
||||||
|
|
||||||
|
uint64 randomId = 0;
|
||||||
Data::FileLocation location;
|
Data::FileLocation location;
|
||||||
Data::FileOrigin origin;
|
Data::FileOrigin origin;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
@ -192,6 +194,7 @@ struct ApiWrap::FileProcess {
|
||||||
QByteArray bytes;
|
QByteArray bytes;
|
||||||
};
|
};
|
||||||
std::deque<Request> requests;
|
std::deque<Request> requests;
|
||||||
|
mtpRequestId requestId = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ApiWrap::FileProgress {
|
struct ApiWrap::FileProgress {
|
||||||
|
@ -383,6 +386,7 @@ auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) {
|
||||||
Expects(location.dcId != 0
|
Expects(location.dcId != 0
|
||||||
|| location.data.type() == mtpc_inputTakeoutFileLocation);
|
|| location.data.type() == mtpc_inputTakeoutFileLocation);
|
||||||
Expects(_takeoutId.has_value());
|
Expects(_takeoutId.has_value());
|
||||||
|
Expects(_fileProcess->requestId == 0);
|
||||||
|
|
||||||
return std::move(_mtp.request(MTPInvokeWithTakeout<MTPupload_GetFile>(
|
return std::move(_mtp.request(MTPInvokeWithTakeout<MTPupload_GetFile>(
|
||||||
MTP_long(*_takeoutId),
|
MTP_long(*_takeoutId),
|
||||||
|
@ -392,6 +396,7 @@ auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) {
|
||||||
MTP_int(offset),
|
MTP_int(offset),
|
||||||
MTP_int(kFileChunkSize))
|
MTP_int(kFileChunkSize))
|
||||||
)).fail([=](const MTP::Error &result) {
|
)).fail([=](const MTP::Error &result) {
|
||||||
|
_fileProcess->requestId = 0;
|
||||||
if (result.type() == qstr("TAKEOUT_FILE_EMPTY")
|
if (result.type() == qstr("TAKEOUT_FILE_EMPTY")
|
||||||
&& _otherDataProcess != nullptr) {
|
&& _otherDataProcess != nullptr) {
|
||||||
filePartDone(
|
filePartDone(
|
||||||
|
@ -853,6 +858,7 @@ bool ApiWrap::loadUserpicProgress(FileProgress progress) {
|
||||||
< _userpicsProcess->slice->list.size()));
|
< _userpicsProcess->slice->list.size()));
|
||||||
|
|
||||||
return _userpicsProcess->fileProgress(DownloadProgress{
|
return _userpicsProcess->fileProgress(DownloadProgress{
|
||||||
|
_fileProcess->randomId,
|
||||||
_fileProcess->relativePath,
|
_fileProcess->relativePath,
|
||||||
_userpicsProcess->fileIndex,
|
_userpicsProcess->fileIndex,
|
||||||
progress.ready,
|
progress.ready,
|
||||||
|
@ -1061,6 +1067,17 @@ void ApiWrap::finishExport(FnMut<void()> done) {
|
||||||
)).done(std::move(done)).send();
|
)).done(std::move(done)).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::skipFile(uint64 randomId) {
|
||||||
|
if (!_fileProcess || _fileProcess->randomId != randomId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG(("Export Info: File skipped."));
|
||||||
|
Assert(!_fileProcess->requests.empty());
|
||||||
|
Assert(_fileProcess->requestId != 0);
|
||||||
|
_mtp.request(base::take(_fileProcess->requestId)).cancel();
|
||||||
|
base::take(_fileProcess)->done(QString());
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::cancelExportFast() {
|
void ApiWrap::cancelExportFast() {
|
||||||
if (_takeoutId.has_value()) {
|
if (_takeoutId.has_value()) {
|
||||||
const auto requestId = mainRequest(MTPaccount_FinishTakeoutSession(
|
const auto requestId = mainRequest(MTPaccount_FinishTakeoutSession(
|
||||||
|
@ -1591,10 +1608,11 @@ bool ApiWrap::loadMessageFileProgress(FileProgress progress) {
|
||||||
&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
|
&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
|
||||||
|
|
||||||
return _chatProcess->fileProgress(DownloadProgress{
|
return _chatProcess->fileProgress(DownloadProgress{
|
||||||
_fileProcess->relativePath,
|
.randomId = _fileProcess->randomId,
|
||||||
_chatProcess->fileIndex,
|
.path = _fileProcess->relativePath,
|
||||||
progress.ready,
|
.itemIndex = _chatProcess->fileIndex,
|
||||||
progress.total });
|
.ready = progress.ready,
|
||||||
|
.total = progress.total });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::loadMessageFileDone(const QString &relativePath) {
|
void ApiWrap::loadMessageFileDone(const QString &relativePath) {
|
||||||
|
@ -1740,6 +1758,8 @@ void ApiWrap::loadFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFilePart();
|
loadFilePart();
|
||||||
|
|
||||||
|
Ensures(_fileProcess->requestId != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ApiWrap::prepareFileProcess(
|
auto ApiWrap::prepareFileProcess(
|
||||||
|
@ -1758,11 +1778,13 @@ auto ApiWrap::prepareFileProcess(
|
||||||
result->location = file.location;
|
result->location = file.location;
|
||||||
result->size = file.size;
|
result->size = file.size;
|
||||||
result->origin = origin;
|
result->origin = origin;
|
||||||
|
result->randomId = openssl::RandomValue<uint64>();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::loadFilePart() {
|
void ApiWrap::loadFilePart() {
|
||||||
if (!_fileProcess
|
if (!_fileProcess
|
||||||
|
|| _fileProcess->requestId
|
||||||
|| _fileProcess->requests.size() >= kFileRequestsCount
|
|| _fileProcess->requests.size() >= kFileRequestsCount
|
||||||
|| (_fileProcess->size > 0
|
|| (_fileProcess->size > 0
|
||||||
&& _fileProcess->offset >= _fileProcess->size)) {
|
&& _fileProcess->offset >= _fileProcess->size)) {
|
||||||
|
@ -1771,16 +1793,18 @@ void ApiWrap::loadFilePart() {
|
||||||
|
|
||||||
const auto offset = _fileProcess->offset;
|
const auto offset = _fileProcess->offset;
|
||||||
_fileProcess->requests.push_back({ offset });
|
_fileProcess->requests.push_back({ offset });
|
||||||
fileRequest(
|
_fileProcess->requestId = fileRequest(
|
||||||
_fileProcess->location,
|
_fileProcess->location,
|
||||||
_fileProcess->offset
|
_fileProcess->offset
|
||||||
).done([=](const MTPupload_File &result) {
|
).done([=](const MTPupload_File &result) {
|
||||||
|
_fileProcess->requestId = 0;
|
||||||
filePartDone(offset, result);
|
filePartDone(offset, result);
|
||||||
}).send();
|
}).send();
|
||||||
_fileProcess->offset += kFileChunkSize;
|
_fileProcess->offset += kFileChunkSize;
|
||||||
|
|
||||||
if (_fileProcess->size > 0
|
if (_fileProcess->size > 0
|
||||||
&& _fileProcess->requests.size() < kFileRequestsCount) {
|
&& _fileProcess->requests.size() < kFileRequestsCount) {
|
||||||
|
// Only one request at a time supported right now.
|
||||||
//const auto runner = _runner;
|
//const auto runner = _runner;
|
||||||
//crl::on_main([=] {
|
//crl::on_main([=] {
|
||||||
// QTimer::singleShot(kFileNextRequestDelay, [=] {
|
// QTimer::singleShot(kFileNextRequestDelay, [=] {
|
||||||
|
@ -1854,6 +1878,7 @@ void ApiWrap::filePartDone(int offset, const MTPupload_File &result) {
|
||||||
|
|
||||||
void ApiWrap::filePartRefreshReference(int offset) {
|
void ApiWrap::filePartRefreshReference(int offset) {
|
||||||
Expects(_fileProcess != nullptr);
|
Expects(_fileProcess != nullptr);
|
||||||
|
Expects(_fileProcess->requestId == 0);
|
||||||
|
|
||||||
const auto &origin = _fileProcess->origin;
|
const auto &origin = _fileProcess->origin;
|
||||||
if (!origin.messageId) {
|
if (!origin.messageId) {
|
||||||
|
@ -1870,26 +1895,33 @@ void ApiWrap::filePartRefreshReference(int offset) {
|
||||||
origin.peer.c_inputPeerChannelFromMessage().vpeer(),
|
origin.peer.c_inputPeerChannelFromMessage().vpeer(),
|
||||||
origin.peer.c_inputPeerChannelFromMessage().vmsg_id(),
|
origin.peer.c_inputPeerChannelFromMessage().vmsg_id(),
|
||||||
origin.peer.c_inputPeerChannelFromMessage().vchannel_id());
|
origin.peer.c_inputPeerChannelFromMessage().vchannel_id());
|
||||||
mainRequest(MTPchannels_GetMessages(
|
_fileProcess->requestId = mainRequest(MTPchannels_GetMessages(
|
||||||
channel,
|
channel,
|
||||||
MTP_vector<MTPInputMessage>(
|
MTP_vector<MTPInputMessage>(
|
||||||
1,
|
1,
|
||||||
MTP_inputMessageID(MTP_int(origin.messageId)))
|
MTP_inputMessageID(MTP_int(origin.messageId)))
|
||||||
)).fail([=](const MTP::Error &error) {
|
)).fail([=](const MTP::Error &error) {
|
||||||
|
_fileProcess->requestId = 0;
|
||||||
filePartUnavailable();
|
filePartUnavailable();
|
||||||
return true;
|
return true;
|
||||||
}).done([=](const MTPmessages_Messages &result) {
|
}).done([=](const MTPmessages_Messages &result) {
|
||||||
|
_fileProcess->requestId = 0;
|
||||||
filePartExtractReference(offset, result);
|
filePartExtractReference(offset, result);
|
||||||
}).send();
|
}).send();
|
||||||
} else {
|
} else {
|
||||||
splitRequest(origin.split, MTPmessages_GetMessages(
|
_fileProcess->requestId = splitRequest(
|
||||||
MTP_vector<MTPInputMessage>(
|
origin.split,
|
||||||
1,
|
MTPmessages_GetMessages(
|
||||||
MTP_inputMessageID(MTP_int(origin.messageId)))
|
MTP_vector<MTPInputMessage>(
|
||||||
)).fail([=](const MTP::Error &error) {
|
1,
|
||||||
|
MTP_inputMessageID(MTP_int(origin.messageId)))
|
||||||
|
)
|
||||||
|
).fail([=](const MTP::Error &error) {
|
||||||
|
_fileProcess->requestId = 0;
|
||||||
filePartUnavailable();
|
filePartUnavailable();
|
||||||
return true;
|
return true;
|
||||||
}).done([=](const MTPmessages_Messages &result) {
|
}).done([=](const MTPmessages_Messages &result) {
|
||||||
|
_fileProcess->requestId = 0;
|
||||||
filePartExtractReference(offset, result);
|
filePartExtractReference(offset, result);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
@ -1899,6 +1931,7 @@ void ApiWrap::filePartExtractReference(
|
||||||
int offset,
|
int offset,
|
||||||
const MTPmessages_Messages &result) {
|
const MTPmessages_Messages &result) {
|
||||||
Expects(_fileProcess != nullptr);
|
Expects(_fileProcess != nullptr);
|
||||||
|
Expects(_fileProcess->requestId == 0);
|
||||||
|
|
||||||
result.match([&](const MTPDmessages_messagesNotModified &data) {
|
result.match([&](const MTPDmessages_messagesNotModified &data) {
|
||||||
error("Unexpected messagesNotModified received.");
|
error("Unexpected messagesNotModified received.");
|
||||||
|
@ -1922,10 +1955,11 @@ void ApiWrap::filePartExtractReference(
|
||||||
_fileProcess->location,
|
_fileProcess->location,
|
||||||
message.thumb().file.location);
|
message.thumb().file.location);
|
||||||
if (refresh1 || refresh2) {
|
if (refresh1 || refresh2) {
|
||||||
fileRequest(
|
_fileProcess->requestId = fileRequest(
|
||||||
_fileProcess->location,
|
_fileProcess->location,
|
||||||
offset
|
offset
|
||||||
).done([=](const MTPupload_File &result) {
|
).done([=](const MTPupload_File &result) {
|
||||||
|
_fileProcess->requestId = 0;
|
||||||
filePartDone(offset, result);
|
filePartDone(offset, result);
|
||||||
}).send();
|
}).send();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -60,6 +60,7 @@ public:
|
||||||
FnMut<void(Data::File&&)> done);
|
FnMut<void(Data::File&&)> done);
|
||||||
|
|
||||||
struct DownloadProgress {
|
struct DownloadProgress {
|
||||||
|
uint64 randomId = 0;
|
||||||
QString path;
|
QString path;
|
||||||
int itemIndex = 0;
|
int itemIndex = 0;
|
||||||
int ready = 0;
|
int ready = 0;
|
||||||
|
@ -83,6 +84,7 @@ public:
|
||||||
FnMut<void()> done);
|
FnMut<void()> done);
|
||||||
|
|
||||||
void finishExport(FnMut<void()> done);
|
void finishExport(FnMut<void()> done);
|
||||||
|
void skipFile(uint64 randomId);
|
||||||
void cancelExportFast();
|
void cancelExportFast();
|
||||||
|
|
||||||
~ApiWrap();
|
~ApiWrap();
|
||||||
|
|
|
@ -51,12 +51,14 @@ public:
|
||||||
void startExport(
|
void startExport(
|
||||||
const Settings &settings,
|
const Settings &settings,
|
||||||
const Environment &environment);
|
const Environment &environment);
|
||||||
|
void skipFile(uint64 randomId);
|
||||||
void cancelExportFast();
|
void cancelExportFast();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Step = ProcessingState::Step;
|
using Step = ProcessingState::Step;
|
||||||
using DownloadProgress = ApiWrap::DownloadProgress;
|
using DownloadProgress = ApiWrap::DownloadProgress;
|
||||||
|
|
||||||
|
[[nodiscard]] bool stopped() const;
|
||||||
void setState(State &&state);
|
void setState(State &&state);
|
||||||
void ioError(const QString &path);
|
void ioError(const QString &path);
|
||||||
bool ioCatchError(Output::Result result);
|
bool ioCatchError(Output::Result result);
|
||||||
|
@ -166,8 +168,15 @@ rpl::producer<State> ControllerObject::state() const {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ControllerObject::stopped() const {
|
||||||
|
return v::is<CancelledState>(_state)
|
||||||
|
|| v::is<ApiErrorState>(_state)
|
||||||
|
|| v::is<OutputErrorState>(_state)
|
||||||
|
|| v::is<FinishedState>(_state);
|
||||||
|
}
|
||||||
|
|
||||||
void ControllerObject::setState(State &&state) {
|
void ControllerObject::setState(State &&state) {
|
||||||
if (v::is<CancelledState>(_state)) {
|
if (stopped()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_state = std::move(state);
|
_state = std::move(state);
|
||||||
|
@ -245,6 +254,13 @@ void ControllerObject::startExport(
|
||||||
exportNext();
|
exportNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControllerObject::skipFile(uint64 randomId) {
|
||||||
|
if (stopped()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_api.skipFile(randomId);
|
||||||
|
}
|
||||||
|
|
||||||
void ControllerObject::fillExportSteps() {
|
void ControllerObject::fillExportSteps() {
|
||||||
using Type = Settings::Type;
|
using Type = Settings::Type;
|
||||||
_steps.push_back(Step::Initializing);
|
_steps.push_back(Step::Initializing);
|
||||||
|
@ -518,6 +534,7 @@ ProcessingState ControllerObject::stateUserpics(
|
||||||
result.entityIndex = _userpicsWritten + progress.itemIndex;
|
result.entityIndex = _userpicsWritten + progress.itemIndex;
|
||||||
result.entityCount = std::max(_userpicsCount, result.entityIndex);
|
result.entityCount = std::max(_userpicsCount, result.entityIndex);
|
||||||
result.bytesType = ProcessingState::FileType::Photo;
|
result.bytesType = ProcessingState::FileType::Photo;
|
||||||
|
result.bytesRandomId = progress.randomId;
|
||||||
if (!progress.path.isEmpty()) {
|
if (!progress.path.isEmpty()) {
|
||||||
const auto last = progress.path.lastIndexOf('/');
|
const auto last = progress.path.lastIndexOf('/');
|
||||||
result.bytesName = progress.path.mid(last + 1);
|
result.bytesName = progress.path.mid(last + 1);
|
||||||
|
@ -570,6 +587,7 @@ void ControllerObject::fillMessagesState(
|
||||||
result.itemIndex = _messagesWritten + progress.itemIndex;
|
result.itemIndex = _messagesWritten + progress.itemIndex;
|
||||||
result.itemCount = std::max(_messagesCount, result.itemIndex);
|
result.itemCount = std::max(_messagesCount, result.itemIndex);
|
||||||
result.bytesType = ProcessingState::FileType::File; // TODO
|
result.bytesType = ProcessingState::FileType::File; // TODO
|
||||||
|
result.bytesRandomId = progress.randomId;
|
||||||
if (!progress.path.isEmpty()) {
|
if (!progress.path.isEmpty()) {
|
||||||
const auto last = progress.path.lastIndexOf('/');
|
const auto last = progress.path.lastIndexOf('/');
|
||||||
result.bytesName = progress.path.mid(last + 1);
|
result.bytesName = progress.path.mid(last + 1);
|
||||||
|
@ -643,6 +661,12 @@ void Controller::startExport(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::skipFile(uint64 randomId) {
|
||||||
|
_wrapped.with([=](Implementation &unwrapped) {
|
||||||
|
unwrapped.skipFile(randomId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::cancelExportFast() {
|
void Controller::cancelExportFast() {
|
||||||
LOG(("Export Info: Cancelled export."));
|
LOG(("Export Info: Cancelled export."));
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@ struct ProcessingState {
|
||||||
int itemIndex = 0;
|
int itemIndex = 0;
|
||||||
int itemCount = 0;
|
int itemCount = 0;
|
||||||
|
|
||||||
|
uint64 bytesRandomId = 0;
|
||||||
FileType bytesType = FileType::None;
|
FileType bytesType = FileType::None;
|
||||||
QString bytesName;
|
QString bytesName;
|
||||||
int bytesLoaded = 0;
|
int bytesLoaded = 0;
|
||||||
|
@ -136,6 +137,7 @@ public:
|
||||||
void startExport(
|
void startExport(
|
||||||
const Settings &settings,
|
const Settings &settings,
|
||||||
const Environment &environment);
|
const Environment &environment);
|
||||||
|
void skipFile(uint64 randomId);
|
||||||
void cancelExportFast();
|
void cancelExportFast();
|
||||||
|
|
||||||
rpl::lifetime &lifetime();
|
rpl::lifetime &lifetime();
|
||||||
|
|
|
@ -50,7 +50,8 @@ exportErrorLabel: FlatLabel(boxLabel) {
|
||||||
|
|
||||||
exportProgressDuration: 200;
|
exportProgressDuration: 200;
|
||||||
exportProgressRowHeight: 30px;
|
exportProgressRowHeight: 30px;
|
||||||
exportProgressRowPadding: margins(22px, 10px, 22px, 20px);
|
exportProgressRowPadding: margins(22px, 10px, 22px, 10px);
|
||||||
|
exportProgressRowSkip: 10px;
|
||||||
exportProgressLabel: FlatLabel(boxLabel) {
|
exportProgressLabel: FlatLabel(boxLabel) {
|
||||||
textFg: windowBoldFg;
|
textFg: windowBoldFg;
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
|
|
|
@ -26,8 +26,9 @@ Content ContentFromState(
|
||||||
const QString &id,
|
const QString &id,
|
||||||
const QString &label,
|
const QString &label,
|
||||||
const QString &info,
|
const QString &info,
|
||||||
float64 progress) {
|
float64 progress,
|
||||||
result.rows.push_back({ id, label, info, progress });
|
uint64 randomId = 0) {
|
||||||
|
result.rows.push_back({ id, label, info, progress, randomId });
|
||||||
};
|
};
|
||||||
const auto pushMain = [&](const QString &label) {
|
const auto pushMain = [&](const QString &label) {
|
||||||
const auto info = (state.entityCount > 0)
|
const auto info = (state.entityCount > 0)
|
||||||
|
@ -56,7 +57,10 @@ Content ContentFromState(
|
||||||
: addPart(state.entityIndex, state.entityCount);
|
: addPart(state.entityIndex, state.entityCount);
|
||||||
push("main", label, info, doneProgress + addProgress);
|
push("main", label, info, doneProgress + addProgress);
|
||||||
};
|
};
|
||||||
const auto pushBytes = [&](const QString &id, const QString &label) {
|
const auto pushBytes = [&](
|
||||||
|
const QString &id,
|
||||||
|
const QString &label,
|
||||||
|
uint64 randomId) {
|
||||||
if (!state.bytesCount) {
|
if (!state.bytesCount) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -64,7 +68,7 @@ Content ContentFromState(
|
||||||
const auto info = Ui::FormatDownloadText(
|
const auto info = Ui::FormatDownloadText(
|
||||||
state.bytesLoaded,
|
state.bytesLoaded,
|
||||||
state.bytesCount);
|
state.bytesCount);
|
||||||
push(id, label, info, progress);
|
push(id, label, info, progress, randomId);
|
||||||
};
|
};
|
||||||
switch (state.step) {
|
switch (state.step) {
|
||||||
case Step::Initializing:
|
case Step::Initializing:
|
||||||
|
@ -80,7 +84,8 @@ Content ContentFromState(
|
||||||
pushMain(tr::lng_export_state_userpics(tr::now));
|
pushMain(tr::lng_export_state_userpics(tr::now));
|
||||||
pushBytes(
|
pushBytes(
|
||||||
"userpic" + QString::number(state.entityIndex),
|
"userpic" + QString::number(state.entityIndex),
|
||||||
state.bytesName);
|
state.bytesName,
|
||||||
|
state.bytesRandomId);
|
||||||
break;
|
break;
|
||||||
case Step::Contacts:
|
case Step::Contacts:
|
||||||
pushMain(tr::lng_export_option_contacts(tr::now));
|
pushMain(tr::lng_export_option_contacts(tr::now));
|
||||||
|
@ -117,7 +122,8 @@ Content ContentFromState(
|
||||||
+ QString::number(state.entityIndex)
|
+ QString::number(state.entityIndex)
|
||||||
+ '_'
|
+ '_'
|
||||||
+ QString::number(state.itemIndex)),
|
+ QString::number(state.itemIndex)),
|
||||||
state.bytesName);
|
state.bytesName,
|
||||||
|
state.bytesRandomId);
|
||||||
break;
|
break;
|
||||||
default: Unexpected("Step in ContentFromState.");
|
default: Unexpected("Step in ContentFromState.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ struct Content {
|
||||||
QString label;
|
QString label;
|
||||||
QString info;
|
QString info;
|
||||||
float64 progress = 0.;
|
float64 progress = 0.;
|
||||||
|
uint64 randomId = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Row> rows;
|
std::vector<Row> rows;
|
||||||
|
|
|
@ -299,6 +299,11 @@ void PanelController::showProgress() {
|
||||||
ContentFromState(_settings.get(), ProcessingState())
|
ContentFromState(_settings.get(), ProcessingState())
|
||||||
) | rpl::then(progressState()));
|
) | rpl::then(progressState()));
|
||||||
|
|
||||||
|
progress->skipFileClicks(
|
||||||
|
) | rpl::start_with_next([=](uint64 randomId) {
|
||||||
|
_process->skipFile(randomId);
|
||||||
|
}, progress->lifetime());
|
||||||
|
|
||||||
progress->cancelClicks(
|
progress->cancelClicks(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
stopWithConfirmation();
|
stopWithConfirmation();
|
||||||
|
|
|
@ -18,6 +18,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
namespace View {
|
namespace View {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kShowSkipFileTimeout = 5 * crl::time(1000);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
class ProgressWidget::Row : public Ui::RpWidget {
|
class ProgressWidget::Row : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
|
@ -235,13 +240,26 @@ ProgressWidget::ProgressWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
rpl::producer<Content> content)
|
rpl::producer<Content> content)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _body(this) {
|
, _body(this)
|
||||||
|
, _fileShowSkipTimer([=] { _skipFile->show(anim::type::normal); }) {
|
||||||
widthValue(
|
widthValue(
|
||||||
) | rpl::start_with_next([=](int width) {
|
) | rpl::start_with_next([=](int width) {
|
||||||
_body->resizeToWidth(width);
|
_body->resizeToWidth(width);
|
||||||
_body->moveToLeft(0, 0);
|
_body->moveToLeft(0, 0);
|
||||||
}, _body->lifetime());
|
}, _body->lifetime());
|
||||||
|
|
||||||
|
auto skipFileWrap = _body->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
_body.data(),
|
||||||
|
st::defaultLinkButton.font->height + st::exportProgressRowSkip));
|
||||||
|
_skipFile = base::make_unique_q<Ui::FadeWrap<Ui::LinkButton>>(
|
||||||
|
skipFileWrap,
|
||||||
|
object_ptr<Ui::LinkButton>(
|
||||||
|
this,
|
||||||
|
tr::lng_export_skip_file(tr::now),
|
||||||
|
st::defaultLinkButton));
|
||||||
|
_skipFile->hide(anim::type::instant);
|
||||||
|
_skipFile->moveToLeft(st::exportProgressRowPadding.left(), 0);
|
||||||
|
|
||||||
_about = _body->add(
|
_about = _body->add(
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
this,
|
this,
|
||||||
|
@ -262,6 +280,11 @@ ProgressWidget::ProgressWidget(
|
||||||
setupBottomButton(_cancel.get());
|
setupBottomButton(_cancel.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<uint64> ProgressWidget::skipFileClicks() const {
|
||||||
|
return _skipFile->entity()->clicks(
|
||||||
|
) | rpl::map([=] { return _fileRandomId; });
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<> ProgressWidget::cancelClicks() const {
|
rpl::producer<> ProgressWidget::cancelClicks() const {
|
||||||
return _cancel
|
return _cancel
|
||||||
? (_cancel->clicks() | rpl::to_empty)
|
? (_cancel->clicks() | rpl::to_empty)
|
||||||
|
@ -294,14 +317,32 @@ void ProgressWidget::updateState(Content &&content) {
|
||||||
if (index < _rows.size()) {
|
if (index < _rows.size()) {
|
||||||
_rows[index]->updateData(std::move(row));
|
_rows[index]->updateData(std::move(row));
|
||||||
} else {
|
} else {
|
||||||
|
if (index > 0) {
|
||||||
|
_body->insert(
|
||||||
|
index * 2 - 1,
|
||||||
|
object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
this,
|
||||||
|
st::exportProgressRowSkip));
|
||||||
|
}
|
||||||
_rows.push_back(_body->insert(
|
_rows.push_back(_body->insert(
|
||||||
index,
|
index * 2,
|
||||||
object_ptr<Row>(this, std::move(row)),
|
object_ptr<Row>(this, std::move(row)),
|
||||||
st::exportProgressRowPadding));
|
st::exportProgressRowPadding));
|
||||||
_rows.back()->show();
|
_rows.back()->show();
|
||||||
}
|
}
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
const auto fileRandomId = !content.rows.empty()
|
||||||
|
? content.rows.back().randomId
|
||||||
|
: uint64(0);
|
||||||
|
if (_fileRandomId != fileRandomId) {
|
||||||
|
_fileShowSkipTimer.cancel();
|
||||||
|
_skipFile->hide(anim::type::normal);
|
||||||
|
_fileRandomId = fileRandomId;
|
||||||
|
if (_fileRandomId) {
|
||||||
|
_fileShowSkipTimer.callOnce(kShowSkipFileTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const auto count = _rows.size(); index != count; ++index) {
|
for (const auto count = _rows.size(); index != count; ++index) {
|
||||||
_rows[index]->updateData(Content::Row());
|
_rows[index]->updateData(Content::Row());
|
||||||
}
|
}
|
||||||
|
@ -312,6 +353,8 @@ void ProgressWidget::updateState(Content &&content) {
|
||||||
|
|
||||||
void ProgressWidget::showDone() {
|
void ProgressWidget::showDone() {
|
||||||
_cancel = nullptr;
|
_cancel = nullptr;
|
||||||
|
_skipFile->hide(anim::type::instant);
|
||||||
|
_fileShowSkipTimer.cancel();
|
||||||
_about->setText(tr::lng_export_about_done(tr::now));
|
_about->setText(tr::lng_export_about_done(tr::now));
|
||||||
_done = base::make_unique_q<Ui::RoundButton>(
|
_done = base::make_unique_q<Ui::RoundButton>(
|
||||||
this,
|
this,
|
||||||
|
|
|
@ -10,11 +10,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "export/view/export_view_content.h"
|
#include "export/view/export_view_content.h"
|
||||||
#include "base/object_ptr.h"
|
#include "base/object_ptr.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class VerticalLayout;
|
class VerticalLayout;
|
||||||
class RoundButton;
|
class RoundButton;
|
||||||
class FlatLabel;
|
class FlatLabel;
|
||||||
|
class LinkButton;
|
||||||
|
template <typename Widget>
|
||||||
|
class FadeWrap;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
|
@ -26,6 +30,7 @@ public:
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
rpl::producer<Content> content);
|
rpl::producer<Content> content);
|
||||||
|
|
||||||
|
rpl::producer<uint64> skipFileClicks() const;
|
||||||
rpl::producer<> cancelClicks() const;
|
rpl::producer<> cancelClicks() const;
|
||||||
rpl::producer<> doneClicks() const;
|
rpl::producer<> doneClicks() const;
|
||||||
|
|
||||||
|
@ -42,11 +47,15 @@ private:
|
||||||
object_ptr<Ui::VerticalLayout> _body;
|
object_ptr<Ui::VerticalLayout> _body;
|
||||||
std::vector<not_null<Row*>> _rows;
|
std::vector<not_null<Row*>> _rows;
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::FadeWrap<Ui::LinkButton>> _skipFile;
|
||||||
QPointer<Ui::FlatLabel> _about;
|
QPointer<Ui::FlatLabel> _about;
|
||||||
base::unique_qptr<Ui::RoundButton> _cancel;
|
base::unique_qptr<Ui::RoundButton> _cancel;
|
||||||
base::unique_qptr<Ui::RoundButton> _done;
|
base::unique_qptr<Ui::RoundButton> _done;
|
||||||
rpl::event_stream<> _doneClicks;
|
rpl::event_stream<> _doneClicks;
|
||||||
|
|
||||||
|
uint64 _fileRandomId = 0;
|
||||||
|
base::Timer _fileShowSkipTimer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace View
|
} // namespace View
|
||||||
|
|
Loading…
Add table
Reference in a new issue