mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -05:00
Allow single chat history export.
This commit is contained in:
parent
6429e8b532
commit
a99ae76ad4
22 changed files with 424 additions and 62 deletions
|
@ -672,6 +672,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_profile_delete_conversation" = "Delete conversation";
|
"lng_profile_delete_conversation" = "Delete conversation";
|
||||||
"lng_profile_block_user" = "Block user";
|
"lng_profile_block_user" = "Block user";
|
||||||
"lng_profile_unblock_user" = "Unblock user";
|
"lng_profile_unblock_user" = "Unblock user";
|
||||||
|
"lng_profile_export_chat" = "Export chat history";
|
||||||
|
"lng_profile_export_channel" = "Export channel history";
|
||||||
"lng_media_selected_photo#one" = "{count} Photo";
|
"lng_media_selected_photo#one" = "{count} Photo";
|
||||||
"lng_media_selected_photo#other" = "{count} Photos";
|
"lng_media_selected_photo#other" = "{count} Photos";
|
||||||
"lng_media_selected_video#one" = "{count} Video";
|
"lng_media_selected_video#one" = "{count} Video";
|
||||||
|
|
|
@ -72,12 +72,16 @@ Session::Session(not_null<AuthSession*> session)
|
||||||
setupChannelLeavingViewer();
|
setupChannelLeavingViewer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::startExport() {
|
void Session::startExport(PeerData *peer) {
|
||||||
|
startExport(peer ? peer->input : MTP_inputPeerEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Session::startExport(const MTPInputPeer &singlePeer) {
|
||||||
if (_exportPanel) {
|
if (_exportPanel) {
|
||||||
_exportPanel->activatePanel();
|
_exportPanel->activatePanel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_export = std::make_unique<Export::ControllerWrap>();
|
_export = std::make_unique<Export::ControllerWrap>(singlePeer);
|
||||||
_exportPanel = std::make_unique<Export::View::PanelController>(
|
_exportPanel = std::make_unique<Export::View::PanelController>(
|
||||||
_export.get());
|
_export.get());
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,8 @@ public:
|
||||||
return *_session;
|
return *_session;
|
||||||
}
|
}
|
||||||
|
|
||||||
void startExport();
|
void startExport(PeerData *peer = nullptr);
|
||||||
|
void startExport(const MTPInputPeer &singlePeer);
|
||||||
void suggestStartExport(TimeId availableAt);
|
void suggestStartExport(TimeId availableAt);
|
||||||
void clearExportSuggestion();
|
void clearExportSuggestion();
|
||||||
rpl::producer<Export::View::PanelController*> currentExportView() const;
|
rpl::producer<Export::View::PanelController*> currentExportView() const;
|
||||||
|
|
|
@ -1386,19 +1386,36 @@ DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DialogInfo DialogInfoFromUser(const User &data) {
|
||||||
|
auto result = DialogInfo();
|
||||||
|
result.input = (Peer{ data }).input();
|
||||||
|
result.name = data.info.firstName;
|
||||||
|
result.lastName = data.info.lastName;
|
||||||
|
result.peerId = UserPeerId(data.info.userId);
|
||||||
|
result.topMessageDate = 0;
|
||||||
|
result.topMessageId = 0;
|
||||||
|
result.type = DialogTypeFromUser(data);
|
||||||
|
result.isLeftChannel = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogInfo DialogInfoFromChat(const Chat &data) {
|
||||||
|
auto result = DialogInfo();
|
||||||
|
result.input = data.input;
|
||||||
|
result.name = data.title;
|
||||||
|
result.peerId = ChatPeerId(data.id);
|
||||||
|
result.topMessageDate = 0;
|
||||||
|
result.topMessageId = 0;
|
||||||
|
result.type = DialogTypeFromChat(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data) {
|
DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data) {
|
||||||
auto result = DialogsInfo();
|
auto result = DialogsInfo();
|
||||||
data.match([&](const auto &data) { //MTPDmessages_chats &data) {
|
data.match([&](const auto &data) { //MTPDmessages_chats &data) {
|
||||||
result.left.reserve(data.vchats.v.size());
|
result.left.reserve(data.vchats.v.size());
|
||||||
for (const auto &single : data.vchats.v) {
|
for (const auto &single : data.vchats.v) {
|
||||||
const auto chat = ParseChat(single);
|
auto info = DialogInfoFromChat(ParseChat(single));
|
||||||
auto info = DialogInfo();
|
|
||||||
info.input = chat.input;
|
|
||||||
info.name = chat.title;
|
|
||||||
info.peerId = ChatPeerId(chat.id);
|
|
||||||
info.topMessageDate = 0;
|
|
||||||
info.topMessageId = 0;
|
|
||||||
info.type = DialogTypeFromChat(chat);
|
|
||||||
info.isLeftChannel = true;
|
info.isLeftChannel = true;
|
||||||
result.left.push_back(std::move(info));
|
result.left.push_back(std::move(info));
|
||||||
}
|
}
|
||||||
|
@ -1406,6 +1423,65 @@ DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DialogsInfo ParseDialogsInfo(
|
||||||
|
const MTPInputPeer &singlePeer,
|
||||||
|
const MTPVector<MTPUser> &data) {
|
||||||
|
const auto singleId = singlePeer.match(
|
||||||
|
[](const MTPDinputPeerUser &data) {
|
||||||
|
return data.vuser_id.v;
|
||||||
|
}, [](const MTPDinputPeerSelf &data) {
|
||||||
|
return 0;
|
||||||
|
}, [](const auto &data) -> int {
|
||||||
|
Unexpected("Single peer type in ParseDialogsInfo(users).");
|
||||||
|
});
|
||||||
|
auto result = DialogsInfo();
|
||||||
|
result.chats.reserve(data.v.size());
|
||||||
|
for (const auto &single : data.v) {
|
||||||
|
const auto userId = single.match([&](const auto &data) {
|
||||||
|
return data.vid.v;
|
||||||
|
});
|
||||||
|
if (userId != singleId
|
||||||
|
&& (singleId != 0
|
||||||
|
|| single.type() != mtpc_user
|
||||||
|
|| !single.c_user().is_self())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto info = DialogInfoFromUser(ParseUser(single));
|
||||||
|
result.chats.push_back(std::move(info));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogsInfo ParseDialogsInfo(
|
||||||
|
const MTPInputPeer &singlePeer,
|
||||||
|
const MTPmessages_Chats &data) {
|
||||||
|
const auto singleId = singlePeer.match(
|
||||||
|
[](const MTPDinputPeerChat &data) {
|
||||||
|
return data.vchat_id.v;
|
||||||
|
}, [](const MTPDinputPeerChannel &data) {
|
||||||
|
return data.vchannel_id.v;
|
||||||
|
}, [](const auto &data) -> int {
|
||||||
|
Unexpected("Single peer type in ParseDialogsInfo(chats).");
|
||||||
|
});
|
||||||
|
auto result = DialogsInfo();
|
||||||
|
data.match([&](const auto &data) { //MTPDmessages_chats &data) {
|
||||||
|
result.chats.reserve(data.vchats.v.size());
|
||||||
|
for (const auto &single : data.vchats.v) {
|
||||||
|
const auto chatId = single.match([&](const auto &data) {
|
||||||
|
return data.vid.v;
|
||||||
|
});
|
||||||
|
if (chatId != singleId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto chat = ParseChat(single);
|
||||||
|
auto info = DialogInfoFromChat(ParseChat(single));
|
||||||
|
info.isLeftChannel = false;
|
||||||
|
result.chats.push_back(std::move(info));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) {
|
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) {
|
||||||
auto &chats = info.chats;
|
auto &chats = info.chats;
|
||||||
auto &left = info.left;
|
auto &left = info.left;
|
||||||
|
@ -1414,7 +1490,9 @@ void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) {
|
||||||
auto index = 0;
|
auto index = 0;
|
||||||
for (auto &dialog : chats) {
|
for (auto &dialog : chats) {
|
||||||
const auto number = Data::NumberToString(++index, digits, '0');
|
const auto number = Data::NumberToString(++index, digits, '0');
|
||||||
dialog.relativePath = "chats/chat_" + number + '/';
|
dialog.relativePath = settings.onlySinglePeer()
|
||||||
|
? QString()
|
||||||
|
: "chats/chat_" + QString::fromUtf8(number) + '/';
|
||||||
|
|
||||||
using DialogType = DialogInfo::Type;
|
using DialogType = DialogInfo::Type;
|
||||||
using Type = Settings::Type;
|
using Type = Settings::Type;
|
||||||
|
@ -1436,6 +1514,8 @@ void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) {
|
||||||
ranges::reverse(dialog.splits);
|
ranges::reverse(dialog.splits);
|
||||||
}
|
}
|
||||||
for (auto &dialog : left) {
|
for (auto &dialog : left) {
|
||||||
|
Assert(!settings.onlySinglePeer());
|
||||||
|
|
||||||
const auto number = Data::NumberToString(++index, digits, '0');
|
const auto number = Data::NumberToString(++index, digits, '0');
|
||||||
dialog.relativePath = "chats/chat_" + number + '/';
|
dialog.relativePath = "chats/chat_" + number + '/';
|
||||||
dialog.onlyMyMessages = true;
|
dialog.onlyMyMessages = true;
|
||||||
|
|
|
@ -546,6 +546,12 @@ struct DialogsInfo {
|
||||||
DialogInfo::Type DialogTypeFromChat(const Chat &chat);
|
DialogInfo::Type DialogTypeFromChat(const Chat &chat);
|
||||||
|
|
||||||
DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data);
|
DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data);
|
||||||
|
DialogsInfo ParseDialogsInfo(
|
||||||
|
const MTPInputPeer &singlePeer,
|
||||||
|
const MTPVector<MTPUser> &data);
|
||||||
|
DialogsInfo ParseDialogsInfo(
|
||||||
|
const MTPInputPeer &singlePeer,
|
||||||
|
const MTPmessages_Chats &data);
|
||||||
DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data);
|
DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data);
|
||||||
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings);
|
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings);
|
||||||
|
|
||||||
|
|
|
@ -369,7 +369,8 @@ auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) {
|
||||||
filePartDone(0, MTP_upload_file(MTP_storage_filePartial(),
|
filePartDone(0, MTP_upload_file(MTP_storage_filePartial(),
|
||||||
MTP_int(0),
|
MTP_int(0),
|
||||||
MTP_bytes(QByteArray())));
|
MTP_bytes(QByteArray())));
|
||||||
} else if (result.type() == qstr("LOCATION_INVALID")) {
|
} else if (result.type() == qstr("LOCATION_INVALID")
|
||||||
|
|| result.type() == qstr("VERSION_INVALID")) {
|
||||||
filePartUnavailable();
|
filePartUnavailable();
|
||||||
} else {
|
} else {
|
||||||
error(std::move(result));
|
error(std::move(result));
|
||||||
|
@ -413,7 +414,9 @@ void ApiWrap::startExport(
|
||||||
_startProcess->steps.push_back(Step::DialogsCount);
|
_startProcess->steps.push_back(Step::DialogsCount);
|
||||||
}
|
}
|
||||||
if (_settings->types & Settings::Type::GroupsChannelsMask) {
|
if (_settings->types & Settings::Type::GroupsChannelsMask) {
|
||||||
_startProcess->steps.push_back(Step::LeftChannelsCount);
|
if (!_settings->onlySinglePeer()) {
|
||||||
|
_startProcess->steps.push_back(Step::LeftChannelsCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
startMainSession([=] {
|
startMainSession([=] {
|
||||||
sendNextStartRequest();
|
sendNextStartRequest();
|
||||||
|
@ -489,6 +492,15 @@ void ApiWrap::requestSplitRanges() {
|
||||||
void ApiWrap::requestDialogsCount() {
|
void ApiWrap::requestDialogsCount() {
|
||||||
Expects(_startProcess != nullptr);
|
Expects(_startProcess != nullptr);
|
||||||
|
|
||||||
|
if (_settings->onlySinglePeer()) {
|
||||||
|
_startProcess->info.dialogsCount =
|
||||||
|
(_settings->singlePeer.type() == mtpc_inputPeerChannel
|
||||||
|
? 1
|
||||||
|
: _splits.size());
|
||||||
|
sendNextStartRequest();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto offsetDate = 0;
|
const auto offsetDate = 0;
|
||||||
const auto offsetId = 0;
|
const auto offsetId = 0;
|
||||||
const auto offsetPeer = MTP_inputPeerEmpty();
|
const auto offsetPeer = MTP_inputPeerEmpty();
|
||||||
|
@ -959,9 +971,58 @@ void ApiWrap::cancelExportFast() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::requestSinglePeerDialog() {
|
||||||
|
auto doneSinglePeer = [=](const auto &result) {
|
||||||
|
auto info = Data::ParseDialogsInfo(_settings->singlePeer, result);
|
||||||
|
|
||||||
|
_dialogsProcess->processedCount += info.chats.size();
|
||||||
|
appendDialogsSlice(std::move(info));
|
||||||
|
|
||||||
|
const auto last = _dialogsProcess->splitIndexPlusOne - 1;
|
||||||
|
for (auto &info : _dialogsProcess->info.chats) {
|
||||||
|
for (auto i = last; i != 0; --i) {
|
||||||
|
info.splits.push_back(i - 1);
|
||||||
|
info.messagesCountPerSplit.push_back(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_dialogsProcess->progress(_dialogsProcess->processedCount)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finishDialogsList();
|
||||||
|
};
|
||||||
|
const auto requestUser = [&](const MTPInputUser &data) {
|
||||||
|
mainRequest(MTPusers_GetUsers(
|
||||||
|
MTP_vector<MTPInputUser>(1, data)
|
||||||
|
)).done(std::move(doneSinglePeer)).send();
|
||||||
|
};
|
||||||
|
_settings->singlePeer.match([&](const MTPDinputPeerUser &data) {
|
||||||
|
requestUser(MTP_inputUser(data.vuser_id, data.vaccess_hash));
|
||||||
|
}, [&](const MTPDinputPeerChat &data) {
|
||||||
|
mainRequest(MTPmessages_GetChats(
|
||||||
|
MTP_vector<MTPint>(1, data.vchat_id)
|
||||||
|
)).done(std::move(doneSinglePeer)).send();
|
||||||
|
}, [&](const MTPDinputPeerChannel &data) {
|
||||||
|
mainRequest(MTPchannels_GetChannels(
|
||||||
|
MTP_vector<MTPInputChannel>(
|
||||||
|
1,
|
||||||
|
MTP_inputChannel(data.vchannel_id, data.vaccess_hash))
|
||||||
|
)).done(std::move(doneSinglePeer)).send();
|
||||||
|
}, [&](const MTPDinputPeerSelf &data) {
|
||||||
|
requestUser(MTP_inputUserSelf());
|
||||||
|
}, [](const MTPDinputPeerEmpty &data) {
|
||||||
|
Unexpected("Empty peer in ApiWrap::requestSinglePeerDialog.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::requestDialogsSlice() {
|
void ApiWrap::requestDialogsSlice() {
|
||||||
Expects(_dialogsProcess != nullptr);
|
Expects(_dialogsProcess != nullptr);
|
||||||
|
|
||||||
|
if (_settings->onlySinglePeer()) {
|
||||||
|
requestSinglePeerDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto splitIndex = _dialogsProcess->splitIndexPlusOne - 1;
|
const auto splitIndex = _dialogsProcess->splitIndexPlusOne - 1;
|
||||||
const auto hash = 0;
|
const auto hash = 0;
|
||||||
splitRequest(splitIndex, MTPmessages_GetDialogs(
|
splitRequest(splitIndex, MTPmessages_GetDialogs(
|
||||||
|
|
|
@ -124,6 +124,7 @@ private:
|
||||||
void requestDialogsSlice();
|
void requestDialogsSlice();
|
||||||
void appendDialogsSlice(Data::DialogsInfo &&info);
|
void appendDialogsSlice(Data::DialogsInfo &&info);
|
||||||
void finishDialogsList();
|
void finishDialogsList();
|
||||||
|
void requestSinglePeerDialog();
|
||||||
|
|
||||||
void requestLeftChannelsIfNeeded();
|
void requestLeftChannelsIfNeeded();
|
||||||
void requestLeftChannelsList(
|
void requestLeftChannelsList(
|
||||||
|
|
|
@ -15,12 +15,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "export/output/export_output_stats.h"
|
#include "export/output/export_output_stats.h"
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
|
namespace {
|
||||||
|
|
||||||
auto kNullStateCallback = [](ProcessingState&) {};
|
const auto kNullStateCallback = [](ProcessingState&) {};
|
||||||
|
|
||||||
|
Settings NormalizeSettings(const Settings &settings) {
|
||||||
|
if (!settings.onlySinglePeer()) {
|
||||||
|
return base::duplicate(settings);
|
||||||
|
}
|
||||||
|
auto result = base::duplicate(settings);
|
||||||
|
result.format = Output::Format::Html;
|
||||||
|
result.types = result.fullChats = Settings::Type::AnyChatsMask;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
public:
|
public:
|
||||||
Controller(crl::weak_on_queue<Controller> weak);
|
Controller(
|
||||||
|
crl::weak_on_queue<Controller> weak,
|
||||||
|
const MTPInputPeer &peer);
|
||||||
|
|
||||||
rpl::producer<State> state() const;
|
rpl::producer<State> state() const;
|
||||||
|
|
||||||
|
@ -83,8 +98,6 @@ private:
|
||||||
|
|
||||||
int substepsInStep(Step step) const;
|
int substepsInStep(Step step) const;
|
||||||
|
|
||||||
bool normalizePath();
|
|
||||||
|
|
||||||
ApiWrap _api;
|
ApiWrap _api;
|
||||||
Settings _settings;
|
Settings _settings;
|
||||||
Environment _environment;
|
Environment _environment;
|
||||||
|
@ -117,7 +130,9 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Controller::Controller(crl::weak_on_queue<Controller> weak)
|
Controller::Controller(
|
||||||
|
crl::weak_on_queue<Controller> weak,
|
||||||
|
const MTPInputPeer &peer)
|
||||||
: _api(weak.runner())
|
: _api(weak.runner())
|
||||||
, _state(PasswordCheckState{}) {
|
, _state(PasswordCheckState{}) {
|
||||||
_api.errors(
|
_api.errors(
|
||||||
|
@ -134,6 +149,7 @@ Controller::Controller(crl::weak_on_queue<Controller> weak)
|
||||||
auto state = PasswordCheckState();
|
auto state = PasswordCheckState();
|
||||||
state.checked = false;
|
state.checked = false;
|
||||||
state.requesting = false;
|
state.requesting = false;
|
||||||
|
state.singlePeer = peer;
|
||||||
setState(std::move(state));
|
setState(std::move(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,10 +234,10 @@ void Controller::startExport(
|
||||||
if (!_settings.path.isEmpty()) {
|
if (!_settings.path.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_settings = base::duplicate(settings);
|
_settings = NormalizeSettings(settings);
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
|
|
||||||
_settings.path = Output::NormalizePath(_settings.path);
|
_settings.path = Output::NormalizePath(_settings);
|
||||||
_writer = Output::CreateWriter(_settings.format);
|
_writer = Output::CreateWriter(_settings.format);
|
||||||
fillExportSteps();
|
fillExportSteps();
|
||||||
exportNext();
|
exportNext();
|
||||||
|
@ -569,7 +585,7 @@ void Controller::setFinishedState() {
|
||||||
_stats.bytesCount() });
|
_stats.bytesCount() });
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerWrap::ControllerWrap() {
|
ControllerWrap::ControllerWrap(const MTPInputPeer &peer) : _wrapped(peer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<State> ControllerWrap::state() const {
|
rpl::producer<State> ControllerWrap::state() const {
|
||||||
|
|
|
@ -23,6 +23,7 @@ struct PasswordCheckState {
|
||||||
bool requesting = true;
|
bool requesting = true;
|
||||||
bool hasPassword = false;
|
bool hasPassword = false;
|
||||||
bool checked = false;
|
bool checked = false;
|
||||||
|
MTPInputPeer singlePeer = MTP_inputPeerEmpty();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProcessingState {
|
struct ProcessingState {
|
||||||
|
@ -110,7 +111,7 @@ using State = base::optional_variant<
|
||||||
|
|
||||||
class ControllerWrap {
|
class ControllerWrap {
|
||||||
public:
|
public:
|
||||||
ControllerWrap();
|
explicit ControllerWrap(const MTPInputPeer &peer);
|
||||||
|
|
||||||
rpl::producer<State> state() const;
|
rpl::producer<State> state() const;
|
||||||
|
|
||||||
|
|
|
@ -76,8 +76,14 @@ struct Settings {
|
||||||
Types fullChats = DefaultFullChats();
|
Types fullChats = DefaultFullChats();
|
||||||
MediaSettings media;
|
MediaSettings media;
|
||||||
|
|
||||||
|
MTPInputPeer singlePeer = MTP_inputPeerEmpty();
|
||||||
|
|
||||||
TimeId availableAt = 0;
|
TimeId availableAt = 0;
|
||||||
|
|
||||||
|
bool onlySinglePeer() const {
|
||||||
|
return singlePeer.type() != mtpc_inputPeerEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
static inline Types DefaultTypes() {
|
static inline Types DefaultTypes() {
|
||||||
return Type::PersonalInfo
|
return Type::PersonalInfo
|
||||||
| Type::Userpics
|
| Type::Userpics
|
||||||
|
|
|
@ -19,8 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Export {
|
namespace Export {
|
||||||
namespace Output {
|
namespace Output {
|
||||||
|
|
||||||
QString NormalizePath(const QString &source) {
|
QString NormalizePath(const Settings &settings) {
|
||||||
QDir folder(source);
|
QDir folder(settings.path);
|
||||||
const auto path = folder.absolutePath();
|
const auto path = folder.absolutePath();
|
||||||
auto result = path.endsWith('/') ? path : (path + '/');
|
auto result = path.endsWith('/') ? path : (path + '/');
|
||||||
if (!folder.exists()) {
|
if (!folder.exists()) {
|
||||||
|
@ -32,7 +32,9 @@ QString NormalizePath(const QString &source) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
const auto date = QDate::currentDate();
|
const auto date = QDate::currentDate();
|
||||||
const auto base = QString("DataExport_%1_%2_%3"
|
const auto base = QString(settings.onlySinglePeer()
|
||||||
|
? "ChatExport_%1_%2_%3"
|
||||||
|
: "DataExport_%1_%2_%3"
|
||||||
).arg(date.day(), 2, 10, QChar('0')
|
).arg(date.day(), 2, 10, QChar('0')
|
||||||
).arg(date.month(), 2, 10, QChar('0')
|
).arg(date.month(), 2, 10, QChar('0')
|
||||||
).arg(date.year());
|
).arg(date.year());
|
||||||
|
|
|
@ -27,7 +27,7 @@ struct Environment;
|
||||||
|
|
||||||
namespace Output {
|
namespace Output {
|
||||||
|
|
||||||
QString NormalizePath(const QString &source);
|
QString NormalizePath(const Settings &settings);
|
||||||
|
|
||||||
struct Result;
|
struct Result;
|
||||||
class Stats;
|
class Stats;
|
||||||
|
|
|
@ -1727,7 +1727,6 @@ Result HtmlWriter::start(
|
||||||
_settings = base::duplicate(settings);
|
_settings = base::duplicate(settings);
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
_stats = stats;
|
_stats = stats;
|
||||||
_summary = fileWithRelativePath(mainFileRelativePath());
|
|
||||||
|
|
||||||
//const auto result = copyFile(
|
//const auto result = copyFile(
|
||||||
// ":/export/css/bootstrap.min.css",
|
// ":/export/css/bootstrap.min.css",
|
||||||
|
@ -1771,6 +1770,11 @@ Result HtmlWriter::start(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_settings.onlySinglePeer()) {
|
||||||
|
return Result::Success();
|
||||||
|
}
|
||||||
|
_summary = fileWithRelativePath(mainFileRelativePath());
|
||||||
auto block = _summary->pushHeader("Exported Data");
|
auto block = _summary->pushHeader("Exported Data");
|
||||||
block.append(_summary->pushDiv("page_body"));
|
block.append(_summary->pushDiv("page_body"));
|
||||||
return _summary->writeBlock(block);
|
return _summary->writeBlock(block);
|
||||||
|
@ -1810,6 +1814,8 @@ Result HtmlWriter::writeDelayedPersonal(const QString &userpicPath) {
|
||||||
Result HtmlWriter::writePreparedPersonal(
|
Result HtmlWriter::writePreparedPersonal(
|
||||||
const Data::PersonalInfo &data,
|
const Data::PersonalInfo &data,
|
||||||
const QString &userpicPath) {
|
const QString &userpicPath) {
|
||||||
|
Expects(_summary != nullptr);
|
||||||
|
|
||||||
const auto &info = data.user.info;
|
const auto &info = data.user.info;
|
||||||
|
|
||||||
auto userpic = UserpicData{ _selfColorIndex, kPersonalUserpicSize };
|
auto userpic = UserpicData{ _selfColorIndex, kPersonalUserpicSize };
|
||||||
|
@ -2197,11 +2203,12 @@ Result HtmlWriter::writeOtherData(const Data::File &data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HtmlWriter::writeDialogsStart(const Data::DialogsInfo &data) {
|
Result HtmlWriter::writeDialogsStart(const Data::DialogsInfo &data) {
|
||||||
Expects(_summary != nullptr);
|
|
||||||
Expects(_chats == nullptr);
|
Expects(_chats == nullptr);
|
||||||
|
|
||||||
if (data.chats.empty() && data.left.empty()) {
|
if (data.chats.empty() && data.left.empty()) {
|
||||||
return Result::Success();
|
return Result::Success();
|
||||||
|
} else if (_settings.onlySinglePeer()) {
|
||||||
|
return Result::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
_dialogsRelativePath = "lists/chats.html";
|
_dialogsRelativePath = "lists/chats.html";
|
||||||
|
@ -2293,12 +2300,35 @@ Result HtmlWriter::writeDialogSlice(const Data::MessagesSlice &data) {
|
||||||
return _chat->writeBlock(block);
|
return _chat->writeBlock(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HtmlWriter::writeDialogEnd() {
|
Result HtmlWriter::writeEmptySinglePeer() {
|
||||||
Expects(_chats != nullptr);
|
|
||||||
Expects(_chat != nullptr);
|
Expects(_chat != nullptr);
|
||||||
|
|
||||||
|
if (!_settings.onlySinglePeer() || _messagesCount != 0) {
|
||||||
|
return Result::Success();
|
||||||
|
}
|
||||||
|
Assert(_chatFileEmpty);
|
||||||
|
if (const auto result = writeDialogOpening(0); !result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return _chat->writeBlock(_chat->pushServiceMessage(
|
||||||
|
--_dateMessageId,
|
||||||
|
_dialog,
|
||||||
|
_settings.path,
|
||||||
|
"Empty chat"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result HtmlWriter::writeDialogEnd() {
|
||||||
|
Expects(_settings.onlySinglePeer() || _chats != nullptr);
|
||||||
|
Expects(_chat != nullptr);
|
||||||
|
|
||||||
|
if (const auto result = writeEmptySinglePeer(); !result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if (const auto closed = base::take(_chat)->close(); !closed) {
|
if (const auto closed = base::take(_chat)->close(); !closed) {
|
||||||
return closed;
|
return closed;
|
||||||
|
} else if (_settings.onlySinglePeer()) {
|
||||||
|
return Result::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
using Type = Data::DialogInfo::Type;
|
using Type = Data::DialogInfo::Type;
|
||||||
|
@ -2411,7 +2441,7 @@ Result HtmlWriter::writeDialogOpening(int index) {
|
||||||
: (_dialog.name + ' ' + _dialog.lastName);
|
: (_dialog.name + ' ' + _dialog.lastName);
|
||||||
auto block = _chat->pushHeader(
|
auto block = _chat->pushHeader(
|
||||||
name,
|
name,
|
||||||
_dialogsRelativePath);
|
_settings.onlySinglePeer() ? QString() : _dialogsRelativePath);
|
||||||
block.append(_chat->pushDiv("page_body chat_page"));
|
block.append(_chat->pushDiv("page_body chat_page"));
|
||||||
block.append(_chat->pushDiv("history"));
|
block.append(_chat->pushDiv("history"));
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
|
@ -2489,7 +2519,11 @@ Result HtmlWriter::switchToNextChatFile(int index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HtmlWriter::finish() {
|
Result HtmlWriter::finish() {
|
||||||
Expects(_summary != nullptr);
|
Expects(_settings.onlySinglePeer() || _summary != nullptr);
|
||||||
|
|
||||||
|
if (_settings.onlySinglePeer()) {
|
||||||
|
return Result::Success();
|
||||||
|
}
|
||||||
|
|
||||||
auto block = QByteArray();
|
auto block = QByteArray();
|
||||||
if (_haveSections) {
|
if (_haveSections) {
|
||||||
|
@ -2516,7 +2550,9 @@ Result HtmlWriter::copyFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
QString HtmlWriter::mainFilePath() {
|
QString HtmlWriter::mainFilePath() {
|
||||||
return pathWithRelativePath(mainFileRelativePath());
|
return pathWithRelativePath(_settings.onlySinglePeer()
|
||||||
|
? messagesFile(0)
|
||||||
|
: mainFileRelativePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString HtmlWriter::mainFileRelativePath() const {
|
QString HtmlWriter::mainFileRelativePath() const {
|
||||||
|
|
|
@ -108,6 +108,7 @@ private:
|
||||||
[[nodiscard]] Result validateDialogsMode(bool isLeftChannel);
|
[[nodiscard]] Result validateDialogsMode(bool isLeftChannel);
|
||||||
[[nodiscard]] Result writeDialogOpening(int index);
|
[[nodiscard]] Result writeDialogOpening(int index);
|
||||||
[[nodiscard]] Result switchToNextChatFile(int index);
|
[[nodiscard]] Result switchToNextChatFile(int index);
|
||||||
|
[[nodiscard]] Result writeEmptySinglePeer();
|
||||||
|
|
||||||
void pushSection(
|
void pushSection(
|
||||||
int priority,
|
int priority,
|
||||||
|
|
|
@ -45,7 +45,7 @@ void SuggestBox::prepare() {
|
||||||
|
|
||||||
addButton(langFactory(lng_box_ok), [=] {
|
addButton(langFactory(lng_box_ok), [=] {
|
||||||
closeBox();
|
closeBox();
|
||||||
Auth().data().startExport();
|
Auth().data().startExport(Local::ReadExportSettings().singlePeer);
|
||||||
});
|
});
|
||||||
addButton(langFactory(lng_export_suggest_cancel), [=] { closeBox(); });
|
addButton(langFactory(lng_export_suggest_cancel), [=] { closeBox(); });
|
||||||
setCloseByOutsideClick(false);
|
setCloseByOutsideClick(false);
|
||||||
|
@ -121,8 +121,11 @@ void PanelController::activatePanel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::createPanel() {
|
void PanelController::createPanel() {
|
||||||
|
const auto singlePeer = _settings->onlySinglePeer();
|
||||||
_panel = base::make_unique_q<Ui::SeparatePanel>();
|
_panel = base::make_unique_q<Ui::SeparatePanel>();
|
||||||
_panel->setTitle(Lang::Viewer(lng_export_title));
|
_panel->setTitle(Lang::Viewer(singlePeer
|
||||||
|
? lng_export_header_chats
|
||||||
|
: lng_export_title));
|
||||||
_panel->setInnerSize(st::exportPanelSize);
|
_panel->setInnerSize(st::exportPanelSize);
|
||||||
_panel->closeRequests(
|
_panel->closeRequests(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
|
@ -154,7 +157,6 @@ void PanelController::showSettings() {
|
||||||
settings->changes(
|
settings->changes(
|
||||||
) | rpl::start_with_next([=](Settings &&settings) {
|
) | rpl::start_with_next([=](Settings &&settings) {
|
||||||
*_settings = std::move(settings);
|
*_settings = std::move(settings);
|
||||||
_saveSettingsTimer.callOnce(kSaveSettingsTimeout);
|
|
||||||
}, settings->lifetime());
|
}, settings->lifetime());
|
||||||
|
|
||||||
_panel->showInner(std::move(settings));
|
_panel->showInner(std::move(settings));
|
||||||
|
@ -320,7 +322,14 @@ rpl::producer<> PanelController::stopRequests() const {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PanelController::fillParams(const PasswordCheckState &state) {
|
||||||
|
_settings->singlePeer = state.singlePeer;
|
||||||
|
}
|
||||||
|
|
||||||
void PanelController::updateState(State &&state) {
|
void PanelController::updateState(State &&state) {
|
||||||
|
if (const auto start = base::get_if<PasswordCheckState>(&state)) {
|
||||||
|
fillParams(*start);
|
||||||
|
}
|
||||||
if (!_panel) {
|
if (!_panel) {
|
||||||
createPanel();
|
createPanel();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ public:
|
||||||
~PanelController();
|
~PanelController();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void fillParams(const PasswordCheckState &state);
|
||||||
void stopExport();
|
void stopExport();
|
||||||
void createPanel();
|
void createPanel();
|
||||||
void updateState(State &&state);
|
void updateState(State &&state);
|
||||||
|
|
|
@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
#include "platform/platform_specific.h"
|
#include "platform/platform_specific.h"
|
||||||
#include "core/file_utilities.h"
|
#include "core/file_utilities.h"
|
||||||
|
#include "auth_session.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
#include "styles/style_export.h"
|
#include "styles/style_export.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
@ -55,10 +56,25 @@ int SizeLimitByIndex(int index) {
|
||||||
return megabytes() * kMegabyte;
|
return megabytes() * kMegabyte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PeerId ReadPeerId(const MTPInputPeer &data) {
|
||||||
|
return data.match([](const MTPDinputPeerUser &data) {
|
||||||
|
return peerFromUser(data.vuser_id.v);
|
||||||
|
}, [](const MTPDinputPeerChat &data) {
|
||||||
|
return peerFromChat(data.vchat_id.v);
|
||||||
|
}, [](const MTPDinputPeerChannel &data) {
|
||||||
|
return peerFromChannel(data.vchannel_id.v);
|
||||||
|
}, [](const MTPDinputPeerSelf &data) {
|
||||||
|
return Auth().userPeerId();
|
||||||
|
}, [](const MTPDinputPeerEmpty &data) {
|
||||||
|
return PeerId(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
SettingsWidget::SettingsWidget(QWidget *parent, Settings data)
|
SettingsWidget::SettingsWidget(QWidget *parent, Settings data)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
|
, _singlePeerId(ReadPeerId(data.singlePeer))
|
||||||
, _internal_data(std::move(data)) {
|
, _internal_data(std::move(data)) {
|
||||||
setupContent();
|
setupContent();
|
||||||
}
|
}
|
||||||
|
@ -95,6 +111,17 @@ void SettingsWidget::setupContent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsWidget::setupOptions(not_null<Ui::VerticalLayout*> container) {
|
void SettingsWidget::setupOptions(not_null<Ui::VerticalLayout*> container) {
|
||||||
|
if (!_singlePeerId) {
|
||||||
|
setupFullExportOptions(container);
|
||||||
|
}
|
||||||
|
setupMediaOptions(container);
|
||||||
|
if (!_singlePeerId) {
|
||||||
|
setupOtherOptions(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsWidget::setupFullExportOptions(
|
||||||
|
not_null<Ui::VerticalLayout*> container) {
|
||||||
addOptionWithAbout(
|
addOptionWithAbout(
|
||||||
container,
|
container,
|
||||||
lng_export_option_info,
|
lng_export_option_info,
|
||||||
|
@ -127,38 +154,21 @@ void SettingsWidget::setupOptions(not_null<Ui::VerticalLayout*> container) {
|
||||||
container,
|
container,
|
||||||
lng_export_option_public_channels,
|
lng_export_option_public_channels,
|
||||||
Type::PublicChannels);
|
Type::PublicChannels);
|
||||||
|
|
||||||
setupMediaOptions(container);
|
|
||||||
|
|
||||||
addHeader(container, lng_export_header_other);
|
|
||||||
addOptionWithAbout(
|
|
||||||
container,
|
|
||||||
lng_export_option_sessions,
|
|
||||||
Type::Sessions,
|
|
||||||
lng_export_option_sessions_about);
|
|
||||||
addOptionWithAbout(
|
|
||||||
container,
|
|
||||||
lng_export_option_other,
|
|
||||||
Type::OtherData,
|
|
||||||
lng_export_option_other_about);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsWidget::setupMediaOptions(
|
void SettingsWidget::setupMediaOptions(
|
||||||
not_null<Ui::VerticalLayout*> container) {
|
not_null<Ui::VerticalLayout*> container) {
|
||||||
|
if (_singlePeerId != 0) {
|
||||||
|
addMediaOptions(container);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto mediaWrap = container->add(
|
const auto mediaWrap = container->add(
|
||||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
container,
|
container,
|
||||||
object_ptr<Ui::VerticalLayout>(container)));
|
object_ptr<Ui::VerticalLayout>(container)));
|
||||||
const auto media = mediaWrap->entity();
|
const auto media = mediaWrap->entity();
|
||||||
addHeader(media, lng_export_header_media);
|
addHeader(media, lng_export_header_media);
|
||||||
addMediaOption(media, lng_export_option_photos, MediaType::Photo);
|
addMediaOptions(media);
|
||||||
addMediaOption(media, lng_export_option_video_files, MediaType::Video);
|
|
||||||
addMediaOption(media, lng_export_option_voice_messages, MediaType::VoiceMessage);
|
|
||||||
addMediaOption(media, lng_export_option_video_messages, MediaType::VideoMessage);
|
|
||||||
addMediaOption(media, lng_export_option_stickers, MediaType::Sticker);
|
|
||||||
addMediaOption(media, lng_export_option_gifs, MediaType::GIF);
|
|
||||||
addMediaOption(media, lng_export_option_files, MediaType::File);
|
|
||||||
addSizeSlider(media);
|
|
||||||
|
|
||||||
value() | rpl::map([](const Settings &data) {
|
value() | rpl::map([](const Settings &data) {
|
||||||
return data.types;
|
return data.types;
|
||||||
|
@ -178,8 +188,27 @@ void SettingsWidget::setupMediaOptions(
|
||||||
}, mediaWrap->lifetime());
|
}, mediaWrap->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsWidget::setupOtherOptions(
|
||||||
|
not_null<Ui::VerticalLayout*> container) {
|
||||||
|
addHeader(container, lng_export_header_other);
|
||||||
|
addOptionWithAbout(
|
||||||
|
container,
|
||||||
|
lng_export_option_sessions,
|
||||||
|
Type::Sessions,
|
||||||
|
lng_export_option_sessions_about);
|
||||||
|
addOptionWithAbout(
|
||||||
|
container,
|
||||||
|
lng_export_option_other,
|
||||||
|
Type::OtherData,
|
||||||
|
lng_export_option_other_about);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsWidget::setupPathAndFormat(
|
void SettingsWidget::setupPathAndFormat(
|
||||||
not_null<Ui::VerticalLayout*> container) {
|
not_null<Ui::VerticalLayout*> container) {
|
||||||
|
if (_singlePeerId != 0) {
|
||||||
|
addLocationLabel(container);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto formatGroup = std::make_shared<Ui::RadioenumGroup<Format>>(
|
const auto formatGroup = std::make_shared<Ui::RadioenumGroup<Format>>(
|
||||||
readData().format);
|
readData().format);
|
||||||
formatGroup->setChangedCallback([=](Format format) {
|
formatGroup->setChangedCallback([=](Format format) {
|
||||||
|
@ -205,6 +234,7 @@ void SettingsWidget::setupPathAndFormat(
|
||||||
|
|
||||||
void SettingsWidget::addLocationLabel(
|
void SettingsWidget::addLocationLabel(
|
||||||
not_null<Ui::VerticalLayout*> container) {
|
not_null<Ui::VerticalLayout*> container) {
|
||||||
|
#ifndef OS_MAC_STORE
|
||||||
auto pathLabel = value() | rpl::map([](const Settings &data) {
|
auto pathLabel = value() | rpl::map([](const Settings &data) {
|
||||||
return data.path;
|
return data.path;
|
||||||
}) | rpl::distinct_until_changed(
|
}) | rpl::distinct_until_changed(
|
||||||
|
@ -241,6 +271,7 @@ void SettingsWidget::addLocationLabel(
|
||||||
chooseFolder();
|
chooseFolder();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
#endif // OS_MAC_STORE
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<Ui::RpWidget*> SettingsWidget::setupButtons(
|
not_null<Ui::RpWidget*> SettingsWidget::setupButtons(
|
||||||
|
@ -382,6 +413,30 @@ void SettingsWidget::addChatOption(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsWidget::addMediaOptions(
|
||||||
|
not_null<Ui::VerticalLayout*> container) {
|
||||||
|
addMediaOption(container, lng_export_option_photos, MediaType::Photo);
|
||||||
|
addMediaOption(
|
||||||
|
container,
|
||||||
|
lng_export_option_video_files,
|
||||||
|
MediaType::Video);
|
||||||
|
addMediaOption(
|
||||||
|
container,
|
||||||
|
lng_export_option_voice_messages,
|
||||||
|
MediaType::VoiceMessage);
|
||||||
|
addMediaOption(
|
||||||
|
container,
|
||||||
|
lng_export_option_video_messages,
|
||||||
|
MediaType::VideoMessage);
|
||||||
|
addMediaOption(
|
||||||
|
container,
|
||||||
|
lng_export_option_stickers,
|
||||||
|
MediaType::Sticker);
|
||||||
|
addMediaOption(container, lng_export_option_gifs, MediaType::GIF);
|
||||||
|
addMediaOption(container, lng_export_option_files, MediaType::File);
|
||||||
|
addSizeSlider(container);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsWidget::addMediaOption(
|
void SettingsWidget::addMediaOption(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
LangKey key,
|
LangKey key,
|
||||||
|
|
|
@ -42,7 +42,9 @@ private:
|
||||||
not_null<Ui::ScrollArea*> scroll,
|
not_null<Ui::ScrollArea*> scroll,
|
||||||
not_null<Ui::RpWidget*> wrap);
|
not_null<Ui::RpWidget*> wrap);
|
||||||
void setupOptions(not_null<Ui::VerticalLayout*> container);
|
void setupOptions(not_null<Ui::VerticalLayout*> container);
|
||||||
|
void setupFullExportOptions(not_null<Ui::VerticalLayout*> container);
|
||||||
void setupMediaOptions(not_null<Ui::VerticalLayout*> container);
|
void setupMediaOptions(not_null<Ui::VerticalLayout*> container);
|
||||||
|
void setupOtherOptions(not_null<Ui::VerticalLayout*> container);
|
||||||
void setupPathAndFormat(not_null<Ui::VerticalLayout*> container);
|
void setupPathAndFormat(not_null<Ui::VerticalLayout*> container);
|
||||||
void addHeader(
|
void addHeader(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
@ -60,6 +62,7 @@ private:
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
LangKey key,
|
LangKey key,
|
||||||
Types types);
|
Types types);
|
||||||
|
void addMediaOptions(not_null<Ui::VerticalLayout*> container);
|
||||||
void addMediaOption(
|
void addMediaOption(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
LangKey key,
|
LangKey key,
|
||||||
|
@ -76,6 +79,8 @@ private:
|
||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
void changeData(Callback &&callback);
|
void changeData(Callback &&callback);
|
||||||
|
|
||||||
|
PeerId _singlePeerId = 0;
|
||||||
|
|
||||||
// Use through readData / changeData wrappers.
|
// Use through readData / changeData wrappers.
|
||||||
Settings _internal_data;
|
Settings _internal_data;
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,12 @@ constexpr auto kFileLoaderQueueStopTimeout = TimeMs(5000);
|
||||||
constexpr auto kDefaultStickerInstallDate = TimeId(1);
|
constexpr auto kDefaultStickerInstallDate = TimeId(1);
|
||||||
constexpr auto kProxyTypeShift = 1024;
|
constexpr auto kProxyTypeShift = 1024;
|
||||||
|
|
||||||
|
constexpr auto kSinglePeerTypeUser = qint32(1);
|
||||||
|
constexpr auto kSinglePeerTypeChat = qint32(2);
|
||||||
|
constexpr auto kSinglePeerTypeChannel = qint32(3);
|
||||||
|
constexpr auto kSinglePeerTypeSelf = qint32(4);
|
||||||
|
constexpr auto kSinglePeerTypeEmpty = qint32(0);
|
||||||
|
|
||||||
using FileKey = quint64;
|
using FileKey = quint64;
|
||||||
|
|
||||||
constexpr char tdfMagic[] = { 'T', 'D', 'F', '$' };
|
constexpr char tdfMagic[] = { 'T', 'D', 'F', '$' };
|
||||||
|
@ -4816,7 +4822,8 @@ void WriteExportSettings(const Export::Settings &settings) {
|
||||||
&& settings.media.sizeLimit == check.media.sizeLimit
|
&& settings.media.sizeLimit == check.media.sizeLimit
|
||||||
&& settings.path == check.path
|
&& settings.path == check.path
|
||||||
&& settings.format == check.format
|
&& settings.format == check.format
|
||||||
&& settings.availableAt == check.availableAt) {
|
&& settings.availableAt == check.availableAt
|
||||||
|
&& !settings.onlySinglePeer()) {
|
||||||
if (_exportSettingsKey) {
|
if (_exportSettingsKey) {
|
||||||
clearKey(_exportSettingsKey);
|
clearKey(_exportSettingsKey);
|
||||||
_exportSettingsKey = 0;
|
_exportSettingsKey = 0;
|
||||||
|
@ -4830,7 +4837,8 @@ void WriteExportSettings(const Export::Settings &settings) {
|
||||||
_writeMap(WriteMapWhen::Fast);
|
_writeMap(WriteMapWhen::Fast);
|
||||||
}
|
}
|
||||||
quint32 size = sizeof(quint32) * 6
|
quint32 size = sizeof(quint32) * 6
|
||||||
+ Serialize::stringSize(settings.path);
|
+ Serialize::stringSize(settings.path)
|
||||||
|
+ sizeof(qint32) * 2 + sizeof(quint64);
|
||||||
EncryptedDescriptor data(size);
|
EncryptedDescriptor data(size);
|
||||||
data.stream
|
data.stream
|
||||||
<< quint32(settings.types)
|
<< quint32(settings.types)
|
||||||
|
@ -4840,6 +4848,23 @@ void WriteExportSettings(const Export::Settings &settings) {
|
||||||
<< quint32(settings.format)
|
<< quint32(settings.format)
|
||||||
<< settings.path
|
<< settings.path
|
||||||
<< quint32(settings.availableAt);
|
<< quint32(settings.availableAt);
|
||||||
|
settings.singlePeer.match([&](const MTPDinputPeerUser &user) {
|
||||||
|
data.stream
|
||||||
|
<< kSinglePeerTypeUser
|
||||||
|
<< qint32(user.vuser_id.v)
|
||||||
|
<< quint64(user.vaccess_hash.v);
|
||||||
|
}, [&](const MTPDinputPeerChat &chat) {
|
||||||
|
data.stream << kSinglePeerTypeChat << qint32(chat.vchat_id.v);
|
||||||
|
}, [&](const MTPDinputPeerChannel &channel) {
|
||||||
|
data.stream
|
||||||
|
<< kSinglePeerTypeChannel
|
||||||
|
<< qint32(channel.vchannel_id.v)
|
||||||
|
<< quint64(channel.vaccess_hash.v);
|
||||||
|
}, [&](const MTPDinputPeerSelf &) {
|
||||||
|
data.stream << kSinglePeerTypeSelf;
|
||||||
|
}, [&](const MTPDinputPeerEmpty &) {
|
||||||
|
data.stream << kSinglePeerTypeEmpty;
|
||||||
|
});
|
||||||
|
|
||||||
FileWriteDescriptor file(_exportSettingsKey);
|
FileWriteDescriptor file(_exportSettingsKey);
|
||||||
file.writeEncrypted(data);
|
file.writeEncrypted(data);
|
||||||
|
@ -4859,6 +4884,8 @@ Export::Settings ReadExportSettings() {
|
||||||
quint32 mediaTypes = 0, mediaSizeLimit = 0;
|
quint32 mediaTypes = 0, mediaSizeLimit = 0;
|
||||||
quint32 format = 0, availableAt = 0;
|
quint32 format = 0, availableAt = 0;
|
||||||
QString path;
|
QString path;
|
||||||
|
qint32 singlePeerType = 0, singlePeerBareId = 0;
|
||||||
|
quint64 singlePeerAccessHash = 0;
|
||||||
file.stream
|
file.stream
|
||||||
>> types
|
>> types
|
||||||
>> fullChats
|
>> fullChats
|
||||||
|
@ -4867,6 +4894,19 @@ Export::Settings ReadExportSettings() {
|
||||||
>> format
|
>> format
|
||||||
>> path
|
>> path
|
||||||
>> availableAt;
|
>> availableAt;
|
||||||
|
if (!file.stream.atEnd()) {
|
||||||
|
file.stream >> singlePeerType;
|
||||||
|
switch (singlePeerType) {
|
||||||
|
case kSinglePeerTypeUser:
|
||||||
|
case kSinglePeerTypeChannel: {
|
||||||
|
file.stream >> singlePeerBareId >> singlePeerAccessHash;
|
||||||
|
} break;
|
||||||
|
case kSinglePeerTypeChat: file.stream >> singlePeerBareId; break;
|
||||||
|
case kSinglePeerTypeSelf:
|
||||||
|
case kSinglePeerTypeEmpty: break;
|
||||||
|
default: return Export::Settings();
|
||||||
|
}
|
||||||
|
}
|
||||||
auto result = Export::Settings();
|
auto result = Export::Settings();
|
||||||
result.types = Export::Settings::Types::from_raw(types);
|
result.types = Export::Settings::Types::from_raw(types);
|
||||||
result.fullChats = Export::Settings::Types::from_raw(fullChats);
|
result.fullChats = Export::Settings::Types::from_raw(fullChats);
|
||||||
|
@ -4875,6 +4915,25 @@ Export::Settings ReadExportSettings() {
|
||||||
result.format = Export::Output::Format(format);
|
result.format = Export::Output::Format(format);
|
||||||
result.path = path;
|
result.path = path;
|
||||||
result.availableAt = availableAt;
|
result.availableAt = availableAt;
|
||||||
|
result.singlePeer = [&] {
|
||||||
|
switch (singlePeerType) {
|
||||||
|
case kSinglePeerTypeUser:
|
||||||
|
return MTP_inputPeerUser(
|
||||||
|
MTP_int(singlePeerBareId),
|
||||||
|
MTP_long(singlePeerAccessHash));
|
||||||
|
case kSinglePeerTypeChat:
|
||||||
|
return MTP_inputPeerChat(MTP_int(singlePeerBareId));
|
||||||
|
case kSinglePeerTypeChannel:
|
||||||
|
return MTP_inputPeerChannel(
|
||||||
|
MTP_int(singlePeerBareId),
|
||||||
|
MTP_long(singlePeerAccessHash));
|
||||||
|
case kSinglePeerTypeSelf:
|
||||||
|
return MTP_inputPeerSelf();
|
||||||
|
case kSinglePeerTypeEmpty:
|
||||||
|
return MTP_inputPeerEmpty();
|
||||||
|
}
|
||||||
|
Unexpected("Type in export data single peer.");
|
||||||
|
}();
|
||||||
return (file.stream.status() == QDataStream::Ok && result.validate())
|
return (file.stream.status() == QDataStream::Ok && result.validate())
|
||||||
? result
|
? result
|
||||||
: Export::Settings();
|
: Export::Settings();
|
||||||
|
|
|
@ -342,6 +342,9 @@ void Filler::addUserActions(not_null<UserData*> user) {
|
||||||
lang(lng_profile_invite_to_group),
|
lang(lng_profile_invite_to_group),
|
||||||
[user] { AddBotToGroupBoxController::Start(user); });
|
[user] { AddBotToGroupBoxController::Start(user); });
|
||||||
}
|
}
|
||||||
|
_addAction(
|
||||||
|
lang(lng_profile_export_chat),
|
||||||
|
[=] { PeerMenuExportChat(user); });
|
||||||
}
|
}
|
||||||
_addAction(
|
_addAction(
|
||||||
lang(lng_profile_delete_conversation),
|
lang(lng_profile_delete_conversation),
|
||||||
|
@ -364,6 +367,9 @@ void Filler::addChatActions(not_null<ChatData*> chat) {
|
||||||
lang(lng_profile_add_participant),
|
lang(lng_profile_add_participant),
|
||||||
[chat] { AddChatMembers(chat); });
|
[chat] { AddChatMembers(chat); });
|
||||||
}
|
}
|
||||||
|
_addAction(
|
||||||
|
lang(lng_profile_export_chat),
|
||||||
|
[=] { PeerMenuExportChat(chat); });
|
||||||
}
|
}
|
||||||
_addAction(
|
_addAction(
|
||||||
lang(lng_profile_clear_and_exit),
|
lang(lng_profile_clear_and_exit),
|
||||||
|
@ -398,6 +404,11 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) {
|
||||||
lang(lng_channel_add_members),
|
lang(lng_channel_add_members),
|
||||||
[channel] { PeerMenuAddChannelMembers(channel); });
|
[channel] { PeerMenuAddChannelMembers(channel); });
|
||||||
}
|
}
|
||||||
|
_addAction(
|
||||||
|
lang(isGroup
|
||||||
|
? lng_profile_export_chat
|
||||||
|
: lng_profile_export_channel),
|
||||||
|
[=] { PeerMenuExportChat(channel); });
|
||||||
}
|
}
|
||||||
if (channel->amIn()) {
|
if (channel->amIn()) {
|
||||||
if (isGroup && !channel->isPublic()) {
|
if (isGroup && !channel->isPublic()) {
|
||||||
|
@ -542,6 +553,10 @@ void FeedFiller::addUngroup() {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void PeerMenuExportChat(not_null<PeerData*> peer) {
|
||||||
|
Auth().data().startExport(peer);
|
||||||
|
}
|
||||||
|
|
||||||
void PeerMenuDeleteContact(not_null<UserData*> user) {
|
void PeerMenuDeleteContact(not_null<UserData*> user) {
|
||||||
auto text = lng_sure_delete_contact(
|
auto text = lng_sure_delete_contact(
|
||||||
lt_contact,
|
lt_contact,
|
||||||
|
|
|
@ -44,6 +44,7 @@ void PeerMenuAddMuteAction(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const PeerMenuCallback &addAction);
|
const PeerMenuCallback &addAction);
|
||||||
|
|
||||||
|
void PeerMenuExportChat(not_null<PeerData*> peer);
|
||||||
void PeerMenuDeleteContact(not_null<UserData*> user);
|
void PeerMenuDeleteContact(not_null<UserData*> user);
|
||||||
void PeerMenuShareContactBox(not_null<UserData*> user);
|
void PeerMenuShareContactBox(not_null<UserData*> user);
|
||||||
void PeerMenuAddContact(not_null<UserData*> user);
|
void PeerMenuAddContact(not_null<UserData*> user);
|
||||||
|
|
2
Telegram/ThirdParty/crl
vendored
2
Telegram/ThirdParty/crl
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 9bc641f2d4ab140a84aea64c7f2d4669f7633246
|
Subproject commit 527ad273b683d52c5adf5b45b73c6466aa0d0cf0
|
Loading…
Add table
Reference in a new issue