From 54cab2c5a502c9ea3171cfe029c2bcba5628e995 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 24 Jun 2018 01:33:47 +0100 Subject: [PATCH] Add other additional data export. --- Telegram/Resources/langs/lang.strings | 10 ++- Telegram/Resources/scheme.tl | 1 + Telegram/SourceFiles/data/data_session.cpp | 4 + .../SourceFiles/export/export_api_wrap.cpp | 54 ++++++++++++- Telegram/SourceFiles/export/export_api_wrap.h | 8 ++ .../SourceFiles/export/export_controller.cpp | 30 +++++++- .../SourceFiles/export/export_controller.h | 1 + .../SourceFiles/export/export_settings.cpp | 2 +- Telegram/SourceFiles/export/export_settings.h | 15 ++-- .../export/output/export_output_abstract.h | 4 + .../export/output/export_output_html.cpp | 21 +++++- .../export/output/export_output_html.h | 2 + .../export/output/export_output_json.cpp | 75 +++++++++++++++++++ .../export/output/export_output_json.h | 2 + .../export/output/export_output_text.cpp | 17 ++++- .../export/output/export_output_text.h | 2 + .../export/view/export_view_content.cpp | 3 + .../view/export_view_panel_controller.cpp | 22 +++++- .../export/view/export_view_settings.cpp | 12 ++- .../export/view/export_view_settings.h | 4 +- 20 files changed, 260 insertions(+), 29 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index b46a9e68f57040..11c8b4e0a366d9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1660,8 +1660,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_export_option_info_about" = "Your chosen screen name, username, phone number and profile pictures."; "lng_export_option_contacts" = "Contacts list"; "lng_export_option_contacts_about" = "If you allow access, contacts are continuously synced with Telegram. You can adjust this in Settings > Privacy & Security on mobile devices."; -"lng_export_option_sessions" = "Sessions list"; +"lng_export_option_sessions" = "Active sessions"; "lng_export_option_sessions_about" = "We store this to display your connected devices in Settings > Privacy & Security > Active Sessions. Terminating a session removes this data from Telegram servers."; +"lng_export_header_other" = "Other"; +"lng_export_option_other" = "Miscellaneous data"; +"lng_export_option_other_about" = "Other types of data not mentioned above. (beta)"; "lng_export_header_chats" = "Chat export settings"; "lng_export_option_personal_chats" = "Personal chats"; "lng_export_option_bot_chats" = "Bot chats"; @@ -1705,7 +1708,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_export_total_size" = "Total size: {size}."; "lng_export_folder" = "Choose export folder"; "lng_export_invalid" = "Sorry, you have started a new data export, so this data export is now cancelled."; -"lng_export_delay" = "Sorry, for security reasons, you will be able to begin downloading your data in 24 hours. We have notified all your devices about the export request to make sure it's authorized and give you time to react if it's not.\n\nPlease come back on {date} and repeat the request using the same device."; +"lng_export_delay" = "Sorry, for security reasons, you will be able to begin downloading your data in {hours}. We have notified all your devices about the export request to make sure it's authorized and give you time to react if it's not.\n\nPlease come back on {date} and repeat the request using the same device."; +"lng_export_delay_less_than_hour" = "less than an hour"; +"lng_export_delay_hours#one" = "{count} hour"; +"lng_export_delay_hours#other" = "{count} hours"; "lng_export_suggest_title" = "Data export ready"; "lng_export_suggest_text" = "You can now download the data you requested. Start exporting data?"; "lng_export_suggest_cancel" = "Not now"; diff --git a/Telegram/Resources/scheme.tl b/Telegram/Resources/scheme.tl index 48e6a2e7778fef..7b022aaa4ea0d9 100644 --- a/Telegram/Resources/scheme.tl +++ b/Telegram/Resources/scheme.tl @@ -192,6 +192,7 @@ inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLo inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation; inputDocumentFileLocation#430f0724 id:long access_hash:long version:int = InputFileLocation; inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation; +inputTakeoutFileLocation#29be5899 = InputFileLocation; inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 0f96a964395543..ad57e3962707a1 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -71,6 +71,10 @@ Session::Session(not_null session) } void Session::startExport() { + if (_exportPanel) { + _exportPanel->activatePanel(); + return; + } _export = std::make_unique(); _exportPanel = std::make_unique( _export.get()); diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index c7e5c404abe58a..7f5316b54ecfdd 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -62,6 +62,8 @@ LocationKey ComputeLocationKey(const Data::FileLocation &value) { }, [&](const MTPDinputEncryptedFileLocation &data) { result.type |= (4ULL << 24); result.id = data.vid.v; + }, [&](const MTPDinputTakeoutFileLocation &data) { + result.type |= (5ULL << 24); }); return result; } @@ -140,6 +142,11 @@ struct ApiWrap::UserpicsProcess { int fileIndex = -1; }; +struct ApiWrap::OtherDataProcess { + Data::File file; + FnMut done; +}; + struct ApiWrap::FileProcess { FileProcess(const QString &path, Output::Stats *stats); @@ -259,7 +266,8 @@ auto ApiWrap::splitRequest(int index, Request &&request) { } auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) { - Expects(location.dcId != 0); + Expects(location.dcId != 0 + || location.data.type() == mtpc_inputTakeoutFileLocation); Expects(_takeoutId.has_value()); return std::move(_mtp.request(MTPInvokeWithTakeout( @@ -269,7 +277,14 @@ auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) { MTP_int(offset), MTP_int(kFileChunkSize)) )).fail([=](RPCError &&result) { - error(std::move(result)); + if (result.type() == qstr("TAKEOUT_FILE_EMPTY") + && _otherDataProcess != nullptr) { + filePartDone(0, MTP_upload_file(MTP_storage_filePartial(), + MTP_int(0), + MTP_bytes(QByteArray()))); + } else { + error(std::move(result)); + } }).toDC(MTP::ShiftDcId(location.dcId, MTP::kExportMediaDcShift))); } @@ -376,6 +391,8 @@ void ApiWrap::requestSplitRanges() { void ApiWrap::requestDialogsCount() { Expects(_startProcess != nullptr); + validateSplits(); + splitRequest(_startProcess->splitIndex, MTPmessages_GetDialogs( MTP_flags(0), MTP_int(0), // offset_date @@ -467,7 +484,7 @@ void ApiWrap::requestDialogsList( void ApiWrap::validateSplits() { if (_splits.empty()) { _splits.push_back(MTP_messageRange( - MTP_int(0), + MTP_int(1), MTP_int(std::numeric_limits::max()))); } } @@ -524,6 +541,29 @@ void ApiWrap::requestPersonalInfo(FnMut done) { }).send(); } +void ApiWrap::requestOtherData( + const QString &suggestedPath, + FnMut done) { + Expects(_otherDataProcess == nullptr); + + _otherDataProcess = std::make_unique(); + _otherDataProcess->done = std::move(done); + _otherDataProcess->file.location.data = MTP_inputTakeoutFileLocation(); + _otherDataProcess->file.suggestedPath = suggestedPath; + loadFile( + _otherDataProcess->file, + [](FileProgress progress) { return true; }, + [=](const QString &result) { otherDataDone(result); }); +} + +void ApiWrap::otherDataDone(const QString &relativePath) { + Expects(_otherDataProcess != nullptr); + + _otherDataProcess->file.relativePath = relativePath; + const auto process = base::take(_otherDataProcess); + process->done(std::move(process->file)); +} + void ApiWrap::requestUserpics( FnMut start, Fn progress, @@ -1185,7 +1225,8 @@ void ApiWrap::loadFile( Fn progress, FnMut done) { Expects(_fileProcess == nullptr); - Expects(file.location.dcId != 0); + Expects(file.location.dcId != 0 + || file.location.data.type() == mtpc_inputTakeoutFileLocation); _fileProcess = prepareFileProcess(file); _fileProcess->progress = std::move(progress); @@ -1265,6 +1306,11 @@ void ApiWrap::filePartDone(int offset, const MTPupload_File &result) { error("Empty bytes received in file part."); return; } + const auto result = _fileProcess->file.writeBlock({}); + if (!result) { + ioError(result); + return; + } } else { using Request = FileProcess::Request; auto &requests = _fileProcess->requests; diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h index 32f5c08331bed1..7e5a59c6e27644 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.h +++ b/Telegram/SourceFiles/export/export_api_wrap.h @@ -58,6 +58,10 @@ class ApiWrap { void requestPersonalInfo(FnMut done); + void requestOtherData( + const QString &suggestedPath, + FnMut done); + struct DownloadProgress { QString path; int itemIndex = 0; @@ -91,6 +95,7 @@ class ApiWrap { struct StartProcess; struct ContactsProcess; struct UserpicsProcess; + struct OtherDataProcess; struct FileProcess; struct FileProgress; struct ChatsProcess; @@ -116,6 +121,8 @@ class ApiWrap { void finishUserpicsSlice(); void finishUserpics(); + void otherDataDone(const QString &relativePath); + void validateSplits(); void requestDialogsSlice(); @@ -186,6 +193,7 @@ class ApiWrap { std::unique_ptr _fileCache; std::unique_ptr _contactsProcess; std::unique_ptr _userpicsProcess; + std::unique_ptr _otherDataProcess; std::unique_ptr _fileProcess; std::unique_ptr _leftChannelsProcess; std::unique_ptr _dialogsProcess; diff --git a/Telegram/SourceFiles/export/export_controller.cpp b/Telegram/SourceFiles/export/export_controller.cpp index 7cb980877cd882..985ed971b5c214 100644 --- a/Telegram/SourceFiles/export/export_controller.cpp +++ b/Telegram/SourceFiles/export/export_controller.cpp @@ -57,6 +57,7 @@ class Controller { void exportUserpics(); void exportContacts(); void exportSessions(); + void exportOtherData(); void exportDialogs(); void exportNextDialog(); void exportLeftChannels(); @@ -73,6 +74,7 @@ class Controller { ProcessingState stateUserpics(const DownloadProgress &progress) const; ProcessingState stateContacts() const; ProcessingState stateSessions() const; + ProcessingState stateOtherData() const; ProcessingState stateLeftChannels( const DownloadProgress &progress) const; ProcessingState stateDialogs(const DownloadProgress &progress) const; @@ -250,6 +252,9 @@ void Controller::fillExportSteps() { if (_settings.types & Type::Sessions) { _steps.push_back(Step::Sessions); } + if (_settings.types & Type::OtherData) { + _steps.push_back(Step::OtherData); + } if (_settings.types & Type::AnyChatsMask) { _steps.push_back(Step::Dialogs); } @@ -286,6 +291,9 @@ void Controller::fillSubstepsInSteps(const ApiWrap::StartInfo &info) { if (_settings.types & Settings::Type::Sessions) { push(Step::Sessions, 1); } + if (_settings.types & Settings::Type::OtherData) { + push(Step::OtherData, 1); + } if (_settings.types & Settings::Type::GroupsChannelsMask) { push(Step::LeftChannels, info.leftChannelsCount); } @@ -321,6 +329,7 @@ void Controller::exportNext() { case Step::Userpics: return exportUserpics(); case Step::Contacts: return exportContacts(); case Step::Sessions: return exportSessions(); + case Step::OtherData: return exportOtherData(); case Step::LeftChannels: return exportLeftChannels(); case Step::Dialogs: return exportDialogs(); } @@ -329,7 +338,6 @@ void Controller::exportNext() { void Controller::initialize() { setState(stateInitializing()); - _api.startExport(_settings, &_stats, [=](ApiWrap::StartInfo info) { if (ioCatchError(_writer->start(_settings, &_stats))) { return; @@ -340,6 +348,7 @@ void Controller::initialize() { } void Controller::collectLeftChannels() { + setState(stateLeftChannelsList(0)); _api.requestLeftChannelsList([=](int count) { setState(stateLeftChannelsList(count)); return true; @@ -350,6 +359,7 @@ void Controller::collectLeftChannels() { } void Controller::collectDialogsList() { + setState(stateDialogsList(0)); _api.requestDialogsList([=](int count) { setState(stateDialogsList(count)); return true; @@ -360,6 +370,7 @@ void Controller::collectDialogsList() { } void Controller::exportPersonalInfo() { + setState(statePersonalInfo()); _api.requestPersonalInfo([=](Data::PersonalInfo &&result) { if (ioCatchError(_writer->writePersonal(result))) { return; @@ -395,6 +406,7 @@ void Controller::exportUserpics() { } void Controller::exportContacts() { + setState(stateContacts()); _api.requestContacts([=](Data::ContactsList &&result) { if (ioCatchError(_writer->writeContactsList(result))) { return; @@ -404,6 +416,7 @@ void Controller::exportContacts() { } void Controller::exportSessions() { + setState(stateSessions()); _api.requestSessions([=](Data::SessionsList &&result) { if (ioCatchError(_writer->writeSessionsList(result))) { return; @@ -412,6 +425,17 @@ void Controller::exportSessions() { }); } +void Controller::exportOtherData() { + setState(stateOtherData()); + const auto relativePath = "lists/other_data.json"; + _api.requestOtherData(relativePath, [=](Data::File &&result) { + if (ioCatchError(_writer->writeOtherData(result))) { + return; + } + exportNext(); + }); +} + void Controller::exportDialogs() { if (ioCatchError(_writer->writeDialogsStart(_dialogsInfo))) { return; @@ -573,6 +597,10 @@ ProcessingState Controller::stateSessions() const { return prepareState(Step::Sessions); } +ProcessingState Controller::stateOtherData() const { + return prepareState(Step::OtherData); +} + ProcessingState Controller::stateLeftChannels( const DownloadProgress & progress) const { const auto step = Step::LeftChannels; diff --git a/Telegram/SourceFiles/export/export_controller.h b/Telegram/SourceFiles/export/export_controller.h index 9d38f1882d3472..ac1928438a28db 100644 --- a/Telegram/SourceFiles/export/export_controller.h +++ b/Telegram/SourceFiles/export/export_controller.h @@ -33,6 +33,7 @@ struct ProcessingState { Userpics, Contacts, Sessions, + OtherData, LeftChannels, Dialogs, }; diff --git a/Telegram/SourceFiles/export/export_settings.cpp b/Telegram/SourceFiles/export/export_settings.cpp index 082f90bcdfb420..d44e17fcbc9aba 100644 --- a/Telegram/SourceFiles/export/export_settings.cpp +++ b/Telegram/SourceFiles/export/export_settings.cpp @@ -37,7 +37,7 @@ bool Settings::validate() const { return false; } else if ((fullChats & MustNotBeFull) != 0) { return false; - } else if (format != Format::Text && format != Format::Json) { + } else if (format != Format::Html && format != Format::Json) { return false; } else if (!media.validate()) { return false; diff --git a/Telegram/SourceFiles/export/export_settings.h b/Telegram/SourceFiles/export/export_settings.h index 129d43f73b08f0..123c2c624be69a 100644 --- a/Telegram/SourceFiles/export/export_settings.h +++ b/Telegram/SourceFiles/export/export_settings.h @@ -50,12 +50,13 @@ struct Settings { Userpics = 0x002, Contacts = 0x004, Sessions = 0x008, - PersonalChats = 0x010, - BotChats = 0x020, - PrivateGroups = 0x040, - PublicGroups = 0x080, - PrivateChannels = 0x100, - PublicChannels = 0x200, + OtherData = 0x010, + PersonalChats = 0x020, + BotChats = 0x040, + PrivateGroups = 0x080, + PublicGroups = 0x100, + PrivateChannels = 0x200, + PublicChannels = 0x400, GroupsMask = PrivateGroups | PublicGroups, ChannelsMask = PrivateChannels | PublicChannels, @@ -63,7 +64,7 @@ struct Settings { NonChannelChatsMask = PersonalChats | BotChats | PrivateGroups, AnyChatsMask = PersonalChats | BotChats | GroupsChannelsMask, NonChatsMask = PersonalInfo | Userpics | Contacts | Sessions, - AllMask = NonChatsMask | AnyChatsMask, + AllMask = NonChatsMask | OtherData | AnyChatsMask, }; using Types = base::flags; friend inline constexpr auto is_flag_type(Type) { return true; }; diff --git a/Telegram/SourceFiles/export/output/export_output_abstract.h b/Telegram/SourceFiles/export/output/export_output_abstract.h index b0a485ca9d9862..e28ae4e7a2d6c5 100644 --- a/Telegram/SourceFiles/export/output/export_output_abstract.h +++ b/Telegram/SourceFiles/export/output/export_output_abstract.h @@ -19,6 +19,7 @@ struct SessionsList; struct DialogsInfo; struct DialogInfo; struct MessagesSlice; +struct File; } // namespace Data struct Settings; @@ -60,6 +61,9 @@ class AbstractWriter { [[nodiscard]] virtual Result writeSessionsList( const Data::SessionsList &data) = 0; + [[nodiscard]] virtual Result writeOtherData( + const Data::File &data) = 0; + [[nodiscard]] virtual Result writeDialogsStart( const Data::DialogsInfo &data) = 0; [[nodiscard]] virtual Result writeDialogStart( diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index da1c404a6e2a0f..9bfda6cd3cd499 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -637,7 +637,7 @@ QByteArray HtmlWriter::Wrap::end() const { } HtmlWriter::Wrap::~Wrap() { - Expects(_file.empty() || _closed); + (void)close(); } HtmlWriter::HtmlWriter() = default; @@ -1019,6 +1019,17 @@ Result HtmlWriter::writeWebSessions(const Data::SessionsList &data) { return _summary->writeBlock(header); } +Result HtmlWriter::writeOtherData(const Data::File &data) { + Expects(_summary != nullptr); + + const auto header = SerializeLink( + "Other data", + _summary->relativePath(data)) + + kLineBreak + + kLineBreak; + return _summary->writeBlock(header); +} + Result HtmlWriter::writeDialogsStart(const Data::DialogsInfo &data) { return writeChatsStart( data, @@ -1123,7 +1134,9 @@ Result HtmlWriter::writeChatSlice(const Data::MessagesSlice &data) { data.peers, _settings.internalLinksDomain)); } - const auto full = kLineBreak + JoinList(kLineBreak, list); + const auto full = _chat->empty() + ? JoinList(kLineBreak, list) + : kLineBreak + JoinList(kLineBreak, list); return _chat->writeBlock(full); } @@ -1172,7 +1185,7 @@ Result HtmlWriter::writeChatEnd() { } Unexpected("Dialog type in TypeString."); }; - return _chats->writeBlock(SerializeKeyValue({ + return _chats->writeBlock(kLineBreak + SerializeKeyValue({ { "Name", SerializeString(NameString(_dialog, _dialog.type)) }, { "Type", SerializeString(TypeString(_dialog.type)) }, { @@ -1190,7 +1203,7 @@ Result HtmlWriter::writeChatEnd() { (_dialog.relativePath + "messages.html"))) : QByteArray()) } - }) + kLineBreak); + })); } Result HtmlWriter::writeChatsEnd() { diff --git a/Telegram/SourceFiles/export/output/export_output_html.h b/Telegram/SourceFiles/export/output/export_output_html.h index fad9725b290930..6a1fd8e2fa222d 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.h +++ b/Telegram/SourceFiles/export/output/export_output_html.h @@ -35,6 +35,8 @@ class HtmlWriter : public AbstractWriter { Result writeSessionsList(const Data::SessionsList &data) override; + Result writeOtherData(const Data::File &data) override; + Result writeDialogsStart(const Data::DialogsInfo &data) override; Result writeDialogStart(const Data::DialogInfo &data) override; Result writeDialogSlice(const Data::MessagesSlice &data) override; diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index 5627b9083cecaa..9300b3009f5b84 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -13,6 +13,10 @@ For license and copyright information please follow this link: #include "core/utils.h" #include +#include +#include +#include +#include namespace Export { namespace Output { @@ -793,6 +797,77 @@ Result JsonWriter::writeSessionsList(const Data::SessionsList &data) { return Result::Success(); } +Result JsonWriter::writeOtherData(const Data::File &data) { + Expects(_output != nullptr); + Expects(data.skipReason == Data::File::SkipReason::None); + Expects(!data.relativePath.isEmpty()); + + QFile f(pathWithRelativePath(data.relativePath)); + if (!f.open(QIODevice::ReadOnly)) { + return Result(Result::Type::FatalError, f.fileName()); + } + const auto content = f.readAll(); + if (content.isEmpty()) { + return Result::Success(); + } + auto error = QJsonParseError{ 0, QJsonParseError::NoError }; + const auto document = QJsonDocument::fromJson(content, &error); + if (error.error != QJsonParseError::NoError) { + return Result(Result::Type::FatalError, f.fileName()); + } + auto block = prepareObjectItemStart("other_data"); + Fn pushObject; + Fn pushArray; + Fn pushValue; + pushObject = [&](const QJsonObject &data) { + block.append(pushNesting(Context::kObject)); + for (auto i = data.begin(); i != data.end(); ++i) { + if ((*i).type() != QJsonValue::Undefined) { + block.append(prepareObjectItemStart(i.key().toUtf8())); + pushValue(*i); + } + } + block.append(popNesting()); + }; + pushArray = [&](const QJsonArray &data) { + block.append(pushNesting(Context::kArray)); + for (auto i = data.begin(); i != data.end(); ++i) { + if ((*i).type() != QJsonValue::Undefined) { + block.append(prepareArrayItemStart()); + pushValue(*i); + } + } + block.append(popNesting()); + }; + pushValue = [&](const QJsonValue &data) { + switch (data.type()) { + case QJsonValue::Null: + block.append("null"); + return; + case QJsonValue::Bool: + block.append(data.toBool() ? "true" : "false"); + return; + case QJsonValue::Double: + block.append(Data::NumberToString(data.toDouble())); + return; + case QJsonValue::String: + block.append(SerializeString(data.toString().toUtf8())); + return; + case QJsonValue::Array: + return pushArray(data.toArray()); + case QJsonValue::Object: + return pushObject(data.toObject()); + } + Unexpected("Type of json valuein JsonWriter::writeOtherData."); + }; + if (document.isObject()) { + pushObject(document.object()); + } else { + pushArray(document.array()); + } + return _output->writeBlock(block); +} + Result JsonWriter::writeSessions(const Data::SessionsList &data) { Expects(_output != nullptr); diff --git a/Telegram/SourceFiles/export/output/export_output_json.h b/Telegram/SourceFiles/export/output/export_output_json.h index 49d5355109b9da..fb59c50ea3a3eb 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.h +++ b/Telegram/SourceFiles/export/output/export_output_json.h @@ -45,6 +45,8 @@ class JsonWriter : public AbstractWriter { Result writeSessionsList(const Data::SessionsList &data) override; + Result writeOtherData(const Data::File &data) override; + Result writeDialogsStart(const Data::DialogsInfo &data) override; Result writeDialogStart(const Data::DialogInfo &data) override; Result writeDialogSlice(const Data::MessagesSlice &data) override; diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp index b3f32c92661c49..4c578c4cf5b953 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.cpp +++ b/Telegram/SourceFiles/export/output/export_output_text.cpp @@ -762,6 +762,15 @@ Result TextWriter::writeWebSessions(const Data::SessionsList &data) { return _summary->writeBlock(header); } +Result TextWriter::writeOtherData(const Data::File &data) { + Expects(_summary != nullptr); + + const auto header = "Other data - " + data.relativePath.toUtf8() + + kLineBreak + + kLineBreak; + return _summary->writeBlock(header); +} + Result TextWriter::writeDialogsStart(const Data::DialogsInfo &data) { return writeChatsStart( data, @@ -864,7 +873,9 @@ Result TextWriter::writeChatSlice(const Data::MessagesSlice &data) { data.peers, _settings.internalLinksDomain)); } - const auto full = kLineBreak + JoinList(kLineBreak, list); + const auto full = _chat->empty() + ? JoinList(kLineBreak, list) + : kLineBreak + JoinList(kLineBreak, list); return _chat->writeBlock(full); } @@ -911,7 +922,7 @@ Result TextWriter::writeChatEnd() { } Unexpected("Dialog type in TypeString."); }; - return _chats->writeBlock(SerializeKeyValue({ + return _chats->writeBlock(kLineBreak + SerializeKeyValue({ { "Name", NameString(_dialog, _dialog.type) }, { "Type", TypeString(_dialog.type) }, { @@ -926,7 +937,7 @@ Result TextWriter::writeChatEnd() { ? (_dialog.relativePath + "messages.txt").toUtf8() : QByteArray()) } - }) + kLineBreak); + })); } Result TextWriter::writeChatsEnd() { diff --git a/Telegram/SourceFiles/export/output/export_output_text.h b/Telegram/SourceFiles/export/output/export_output_text.h index 2c7265394b4d16..3d966236dc8309 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.h +++ b/Telegram/SourceFiles/export/output/export_output_text.h @@ -33,6 +33,8 @@ class TextWriter : public AbstractWriter { Result writeSessionsList(const Data::SessionsList &data) override; + Result writeOtherData(const Data::File &data) override; + Result writeDialogsStart(const Data::DialogsInfo &data) override; Result writeDialogStart(const Data::DialogInfo &data) override; Result writeDialogSlice(const Data::MessagesSlice &data) override; diff --git a/Telegram/SourceFiles/export/view/export_view_content.cpp b/Telegram/SourceFiles/export/view/export_view_content.cpp index 60fcda275af82a..f358a2d13d8a51 100644 --- a/Telegram/SourceFiles/export/view/export_view_content.cpp +++ b/Telegram/SourceFiles/export/view/export_view_content.cpp @@ -80,6 +80,9 @@ Content ContentFromState(const ProcessingState &state) { case Step::Sessions: pushMain(lang(lng_export_option_sessions)); break; + case Step::OtherData: + pushMain(lang(lng_export_option_other)); + break; case Step::LeftChannels: case Step::Dialogs: pushMain(lang(lng_export_state_chats)); diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp index f2f86deb22c585..ed5e9fb75fbd27 100644 --- a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp @@ -91,8 +91,10 @@ void SuggestStart() { void ClearSuggestStart() { auto settings = Local::ReadExportSettings(); - settings.availableAt = 0; - Local::WriteExportSettings(settings); + if (settings.availableAt) { + settings.availableAt = 0; + Local::WriteExportSettings(settings); + } } PanelController::PanelController(not_null process) @@ -160,7 +162,18 @@ void PanelController::showError(const ApiErrorState &error) { qstr("TAKEOUT_INIT_DELAY_").size()).toInt(), 1); const auto now = QDateTime::currentDateTime(); const auto when = now.addSecs(seconds); - showError(lng_export_delay(lt_date, langDateTimeFull(when))); + const auto hours = seconds / 3600; + const auto hoursText = [&] { + if (hours <= 0) { + return lang(lng_export_delay_less_than_hour); + } + return lng_export_delay_hours(lt_count, hours); + }(); + showError(lng_export_delay( + lt_hours, + hoursText, + lt_date, + langDateTimeFull(when))); _settings->availableAt = unixtime() + seconds; _saveSettingsTimer.callOnce(kSaveSettingsTimeout); @@ -217,6 +230,9 @@ void PanelController::showError(const QString &text) { } void PanelController::showProgress() { + _settings->availableAt = 0; + ClearSuggestStart(); + _panel->setTitle(Lang::Viewer(lng_export_progress_title)); auto progress = base::make_unique_q( diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp index b873f4c1353bd2..b9ee8f20455036 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.cpp +++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp @@ -134,6 +134,13 @@ void SettingsWidget::setupOptions(not_null container) { Type::PublicChannels); setupMediaOptions(container); + + addHeader(container, lng_export_header_other); + addOptionWithAbout( + container, + lng_export_option_other, + Type::OtherData, + lng_export_option_other_about); } void SettingsWidget::setupMediaOptions( @@ -320,12 +327,12 @@ not_null SettingsWidget::addOption( return checkbox; } -void SettingsWidget::addOptionWithAbout( +not_null SettingsWidget::addOptionWithAbout( not_null container, LangKey key, Types types, LangKey about) { - addOption(container, key, types); + const auto result = addOption(container, key, types); const auto label = container->add( object_ptr( container, @@ -333,6 +340,7 @@ void SettingsWidget::addOptionWithAbout( Ui::FlatLabel::InitType::Simple, st::exportAboutOptionLabel), st::exportAboutOptionPadding); + return result; } void SettingsWidget::addChatOption( diff --git a/Telegram/SourceFiles/export/view/export_view_settings.h b/Telegram/SourceFiles/export/view/export_view_settings.h index 34c804afa329dd..13cbc061ccf877 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.h +++ b/Telegram/SourceFiles/export/view/export_view_settings.h @@ -51,12 +51,12 @@ class SettingsWidget : public Ui::RpWidget { not_null container, LangKey key, Types types); - void addOptionWithAbout( + not_null addOptionWithAbout( not_null container, LangKey key, Types types, LangKey about); - void addChatOption( + void addChatOption( not_null container, LangKey key, Types types);