mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-20 02:10:24 +00:00
Better steps division in export.
This commit is contained in:
parent
c7aa5ed544
commit
7d4e23448e
@ -1266,7 +1266,7 @@ channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet =
|
||||
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
|
||||
channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool;
|
||||
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
|
||||
channels.getLeftChannels#90920196 = messages.Chats;
|
||||
channels.getLeftChannels#8341ecc0 offset:int = messages.Chats;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
|
@ -944,30 +944,23 @@ DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void InsertLeftDialog(
|
||||
DialogsInfo &info,
|
||||
const Chat &chat,
|
||||
Message &&message) {
|
||||
const auto projection = [](const DialogInfo &dialog) {
|
||||
return std::make_tuple(
|
||||
dialog.topMessageDate,
|
||||
dialog.topMessageId,
|
||||
BarePeerId(dialog.peerId));
|
||||
};
|
||||
const auto i = ranges::lower_bound(
|
||||
info.list,
|
||||
std::make_tuple(message.date, message.id, chat.id),
|
||||
ranges::ordered_less{},
|
||||
projection);
|
||||
|
||||
auto insert = DialogInfo();
|
||||
insert.input = chat.input;
|
||||
insert.name = chat.title;
|
||||
insert.peerId = ChatPeerId(chat.id);
|
||||
insert.topMessageDate = message.date;
|
||||
insert.topMessageId = message.id;
|
||||
insert.type = DialogTypeFromChat(chat);
|
||||
info.list.insert(i, std::move(insert));
|
||||
DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data) {
|
||||
auto result = DialogsInfo();
|
||||
data.match([&](const auto &data) { //MTPDmessages_chats &data) {
|
||||
result.list.reserve(data.vchats.v.size());
|
||||
for (const auto &single : data.vchats.v) {
|
||||
const auto chat = 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);
|
||||
result.list.push_back(std::move(info));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) {
|
||||
@ -995,6 +988,17 @@ void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) {
|
||||
}
|
||||
}
|
||||
|
||||
void FinalizeLeftChannelsInfo(DialogsInfo &info, const Settings &settings) {
|
||||
auto &list = info.list;
|
||||
const auto digits = Data::NumberToString(list.size() - 1).size();
|
||||
auto index = 0;
|
||||
for (auto &dialog : list) {
|
||||
const auto number = Data::NumberToString(++index, digits, '0');
|
||||
dialog.relativePath = "Chats/left_" + number + '/';
|
||||
dialog.onlyMyMessages = true;
|
||||
}
|
||||
}
|
||||
|
||||
MessagesSlice ParseMessagesSlice(
|
||||
const MTPVector<MTPMessage> &data,
|
||||
const MTPVector<MTPUser> &users,
|
||||
|
@ -433,12 +433,12 @@ struct DialogsInfo {
|
||||
std::vector<DialogInfo> list;
|
||||
};
|
||||
|
||||
DialogInfo::Type DialogTypeFromChat(const Chat &chat);
|
||||
|
||||
DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data);
|
||||
void InsertLeftDialog(
|
||||
DialogsInfo &info,
|
||||
const Chat &chat,
|
||||
Message &&message);
|
||||
DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data);
|
||||
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings);
|
||||
void FinalizeLeftChannelsInfo(DialogsInfo &info, const Settings &settings);
|
||||
|
||||
struct MessagesSlice {
|
||||
std::vector<Message> list;
|
||||
|
@ -63,6 +63,25 @@ LocationKey ComputeLocationKey(const Data::FileLocation &value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Settings::Type SettingsFromDialogsType(Data::DialogInfo::Type type) {
|
||||
using DialogType = Data::DialogInfo::Type;
|
||||
switch (type) {
|
||||
case DialogType::Personal:
|
||||
return Settings::Type::PersonalChats;
|
||||
case DialogType::Bot:
|
||||
return Settings::Type::BotChats;
|
||||
case DialogType::PrivateGroup:
|
||||
return Settings::Type::PrivateGroups;
|
||||
case DialogType::PublicGroup:
|
||||
return Settings::Type::PublicGroups;
|
||||
case DialogType::PrivateChannel:
|
||||
return Settings::Type::PrivateChannels;
|
||||
case DialogType::PublicChannel:
|
||||
return Settings::Type::PublicChannels;
|
||||
}
|
||||
return Settings::Type(0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class ApiWrap::LoadedFileCache {
|
||||
@ -81,6 +100,19 @@ private:
|
||||
|
||||
};
|
||||
|
||||
struct ApiWrap::StartProcess {
|
||||
FnMut<void(StartInfo)> done;
|
||||
|
||||
enum class Step {
|
||||
UserpicsCount,
|
||||
DialogsCount,
|
||||
LeftChannelsCount,
|
||||
};
|
||||
std::deque<Step> steps;
|
||||
StartInfo info;
|
||||
|
||||
};
|
||||
|
||||
struct ApiWrap::UserpicsProcess {
|
||||
FnMut<void(Data::UserpicsInfo&&)> start;
|
||||
Fn<void(Data::UserpicsSlice&&)> handleSlice;
|
||||
@ -112,30 +144,36 @@ struct ApiWrap::FileProcess {
|
||||
|
||||
};
|
||||
|
||||
struct ApiWrap::DialogsProcess {
|
||||
Data::DialogsInfo info;
|
||||
std::map<int32, Data::Chat> left;
|
||||
struct ApiWrap::LeftChannelsProcess {
|
||||
FnMut<void(Data::DialogsInfo&&)> done;
|
||||
|
||||
FnMut<void(const Data::DialogsInfo&)> start;
|
||||
Fn<void(const Data::DialogInfo&)> startOne;
|
||||
Fn<void(Data::MessagesSlice&&)> sliceOne;
|
||||
Fn<void()> finishOne;
|
||||
FnMut<void()> finish;
|
||||
Data::DialogsInfo info;
|
||||
|
||||
rpl::variable<int> count;
|
||||
int fullCount = 0;
|
||||
bool finished = false;
|
||||
|
||||
};
|
||||
|
||||
struct ApiWrap::DialogsProcess {
|
||||
FnMut<void(Data::DialogsInfo&&)> done;
|
||||
|
||||
Data::DialogsInfo info;
|
||||
|
||||
Data::TimeId offsetDate = 0;
|
||||
int32 offsetId = 0;
|
||||
MTPInputPeer offsetPeer = MTP_inputPeerEmpty();
|
||||
|
||||
struct Single;
|
||||
std::unique_ptr<Single> single;
|
||||
int singleIndex = -1;
|
||||
rpl::variable<int> count;
|
||||
|
||||
};
|
||||
|
||||
struct ApiWrap::DialogsProcess::Single {
|
||||
Single(const Data::DialogInfo &info);
|
||||
|
||||
struct ApiWrap::ChatProcess {
|
||||
Data::DialogInfo info;
|
||||
|
||||
Fn<void(Data::MessagesSlice&&)> handleSlice;
|
||||
FnMut<void()> done;
|
||||
|
||||
int32 offsetId = 1;
|
||||
|
||||
base::optional<Data::MessagesSlice> slice;
|
||||
@ -173,10 +211,6 @@ base::optional<QString> ApiWrap::LoadedFileCache::find(
|
||||
ApiWrap::FileProcess::FileProcess(const QString &path) : file(path) {
|
||||
}
|
||||
|
||||
ApiWrap::DialogsProcess::Single::Single(const Data::DialogInfo &info)
|
||||
: info(info) {
|
||||
}
|
||||
|
||||
template <typename Request>
|
||||
auto ApiWrap::mainRequest(Request &&request) {
|
||||
Expects(_takeoutId.has_value());
|
||||
@ -215,11 +249,158 @@ rpl::producer<RPCError> ApiWrap::errors() const {
|
||||
|
||||
void ApiWrap::startExport(
|
||||
const Settings &settings,
|
||||
FnMut<void()> done) {
|
||||
FnMut<void(StartInfo)> done) {
|
||||
Expects(_settings == nullptr);
|
||||
Expects(_startProcess == nullptr);
|
||||
|
||||
_settings = std::make_unique<Settings>(settings);
|
||||
startMainSession(std::move(done));
|
||||
_startProcess = std::make_unique<StartProcess>();
|
||||
_startProcess->done = std::move(done);
|
||||
|
||||
using Step = StartProcess::Step;
|
||||
if (_settings->types & Settings::Type::Userpics) {
|
||||
_startProcess->steps.push_back(Step::UserpicsCount);
|
||||
} else if (_settings->types & Settings::Type::AnyChatsMask) {
|
||||
_startProcess->steps.push_back(Step::DialogsCount);
|
||||
} else if (_settings->types & Settings::Type::GroupsChannelsMask) {
|
||||
_startProcess->steps.push_back(Step::LeftChannelsCount);
|
||||
}
|
||||
startMainSession([=] {
|
||||
sendNextStartRequest();
|
||||
});
|
||||
}
|
||||
|
||||
void ApiWrap::sendNextStartRequest() {
|
||||
Expects(_startProcess != nullptr);
|
||||
|
||||
auto &steps = _startProcess->steps;
|
||||
if (steps.empty()) {
|
||||
finishStartProcess();
|
||||
return;
|
||||
}
|
||||
const auto step = steps.front();
|
||||
steps.pop_front();
|
||||
switch (step) {
|
||||
case StartProcess::Step::UserpicsCount:
|
||||
return requestUserpicsCount();
|
||||
case StartProcess::Step::DialogsCount:
|
||||
return requestDialogsCount();
|
||||
case StartProcess::Step::LeftChannelsCount:
|
||||
return requestLeftChannelsCount();
|
||||
}
|
||||
Unexpected("Step in ApiWrap::sendNextStartRequest.");
|
||||
}
|
||||
|
||||
void ApiWrap::requestUserpicsCount() {
|
||||
Expects(_startProcess != nullptr);
|
||||
|
||||
mainRequest(MTPphotos_GetUserPhotos(
|
||||
_user,
|
||||
MTP_int(0), // offset
|
||||
MTP_long(0), // max_id
|
||||
MTP_int(0) // limit
|
||||
)).done([=](const MTPphotos_Photos &result) {
|
||||
Expects(_settings != nullptr);
|
||||
Expects(_startProcess != nullptr);
|
||||
|
||||
_startProcess->info.userpicsCount = result.match(
|
||||
[](const MTPDphotos_photos &data) {
|
||||
return int(data.vphotos.v.size());
|
||||
}, [](const MTPDphotos_photosSlice &data) {
|
||||
return data.vcount.v;
|
||||
});
|
||||
|
||||
sendNextStartRequest();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::requestDialogsCount() {
|
||||
Expects(_startProcess != nullptr);
|
||||
|
||||
mainRequest(MTPmessages_GetDialogs(
|
||||
MTP_flags(0),
|
||||
MTP_int(0), // offset_date
|
||||
MTP_int(0), // offset_id
|
||||
MTP_inputPeerEmpty(), // offset_peer
|
||||
MTP_int(1)
|
||||
)).done([=](const MTPmessages_Dialogs &result) {
|
||||
Expects(_settings != nullptr);
|
||||
Expects(_startProcess != nullptr);
|
||||
|
||||
_startProcess->info.dialogsCount = result.match(
|
||||
[](const MTPDmessages_dialogs &data) {
|
||||
return int(data.vdialogs.v.size());
|
||||
}, [](const MTPDmessages_dialogsSlice &data) {
|
||||
return data.vcount.v;
|
||||
});
|
||||
|
||||
sendNextStartRequest();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::requestLeftChannelsCount() {
|
||||
Expects(_startProcess != nullptr);
|
||||
Expects(_leftChannelsProcess == nullptr);
|
||||
|
||||
_leftChannelsProcess = std::make_unique<LeftChannelsProcess>();
|
||||
requestLeftChannelsSliceGeneric([=] {
|
||||
Expects(_startProcess != nullptr);
|
||||
Expects(_leftChannelsProcess != nullptr);
|
||||
|
||||
_startProcess->info.leftChannelsCount
|
||||
= _leftChannelsProcess->fullCount;
|
||||
sendNextStartRequest();
|
||||
});
|
||||
}
|
||||
|
||||
void ApiWrap::finishStartProcess() {
|
||||
Expects(_startProcess != nullptr);
|
||||
|
||||
const auto process = base::take(_startProcess);
|
||||
process->done(process->info);
|
||||
}
|
||||
|
||||
void ApiWrap::requestLeftChannelsList(
|
||||
FnMut<void(Data::DialogsInfo&&)> done) {
|
||||
Expects(_leftChannelsProcess != nullptr);
|
||||
|
||||
_leftChannelsProcess->done = std::move(done);
|
||||
requestLeftChannelsSlice();
|
||||
}
|
||||
|
||||
void ApiWrap::requestLeftChannelsSlice() {
|
||||
requestLeftChannelsSliceGeneric([=] {
|
||||
Expects(_leftChannelsProcess != nullptr);
|
||||
|
||||
if (_leftChannelsProcess->finished) {
|
||||
const auto process = base::take(_leftChannelsProcess);
|
||||
Data::FinalizeLeftChannelsInfo(process->info, *_settings);
|
||||
process->done(std::move(process->info));
|
||||
} else {
|
||||
requestLeftChannelsSlice();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<int> ApiWrap::leftChannelsLoadedCount() const {
|
||||
Expects(_leftChannelsProcess != nullptr);
|
||||
|
||||
return _leftChannelsProcess->count.value();
|
||||
}
|
||||
|
||||
void ApiWrap::requestDialogsList(FnMut<void(Data::DialogsInfo&&)> done) {
|
||||
Expects(_dialogsProcess == nullptr);
|
||||
|
||||
_dialogsProcess = std::make_unique<DialogsProcess>();
|
||||
_dialogsProcess->done = std::move(done);
|
||||
|
||||
requestDialogsSlice();
|
||||
}
|
||||
|
||||
rpl::producer<int> ApiWrap::dialogsLoadedCount() const {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
|
||||
return _dialogsProcess->count.value();
|
||||
}
|
||||
|
||||
void ApiWrap::startMainSession(FnMut<void()> done) {
|
||||
@ -287,8 +468,8 @@ void ApiWrap::requestUserpics(
|
||||
|
||||
mainRequest(MTPphotos_GetUserPhotos(
|
||||
_user,
|
||||
MTP_int(0),
|
||||
MTP_long(0),
|
||||
MTP_int(0), // offset
|
||||
MTP_long(0), // max_id
|
||||
MTP_int(kUserpicsSliceLimit)
|
||||
)).done([=](const MTPphotos_Photos &result) mutable {
|
||||
Expects(_userpicsProcess != nullptr);
|
||||
@ -405,22 +586,18 @@ void ApiWrap::requestSessions(FnMut<void(Data::SessionsList&&)> done) {
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::requestDialogs(
|
||||
FnMut<void(const Data::DialogsInfo&)> start,
|
||||
Fn<void(const Data::DialogInfo&)> startOne,
|
||||
Fn<void(Data::MessagesSlice&&)> sliceOne,
|
||||
Fn<void()> finishOne,
|
||||
FnMut<void()> finish) {
|
||||
Expects(_dialogsProcess == nullptr);
|
||||
void ApiWrap::requestMessages(
|
||||
const Data::DialogInfo &info,
|
||||
Fn<void(Data::MessagesSlice&&)> slice,
|
||||
FnMut<void()> done) {
|
||||
Expects(_chatProcess == nullptr);
|
||||
|
||||
_dialogsProcess = std::make_unique<DialogsProcess>();
|
||||
_dialogsProcess->start = std::move(start);
|
||||
_dialogsProcess->startOne = std::move(startOne);
|
||||
_dialogsProcess->sliceOne = std::move(sliceOne);
|
||||
_dialogsProcess->finishOne = std::move(finishOne);
|
||||
_dialogsProcess->finish = std::move(finish);
|
||||
_chatProcess = std::make_unique<ChatProcess>();
|
||||
_chatProcess->info = info;
|
||||
_chatProcess->handleSlice = std::move(slice);
|
||||
_chatProcess->done = std::move(done);
|
||||
|
||||
requestDialogsSlice();
|
||||
requestMessagesSlice();
|
||||
}
|
||||
|
||||
void ApiWrap::requestDialogsSlice() {
|
||||
@ -432,7 +609,7 @@ void ApiWrap::requestDialogsSlice() {
|
||||
MTP_int(_dialogsProcess->offsetId),
|
||||
_dialogsProcess->offsetPeer,
|
||||
MTP_int(kChatsSliceLimit)
|
||||
)).done([=](const MTPmessages_Dialogs &result) mutable {
|
||||
)).done([=](const MTPmessages_Dialogs &result) {
|
||||
const auto finished = result.match(
|
||||
[](const MTPDmessages_dialogs &data) {
|
||||
return true;
|
||||
@ -442,7 +619,7 @@ void ApiWrap::requestDialogsSlice() {
|
||||
|
||||
auto info = Data::ParseDialogsInfo(result);
|
||||
if (finished || info.list.empty()) {
|
||||
requestLeftChannels();
|
||||
finishDialogsList();
|
||||
} else {
|
||||
const auto &last = info.list.back();
|
||||
_dialogsProcess->offsetId = last.topMessageId;
|
||||
@ -458,139 +635,86 @@ void ApiWrap::requestDialogsSlice() {
|
||||
|
||||
void ApiWrap::appendDialogsSlice(Data::DialogsInfo &&info) {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_settings != nullptr);
|
||||
|
||||
const auto types = _settings->types;
|
||||
auto filtered = ranges::view::all(
|
||||
info.list
|
||||
) | ranges::view::filter([&](const Data::DialogInfo &info) {
|
||||
const auto bit = [&] {
|
||||
using DialogType = Data::DialogInfo::Type;
|
||||
switch (info.type) {
|
||||
case DialogType::Personal:
|
||||
return Settings::Type::PersonalChats;
|
||||
case DialogType::Bot:
|
||||
return Settings::Type::BotChats;
|
||||
case DialogType::PrivateGroup:
|
||||
return Settings::Type::PrivateGroups;
|
||||
case DialogType::PublicGroup:
|
||||
return Settings::Type::PublicGroups;
|
||||
case DialogType::PrivateChannel:
|
||||
return Settings::Type::PrivateChannels;
|
||||
case DialogType::PublicChannel:
|
||||
return Settings::Type::PublicChannels;
|
||||
}
|
||||
return Settings::Type(0);
|
||||
}();
|
||||
return (types & bit) != 0;
|
||||
});
|
||||
auto &list = _dialogsProcess->info.list;
|
||||
list.reserve(list.size());
|
||||
for (auto &info : filtered) {
|
||||
list.push_back(std::move(info));
|
||||
}
|
||||
appendChatsSlice(_dialogsProcess->info, std::move(info));
|
||||
}
|
||||
|
||||
void ApiWrap::requestLeftChannels() {
|
||||
void ApiWrap::finishDialogsList() {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
|
||||
mainRequest(MTPchannels_GetLeftChannels(
|
||||
)).done([=](const MTPmessages_Chats &result) mutable {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
const auto process = base::take(_dialogsProcess);
|
||||
|
||||
const auto finished = result.match(
|
||||
ranges::reverse(process->info.list);
|
||||
Data::FinalizeDialogsInfo(process->info, *_settings);
|
||||
|
||||
process->done(std::move(process->info));
|
||||
}
|
||||
|
||||
void ApiWrap::requestLeftChannelsSliceGeneric(FnMut<void()> done) {
|
||||
Expects(_leftChannelsProcess != nullptr);
|
||||
|
||||
mainRequest(MTPchannels_GetLeftChannels(
|
||||
MTP_int(_leftChannelsProcess->info.list.size())
|
||||
)).done([=, done = std::move(done)](
|
||||
const MTPmessages_Chats &result) mutable {
|
||||
Expects(_leftChannelsProcess != nullptr);
|
||||
|
||||
appendLeftChannelsSlice(Data::ParseLeftChannelsInfo(result));
|
||||
|
||||
const auto process = _leftChannelsProcess.get();
|
||||
process->fullCount = result.match(
|
||||
[](const MTPDmessages_chats &data) {
|
||||
return int(data.vchats.v.size());
|
||||
}, [](const MTPDmessages_chatsSlice &data) {
|
||||
return data.vcount.v;
|
||||
});
|
||||
|
||||
process->finished = result.match(
|
||||
[](const MTPDmessages_chats &data) {
|
||||
return true;
|
||||
}, [](const MTPDmessages_chatsSlice &data) {
|
||||
return data.vchats.v.isEmpty();
|
||||
});
|
||||
|
||||
_dialogsProcess->left = Data::ParseChatsList(*result.match(
|
||||
[](const auto &data) {
|
||||
return &data.vchats;
|
||||
}));
|
||||
requestLeftDialog();
|
||||
process->count = process->info.list.size();
|
||||
|
||||
done();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::requestLeftDialog() {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
void ApiWrap::appendLeftChannelsSlice(Data::DialogsInfo &&info) {
|
||||
Expects(_leftChannelsProcess != nullptr);
|
||||
|
||||
auto &left = _dialogsProcess->left;
|
||||
if (true || left.empty()) { // #TODO export
|
||||
finishDialogsList();
|
||||
return;
|
||||
}
|
||||
const auto key = std::move(*left.begin());
|
||||
left.erase(key.first);
|
||||
|
||||
mainRequest(MTPmessages_Search(
|
||||
MTP_flags(MTPmessages_Search::Flag::f_from_id),
|
||||
key.second.input,
|
||||
MTP_string(""), // query
|
||||
_user,
|
||||
MTP_inputMessagesFilterEmpty(),
|
||||
MTP_int(0), // min_date
|
||||
MTP_int(0), // max_date
|
||||
MTP_int(0), // offset_id
|
||||
MTP_int(0), // add_offset
|
||||
MTP_int(1), // limit
|
||||
MTP_int(0), // max_id
|
||||
MTP_int(0), // min_id
|
||||
MTP_int(0) // hash
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
|
||||
result.match([=](const MTPDmessages_messagesNotModified &data) {
|
||||
error("Unexpected messagesNotModified received.");
|
||||
}, [=](const auto &data) {
|
||||
auto messages = Data::ParseMessagesList(
|
||||
data.vmessages,
|
||||
QString());
|
||||
if (!messages.empty() && messages.begin()->second.date > 0) {
|
||||
Data::InsertLeftDialog(
|
||||
_dialogsProcess->info,
|
||||
key.second,
|
||||
std::move(messages.begin()->second));
|
||||
}
|
||||
requestLeftDialog();
|
||||
});
|
||||
}).send();
|
||||
appendChatsSlice(_leftChannelsProcess->info, std::move(info));
|
||||
}
|
||||
|
||||
void ApiWrap::finishDialogsList() {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
void ApiWrap::appendChatsSlice(
|
||||
Data::DialogsInfo &to,
|
||||
Data::DialogsInfo &&info) {
|
||||
Expects(_settings != nullptr);
|
||||
|
||||
ranges::reverse(_dialogsProcess->info.list);
|
||||
Data::FinalizeDialogsInfo(_dialogsProcess->info, *_settings);
|
||||
|
||||
_dialogsProcess->start(_dialogsProcess->info);
|
||||
requestNextDialog();
|
||||
}
|
||||
|
||||
void ApiWrap::requestNextDialog() {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_dialogsProcess->single == nullptr);
|
||||
|
||||
const auto index = ++_dialogsProcess->singleIndex;
|
||||
if (index < _dialogsProcess->info.list.size()) {
|
||||
const auto &one = _dialogsProcess->info.list[index];
|
||||
_dialogsProcess->single = std::make_unique<DialogsProcess::Single>(one);
|
||||
_dialogsProcess->startOne(one);
|
||||
requestMessagesSlice();
|
||||
return;
|
||||
const auto types = _settings->types;
|
||||
auto filtered = ranges::view::all(
|
||||
info.list
|
||||
) | ranges::view::filter([&](const Data::DialogInfo &info) {
|
||||
return (types & SettingsFromDialogsType(info.type)) != 0;
|
||||
});
|
||||
auto &list = to.list;
|
||||
if (list.empty()) {
|
||||
list = filtered | ranges::to_vector;
|
||||
} else {
|
||||
list.reserve(list.size() + info.list.size());
|
||||
for (auto &info : filtered) {
|
||||
list.push_back(std::move(info));
|
||||
}
|
||||
}
|
||||
finishDialogs();
|
||||
}
|
||||
|
||||
void ApiWrap::requestMessagesSlice() {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_dialogsProcess->single != nullptr);
|
||||
|
||||
const auto process = _dialogsProcess->single.get();
|
||||
Expects(_chatProcess != nullptr);
|
||||
|
||||
// #TODO export
|
||||
if (process->info.input.match([](const MTPDinputPeerUser &value) {
|
||||
if (_chatProcess->info.input.match([](const MTPDinputPeerUser &value) {
|
||||
return !value.vaccess_hash.v;
|
||||
}, [](const auto &data) { return false; })) {
|
||||
finishMessages();
|
||||
@ -598,33 +722,31 @@ void ApiWrap::requestMessagesSlice() {
|
||||
}
|
||||
|
||||
auto handleResult = [=](const MTPmessages_Messages &result) mutable {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_dialogsProcess->single != nullptr);
|
||||
Expects(_chatProcess != nullptr);
|
||||
|
||||
const auto process = _dialogsProcess->single.get();
|
||||
result.match([&](const MTPDmessages_messagesNotModified &data) {
|
||||
error("Unexpected messagesNotModified received.");
|
||||
}, [&](const auto &data) {
|
||||
if constexpr (MTPDmessages_messages::Is<decltype(data)>()) {
|
||||
process->lastSlice = true;
|
||||
_chatProcess->lastSlice = true;
|
||||
}
|
||||
loadMessagesFiles(Data::ParseMessagesSlice(
|
||||
data.vmessages,
|
||||
data.vusers,
|
||||
data.vchats,
|
||||
process->info.relativePath));
|
||||
_chatProcess->info.relativePath));
|
||||
});
|
||||
};
|
||||
if (process->info.onlyMyMessages) {
|
||||
if (_chatProcess->info.onlyMyMessages) {
|
||||
mainRequest(MTPmessages_Search(
|
||||
MTP_flags(MTPmessages_Search::Flag::f_from_id),
|
||||
process->info.input,
|
||||
_chatProcess->info.input,
|
||||
MTP_string(""), // query
|
||||
_user,
|
||||
MTP_inputMessagesFilterEmpty(),
|
||||
MTP_int(0), // min_date
|
||||
MTP_int(0), // max_date
|
||||
MTP_int(process->offsetId),
|
||||
MTP_int(_chatProcess->offsetId),
|
||||
MTP_int(-kMessagesSliceLimit),
|
||||
MTP_int(kMessagesSliceLimit),
|
||||
MTP_int(0), // max_id
|
||||
@ -633,8 +755,8 @@ void ApiWrap::requestMessagesSlice() {
|
||||
)).done(std::move(handleResult)).send();
|
||||
} else {
|
||||
mainRequest(MTPmessages_GetHistory(
|
||||
process->info.input,
|
||||
MTP_int(process->offsetId),
|
||||
_chatProcess->info.input,
|
||||
MTP_int(_chatProcess->offsetId),
|
||||
MTP_int(0), // offset_date
|
||||
MTP_int(-kMessagesSliceLimit),
|
||||
MTP_int(kMessagesSliceLimit),
|
||||
@ -646,29 +768,25 @@ void ApiWrap::requestMessagesSlice() {
|
||||
}
|
||||
|
||||
void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_dialogsProcess->single != nullptr);
|
||||
Expects(!_dialogsProcess->single->slice.has_value());
|
||||
Expects(_chatProcess != nullptr);
|
||||
Expects(!_chatProcess->slice.has_value());
|
||||
|
||||
const auto process = _dialogsProcess->single.get();
|
||||
if (slice.list.empty()) {
|
||||
process->lastSlice = true;
|
||||
_chatProcess->lastSlice = true;
|
||||
}
|
||||
process->slice = std::move(slice);
|
||||
process->fileIndex = -1;
|
||||
_chatProcess->slice = std::move(slice);
|
||||
_chatProcess->fileIndex = -1;
|
||||
|
||||
loadNextMessageFile();
|
||||
}
|
||||
|
||||
void ApiWrap::loadNextMessageFile() {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_dialogsProcess->single != nullptr);
|
||||
Expects(_dialogsProcess->single->slice.has_value());
|
||||
Expects(_chatProcess != nullptr);
|
||||
Expects(_chatProcess->slice.has_value());
|
||||
|
||||
const auto process = _dialogsProcess->single.get();
|
||||
auto &list = process->slice->list;
|
||||
auto &list = _chatProcess->slice->list;
|
||||
while (true) {
|
||||
const auto index = ++process->fileIndex;
|
||||
const auto index = ++_chatProcess->fileIndex;
|
||||
if (index >= list.size()) {
|
||||
break;
|
||||
}
|
||||
@ -684,17 +802,15 @@ void ApiWrap::loadNextMessageFile() {
|
||||
}
|
||||
|
||||
void ApiWrap::finishMessagesSlice() {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_dialogsProcess->single != nullptr);
|
||||
Expects(_dialogsProcess->single->slice.has_value());
|
||||
Expects(_chatProcess != nullptr);
|
||||
Expects(_chatProcess->slice.has_value());
|
||||
|
||||
const auto process = _dialogsProcess->single.get();
|
||||
auto slice = *base::take(process->slice);
|
||||
auto slice = *base::take(_chatProcess->slice);
|
||||
if (!slice.list.empty()) {
|
||||
process->offsetId = slice.list.back().id + 1;
|
||||
_dialogsProcess->sliceOne(std::move(slice));
|
||||
_chatProcess->offsetId = slice.list.back().id + 1;
|
||||
_chatProcess->handleSlice(std::move(slice));
|
||||
}
|
||||
if (process->lastSlice) {
|
||||
if (_chatProcess->lastSlice) {
|
||||
finishMessages();
|
||||
} else {
|
||||
requestMessagesSlice();
|
||||
@ -702,35 +818,22 @@ void ApiWrap::finishMessagesSlice() {
|
||||
}
|
||||
|
||||
void ApiWrap::loadMessageFileDone(const QString &relativePath) {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_dialogsProcess->single != nullptr);
|
||||
Expects(_dialogsProcess->single->slice.has_value());
|
||||
Expects((_dialogsProcess->single->fileIndex >= 0)
|
||||
&& (_dialogsProcess->single->fileIndex
|
||||
< _dialogsProcess->single->slice->list.size()));
|
||||
Expects(_chatProcess != nullptr);
|
||||
Expects(_chatProcess->slice.has_value());
|
||||
Expects((_chatProcess->fileIndex >= 0)
|
||||
&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
|
||||
|
||||
const auto process = _dialogsProcess->single.get();
|
||||
const auto index = process->fileIndex;
|
||||
process->slice->list[index].file().relativePath = relativePath;
|
||||
const auto index = _chatProcess->fileIndex;
|
||||
_chatProcess->slice->list[index].file().relativePath = relativePath;
|
||||
loadNextMessageFile();
|
||||
}
|
||||
|
||||
void ApiWrap::finishMessages() {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_dialogsProcess->single != nullptr);
|
||||
Expects(!_dialogsProcess->single->slice.has_value());
|
||||
Expects(_chatProcess != nullptr);
|
||||
Expects(!_chatProcess->slice.has_value());
|
||||
|
||||
_dialogsProcess->single = nullptr;
|
||||
_dialogsProcess->finishOne();
|
||||
|
||||
requestNextDialog();
|
||||
}
|
||||
|
||||
void ApiWrap::finishDialogs() {
|
||||
Expects(_dialogsProcess != nullptr);
|
||||
Expects(_dialogsProcess->single == nullptr);
|
||||
|
||||
base::take(_dialogsProcess)->finish();
|
||||
const auto process = base::take(_chatProcess);
|
||||
process->done();
|
||||
}
|
||||
|
||||
bool ApiWrap::processFileLoad(
|
||||
@ -800,7 +903,9 @@ bool ApiWrap::writePreloadedFile(Data::File &file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApiWrap::loadFile(const Data::File &file, FnMut<void(QString)> done) {
|
||||
void ApiWrap::loadFile(
|
||||
const Data::File &file,
|
||||
FnMut<void(QString)> done) {
|
||||
Expects(_fileProcess == nullptr);
|
||||
Expects(file.location.dcId != 0);
|
||||
|
||||
|
@ -33,9 +33,20 @@ public:
|
||||
|
||||
rpl::producer<RPCError> errors() const;
|
||||
|
||||
struct StartInfo {
|
||||
int userpicsCount = 0;
|
||||
int dialogsCount = 0;
|
||||
int leftChannelsCount = 0;
|
||||
};
|
||||
void startExport(
|
||||
const Settings &settings,
|
||||
FnMut<void()> done);
|
||||
FnMut<void(StartInfo)> done);
|
||||
|
||||
void requestLeftChannelsList(FnMut<void(Data::DialogsInfo&&)> done);
|
||||
rpl::producer<int> leftChannelsLoadedCount() const;
|
||||
|
||||
void requestDialogsList(FnMut<void(Data::DialogsInfo&&)> done);
|
||||
rpl::producer<int> dialogsLoadedCount() const;
|
||||
|
||||
void requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done);
|
||||
|
||||
@ -48,22 +59,28 @@ public:
|
||||
|
||||
void requestSessions(FnMut<void(Data::SessionsList&&)> done);
|
||||
|
||||
void requestDialogs(
|
||||
FnMut<void(const Data::DialogsInfo&)> start,
|
||||
Fn<void(const Data::DialogInfo&)> startOne,
|
||||
Fn<void(Data::MessagesSlice&&)> sliceOne,
|
||||
Fn<void()> finishOne,
|
||||
FnMut<void()> finish);
|
||||
void requestMessages(
|
||||
const Data::DialogInfo &info,
|
||||
Fn<void(Data::MessagesSlice&&)> slice,
|
||||
FnMut<void()> done);
|
||||
|
||||
~ApiWrap();
|
||||
|
||||
private:
|
||||
class LoadedFileCache;
|
||||
struct StartProcess;
|
||||
struct UserpicsProcess;
|
||||
struct FileProcess;
|
||||
struct LeftChannelsProcess;
|
||||
struct DialogsProcess;
|
||||
class LoadedFileCache;
|
||||
struct ChatProcess;
|
||||
|
||||
void startMainSession(FnMut<void()> done);
|
||||
void sendNextStartRequest();
|
||||
void requestUserpicsCount();
|
||||
void requestDialogsCount();
|
||||
void requestLeftChannelsCount();
|
||||
void finishStartProcess();
|
||||
|
||||
void handleUserpicsSlice(const MTPphotos_Photos &result);
|
||||
void loadUserpicsFiles(Data::UserpicsSlice &&slice);
|
||||
@ -73,18 +90,20 @@ private:
|
||||
|
||||
void requestDialogsSlice();
|
||||
void appendDialogsSlice(Data::DialogsInfo &&info);
|
||||
void requestLeftChannels();
|
||||
void requestLeftDialog();
|
||||
void finishDialogsList();
|
||||
|
||||
void requestNextDialog();
|
||||
void requestLeftChannelsSliceGeneric(FnMut<void()> done);
|
||||
void requestLeftChannelsSlice();
|
||||
void appendLeftChannelsSlice(Data::DialogsInfo &&info);
|
||||
|
||||
void appendChatsSlice(Data::DialogsInfo &to, Data::DialogsInfo &&info);
|
||||
|
||||
void requestMessagesSlice();
|
||||
void loadMessagesFiles(Data::MessagesSlice &&slice);
|
||||
void loadNextMessageFile();
|
||||
void loadMessageFileDone(const QString &relativePath);
|
||||
void finishMessagesSlice();
|
||||
void finishMessages();
|
||||
void finishDialogs();
|
||||
|
||||
bool processFileLoad(
|
||||
Data::File &file,
|
||||
@ -93,7 +112,9 @@ private:
|
||||
std::unique_ptr<FileProcess> prepareFileProcess(
|
||||
const Data::File &file) const;
|
||||
bool writePreloadedFile(Data::File &file);
|
||||
void loadFile(const Data::File &file, FnMut<void(QString)> done);
|
||||
void loadFile(
|
||||
const Data::File &file,
|
||||
FnMut<void(QString)> done);
|
||||
void loadFilePart();
|
||||
void filePartDone(int offset, const MTPupload_File &result);
|
||||
|
||||
@ -113,10 +134,13 @@ private:
|
||||
std::unique_ptr<Settings> _settings;
|
||||
MTPInputUser _user = MTP_inputUserSelf();
|
||||
|
||||
std::unique_ptr<StartProcess> _startProcess;
|
||||
std::unique_ptr<LoadedFileCache> _fileCache;
|
||||
std::unique_ptr<UserpicsProcess> _userpicsProcess;
|
||||
std::unique_ptr<FileProcess> _fileProcess;
|
||||
std::unique_ptr<LeftChannelsProcess> _leftChannelsProcess;
|
||||
std::unique_ptr<DialogsProcess> _dialogsProcess;
|
||||
std::unique_ptr<ChatProcess> _chatProcess;
|
||||
|
||||
rpl::event_stream<RPCError> _errors;
|
||||
|
||||
|
@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace Export {
|
||||
|
||||
auto kNullStateCallback = [](ProcessingState&) {};
|
||||
|
||||
class Controller {
|
||||
public:
|
||||
Controller(crl::weak_on_queue<Controller> weak);
|
||||
@ -21,11 +23,11 @@ public:
|
||||
rpl::producer<State> state() const;
|
||||
|
||||
// Password step.
|
||||
void submitPassword(const QString &password);
|
||||
void requestPasswordRecover();
|
||||
rpl::producer<PasswordUpdate> passwordUpdate() const;
|
||||
void reloadPasswordState();
|
||||
void cancelUnconfirmedPassword();
|
||||
//void submitPassword(const QString &password);
|
||||
//void requestPasswordRecover();
|
||||
//rpl::producer<PasswordUpdate> passwordUpdate() const;
|
||||
//void reloadPasswordState();
|
||||
//void cancelUnconfirmedPassword();
|
||||
|
||||
// Processing step.
|
||||
void startExport(const Settings &settings);
|
||||
@ -37,30 +39,56 @@ private:
|
||||
void ioError(const QString &path);
|
||||
void setFinishedState();
|
||||
|
||||
void requestPasswordState();
|
||||
void passwordStateDone(const MTPaccount_Password &password);
|
||||
//void requestPasswordState();
|
||||
//void passwordStateDone(const MTPaccount_Password &password);
|
||||
|
||||
void fillExportSteps();
|
||||
void fillSubstepsInSteps(const ApiWrap::StartInfo &info);
|
||||
void exportNext();
|
||||
void initialize();
|
||||
void collectLeftChannels();
|
||||
void collectDialogsList();
|
||||
void exportPersonalInfo();
|
||||
void exportUserpics();
|
||||
void exportContacts();
|
||||
void exportSessions();
|
||||
void exportDialogs();
|
||||
void exportNextDialog();
|
||||
void exportLeftChannels();
|
||||
void exportNextLeftChannel();
|
||||
|
||||
template <typename Callback = const decltype(kNullStateCallback) &>
|
||||
ProcessingState prepareState(
|
||||
Step step,
|
||||
Callback &&callback = kNullStateCallback) const;
|
||||
ProcessingState stateInitializing() const;
|
||||
ProcessingState stateLeftChannelsList(int processed) const;
|
||||
ProcessingState stateDialogsList(int processed) const;
|
||||
ProcessingState statePersonalInfo() const;
|
||||
ProcessingState stateUserpics() const;
|
||||
ProcessingState stateContacts() const;
|
||||
ProcessingState stateSessions() const;
|
||||
ProcessingState stateLeftChannels() const;
|
||||
ProcessingState stateDialogs() const;
|
||||
|
||||
int substepsInStep(Step step) const;
|
||||
|
||||
bool normalizePath();
|
||||
|
||||
ApiWrap _api;
|
||||
Settings _settings;
|
||||
Data::DialogsInfo _leftChannelsInfo;
|
||||
Data::DialogsInfo _dialogsInfo;
|
||||
int _leftChannelIndex = -1;
|
||||
int _dialogIndex = -1;
|
||||
|
||||
// rpl::variable<State> fails to compile in MSVC :(
|
||||
State _state;
|
||||
rpl::event_stream<State> _stateChanges;
|
||||
|
||||
mtpRequestId _passwordRequestId = 0;
|
||||
std::vector<int> _substepsInStep;
|
||||
|
||||
std::unique_ptr<Output::AbstractWriter> _writer;
|
||||
std::vector<ProcessingState::Step> _steps;
|
||||
std::vector<Step> _steps;
|
||||
int _stepIndex = -1;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
@ -102,49 +130,49 @@ void Controller::ioError(const QString &path) {
|
||||
setState(ErrorState{ ErrorState::Type::IO, base::none, path });
|
||||
}
|
||||
|
||||
void Controller::submitPassword(const QString &password) {
|
||||
|
||||
}
|
||||
|
||||
void Controller::requestPasswordRecover() {
|
||||
|
||||
}
|
||||
|
||||
rpl::producer<PasswordUpdate> Controller::passwordUpdate() const {
|
||||
return rpl::never<PasswordUpdate>();
|
||||
}
|
||||
|
||||
void Controller::reloadPasswordState() {
|
||||
//_mtp.request(base::take(_passwordRequestId)).cancel();
|
||||
requestPasswordState();
|
||||
}
|
||||
|
||||
void Controller::requestPasswordState() {
|
||||
if (_passwordRequestId) {
|
||||
return;
|
||||
}
|
||||
//_passwordRequestId = _mtp.request(MTPaccount_GetPassword(
|
||||
//)).done([=](const MTPaccount_Password &result) {
|
||||
// _passwordRequestId = 0;
|
||||
// passwordStateDone(result);
|
||||
//}).fail([=](const RPCError &error) {
|
||||
// apiError(error);
|
||||
//}).send();
|
||||
}
|
||||
|
||||
void Controller::passwordStateDone(const MTPaccount_Password &result) {
|
||||
auto state = PasswordCheckState();
|
||||
state.checked = false;
|
||||
state.requesting = false;
|
||||
state.hasPassword;
|
||||
state.hint;
|
||||
state.unconfirmedPattern;
|
||||
setState(std::move(state));
|
||||
}
|
||||
|
||||
void Controller::cancelUnconfirmedPassword() {
|
||||
|
||||
}
|
||||
//void Controller::submitPassword(const QString &password) {
|
||||
//
|
||||
//}
|
||||
//
|
||||
//void Controller::requestPasswordRecover() {
|
||||
//
|
||||
//}
|
||||
//
|
||||
//rpl::producer<PasswordUpdate> Controller::passwordUpdate() const {
|
||||
// return rpl::never<PasswordUpdate>();
|
||||
//}
|
||||
//
|
||||
//void Controller::reloadPasswordState() {
|
||||
// //_mtp.request(base::take(_passwordRequestId)).cancel();
|
||||
// requestPasswordState();
|
||||
//}
|
||||
//
|
||||
//void Controller::requestPasswordState() {
|
||||
// if (_passwordRequestId) {
|
||||
// return;
|
||||
// }
|
||||
// //_passwordRequestId = _mtp.request(MTPaccount_GetPassword(
|
||||
// //)).done([=](const MTPaccount_Password &result) {
|
||||
// // _passwordRequestId = 0;
|
||||
// // passwordStateDone(result);
|
||||
// //}).fail([=](const RPCError &error) {
|
||||
// // apiError(error);
|
||||
// //}).send();
|
||||
//}
|
||||
//
|
||||
//void Controller::passwordStateDone(const MTPaccount_Password &result) {
|
||||
// auto state = PasswordCheckState();
|
||||
// state.checked = false;
|
||||
// state.requesting = false;
|
||||
// state.hasPassword;
|
||||
// state.hint;
|
||||
// state.unconfirmedPattern;
|
||||
// setState(std::move(state));
|
||||
//}
|
||||
//
|
||||
//void Controller::cancelUnconfirmedPassword() {
|
||||
//
|
||||
//}
|
||||
|
||||
void Controller::startExport(const Settings &settings) {
|
||||
if (!_settings.path.isEmpty()) {
|
||||
@ -158,9 +186,7 @@ void Controller::startExport(const Settings &settings) {
|
||||
}
|
||||
_writer = Output::CreateWriter(_settings.format);
|
||||
fillExportSteps();
|
||||
_api.startExport(_settings, [=] {
|
||||
exportNext();
|
||||
});
|
||||
exportNext();
|
||||
}
|
||||
|
||||
bool Controller::normalizePath() {
|
||||
@ -195,6 +221,13 @@ bool Controller::normalizePath() {
|
||||
|
||||
void Controller::fillExportSteps() {
|
||||
using Type = Settings::Type;
|
||||
_steps.push_back(Step::Initializing);
|
||||
if (_settings.types & Type::GroupsChannelsMask) {
|
||||
_steps.push_back(Step::LeftChannels);
|
||||
}
|
||||
if (_settings.types & Type::AnyChatsMask) {
|
||||
_steps.push_back(Step::DialogsList);
|
||||
}
|
||||
if (_settings.types & Type::PersonalInfo) {
|
||||
_steps.push_back(Step::PersonalInfo);
|
||||
}
|
||||
@ -207,17 +240,46 @@ void Controller::fillExportSteps() {
|
||||
if (_settings.types & Type::Sessions) {
|
||||
_steps.push_back(Step::Sessions);
|
||||
}
|
||||
const auto dialogTypes = Type::PersonalChats
|
||||
| Type::BotChats
|
||||
| Type::PrivateGroups
|
||||
| Type::PublicGroups
|
||||
| Type::PrivateChannels
|
||||
| Type::PublicChannels;
|
||||
if (_settings.types & dialogTypes) {
|
||||
if (_settings.types & Type::AnyChatsMask) {
|
||||
_steps.push_back(Step::Dialogs);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::fillSubstepsInSteps(const ApiWrap::StartInfo &info) {
|
||||
const auto push = [&](Step step, int count) {
|
||||
const auto index = static_cast<int>(step);
|
||||
if (index >= _substepsInStep.size()) {
|
||||
_substepsInStep.resize(index + 1, 0);
|
||||
}
|
||||
_substepsInStep[index] = count;
|
||||
};
|
||||
push(Step::Initializing, 1);
|
||||
if (_settings.types & Settings::Type::GroupsChannelsMask) {
|
||||
push(Step::LeftChannelsList, 1);
|
||||
}
|
||||
if (_settings.types & Settings::Type::AnyChatsMask) {
|
||||
push(Step::DialogsList, 1);
|
||||
}
|
||||
if (_settings.types & Settings::Type::PersonalInfo) {
|
||||
push(Step::PersonalInfo, 1);
|
||||
}
|
||||
if (_settings.types & Settings::Type::Userpics) {
|
||||
push(Step::Userpics, info.userpicsCount);
|
||||
}
|
||||
if (_settings.types & Settings::Type::Contacts) {
|
||||
push(Step::Contacts, 1);
|
||||
}
|
||||
if (_settings.types & Settings::Type::Sessions) {
|
||||
push(Step::Sessions, 1);
|
||||
}
|
||||
if (_settings.types & Settings::Type::GroupsChannelsMask) {
|
||||
push(Step::LeftChannels, info.leftChannelsCount);
|
||||
}
|
||||
if (_settings.types & Settings::Type::AnyChatsMask) {
|
||||
push(Step::Dialogs, info.dialogsCount);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::exportNext() {
|
||||
if (!++_stepIndex) {
|
||||
_writer->start(_settings);
|
||||
@ -229,15 +291,52 @@ void Controller::exportNext() {
|
||||
}
|
||||
const auto step = _steps[_stepIndex];
|
||||
switch (step) {
|
||||
case Step::Initializing: return initialize();
|
||||
case Step::LeftChannelsList: return collectLeftChannels();
|
||||
case Step::DialogsList: return collectDialogsList();
|
||||
case Step::PersonalInfo: return exportPersonalInfo();
|
||||
case Step::Userpics: return exportUserpics();
|
||||
case Step::Contacts: return exportContacts();
|
||||
case Step::Sessions: return exportSessions();
|
||||
case Step::LeftChannels: return exportLeftChannels();
|
||||
case Step::Dialogs: return exportDialogs();
|
||||
}
|
||||
Unexpected("Step in Controller::exportNext.");
|
||||
}
|
||||
|
||||
void Controller::initialize() {
|
||||
setState(stateInitializing());
|
||||
|
||||
_api.startExport(_settings, [=](ApiWrap::StartInfo info) {
|
||||
fillSubstepsInSteps(info);
|
||||
exportNext();
|
||||
});
|
||||
}
|
||||
|
||||
void Controller::collectLeftChannels() {
|
||||
_api.requestLeftChannelsList([=](Data::DialogsInfo &&result) {
|
||||
_leftChannelsInfo = std::move(result);
|
||||
exportNext();
|
||||
});
|
||||
|
||||
_api.leftChannelsLoadedCount(
|
||||
) | rpl::start_with_next([=](int count) {
|
||||
setState(stateLeftChannelsList(count));
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
void Controller::collectDialogsList() {
|
||||
_api.requestDialogsList([=](Data::DialogsInfo &&result) {
|
||||
_dialogsInfo = std::move(result);
|
||||
exportNext();
|
||||
});
|
||||
|
||||
_api.dialogsLoadedCount(
|
||||
) | rpl::start_with_next([=](int count) {
|
||||
setState(stateDialogsList(count));
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
void Controller::exportPersonalInfo() {
|
||||
_api.requestPersonalInfo([=](Data::PersonalInfo &&result) {
|
||||
_writer->writePersonal(result);
|
||||
@ -271,20 +370,123 @@ void Controller::exportSessions() {
|
||||
}
|
||||
|
||||
void Controller::exportDialogs() {
|
||||
_api.requestDialogs([=](const Data::DialogsInfo &result) {
|
||||
_writer->writeDialogsStart(result);
|
||||
}, [=](const Data::DialogInfo &result) {
|
||||
_writer->writeDialogStart(result);
|
||||
}, [=](Data::MessagesSlice &&result) {
|
||||
_writer->writeMessagesSlice(result);
|
||||
}, [=] {
|
||||
_writer->writeDialogEnd();
|
||||
}, [=] {
|
||||
_writer->writeDialogsEnd();
|
||||
exportNext();
|
||||
_writer->writeDialogsStart(_dialogsInfo);
|
||||
|
||||
exportNextDialog();
|
||||
}
|
||||
|
||||
void Controller::exportNextDialog() {
|
||||
const auto index = ++_dialogIndex;
|
||||
if (index < _dialogsInfo.list.size()) {
|
||||
const auto &info = _dialogsInfo.list[index];
|
||||
_writer->writeDialogStart(info);
|
||||
|
||||
_api.requestMessages(info, [=](Data::MessagesSlice &&result) {
|
||||
_writer->writeDialogSlice(result);
|
||||
}, [=] {
|
||||
_writer->writeDialogEnd();
|
||||
exportNextDialog();
|
||||
});
|
||||
return;
|
||||
}
|
||||
_writer->writeDialogsEnd();
|
||||
exportNext();
|
||||
}
|
||||
|
||||
void Controller::exportLeftChannels() {
|
||||
_writer->writeLeftChannelsStart(_leftChannelsInfo);
|
||||
|
||||
exportNextLeftChannel();
|
||||
}
|
||||
|
||||
void Controller::exportNextLeftChannel() {
|
||||
const auto index = ++_leftChannelIndex;
|
||||
if (index < _leftChannelsInfo.list.size()) {
|
||||
const auto &chat = _leftChannelsInfo.list[index];
|
||||
_writer->writeLeftChannelStart(chat);
|
||||
|
||||
_api.requestMessages(chat, [=](Data::MessagesSlice &&result) {
|
||||
_writer->writeLeftChannelSlice(result);
|
||||
}, [=] {
|
||||
_writer->writeLeftChannelEnd();
|
||||
exportNextLeftChannel();
|
||||
});
|
||||
return;
|
||||
}
|
||||
_writer->writeLeftChannelsEnd();
|
||||
exportNext();
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
ProcessingState Controller::prepareState(
|
||||
Step step,
|
||||
Callback &&callback) const {
|
||||
auto result = ProcessingState();
|
||||
callback(result);
|
||||
result.step = step;
|
||||
result.substepsInStep = _substepsInStep;
|
||||
return result;
|
||||
}
|
||||
|
||||
ProcessingState Controller::stateInitializing() const {
|
||||
return ProcessingState();
|
||||
}
|
||||
|
||||
ProcessingState Controller::stateLeftChannelsList(int processed) const {
|
||||
const auto step = Step::LeftChannelsList;
|
||||
return prepareState(step, [&](ProcessingState &result) {
|
||||
result.entityIndex = processed;
|
||||
result.entityCount = std::max(processed, substepsInStep(step));
|
||||
});
|
||||
}
|
||||
|
||||
ProcessingState Controller::stateDialogsList(int processed) const {
|
||||
const auto step = Step::DialogsList;
|
||||
return prepareState(step, [&](ProcessingState &result) {
|
||||
result.entityIndex = processed;
|
||||
result.entityCount = std::max(processed, substepsInStep(step));
|
||||
});
|
||||
}
|
||||
ProcessingState Controller::statePersonalInfo() const {
|
||||
return prepareState(Step::PersonalInfo);
|
||||
}
|
||||
|
||||
ProcessingState Controller::stateUserpics() const {
|
||||
return prepareState(Step::Userpics, [&](ProcessingState &result) {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
ProcessingState Controller::stateContacts() const {
|
||||
return prepareState(Step::Contacts);
|
||||
}
|
||||
|
||||
ProcessingState Controller::stateSessions() const {
|
||||
return prepareState(Step::Sessions);
|
||||
}
|
||||
|
||||
ProcessingState Controller::stateLeftChannels() const {
|
||||
const auto step = Step::LeftChannels;
|
||||
return prepareState(step, [&](ProcessingState &result) {
|
||||
//result.entityIndex = processed;
|
||||
//result.entityCount = std::max(processed, substepsInStep(step));
|
||||
});
|
||||
}
|
||||
|
||||
ProcessingState Controller::stateDialogs() const {
|
||||
const auto step = Step::Dialogs;
|
||||
return prepareState(step, [&](ProcessingState &result) {
|
||||
//result.entityIndex = processed;
|
||||
//result.entityCount = std::max(processed, substepsInStep(step));
|
||||
});
|
||||
}
|
||||
|
||||
int Controller::substepsInStep(Step step) const {
|
||||
Expects(_substepsInStep.size() > static_cast<int>(step));
|
||||
|
||||
return _substepsInStep[static_cast<int>(step)];
|
||||
}
|
||||
|
||||
void Controller::setFinishedState() {
|
||||
setState(FinishedState{ _writer->mainFilePath() });
|
||||
}
|
||||
@ -298,35 +500,35 @@ rpl::producer<State> ControllerWrap::state() const {
|
||||
});
|
||||
}
|
||||
|
||||
void ControllerWrap::submitPassword(const QString &password) {
|
||||
_wrapped.with([=](Controller &controller) {
|
||||
controller.submitPassword(password);
|
||||
});
|
||||
}
|
||||
|
||||
void ControllerWrap::requestPasswordRecover() {
|
||||
_wrapped.with([=](Controller &controller) {
|
||||
controller.requestPasswordRecover();
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<PasswordUpdate> ControllerWrap::passwordUpdate() const {
|
||||
return _wrapped.producer_on_main([=](const Controller &controller) {
|
||||
return controller.passwordUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
void ControllerWrap::reloadPasswordState() {
|
||||
_wrapped.with([=](Controller &controller) {
|
||||
controller.reloadPasswordState();
|
||||
});
|
||||
}
|
||||
|
||||
void ControllerWrap::cancelUnconfirmedPassword() {
|
||||
_wrapped.with([=](Controller &controller) {
|
||||
controller.cancelUnconfirmedPassword();
|
||||
});
|
||||
}
|
||||
//void ControllerWrap::submitPassword(const QString &password) {
|
||||
// _wrapped.with([=](Controller &controller) {
|
||||
// controller.submitPassword(password);
|
||||
// });
|
||||
//}
|
||||
//
|
||||
//void ControllerWrap::requestPasswordRecover() {
|
||||
// _wrapped.with([=](Controller &controller) {
|
||||
// controller.requestPasswordRecover();
|
||||
// });
|
||||
//}
|
||||
//
|
||||
//rpl::producer<PasswordUpdate> ControllerWrap::passwordUpdate() const {
|
||||
// return _wrapped.producer_on_main([=](const Controller &controller) {
|
||||
// return controller.passwordUpdate();
|
||||
// });
|
||||
//}
|
||||
//
|
||||
//void ControllerWrap::reloadPasswordState() {
|
||||
// _wrapped.with([=](Controller &controller) {
|
||||
// controller.reloadPasswordState();
|
||||
// });
|
||||
//}
|
||||
//
|
||||
//void ControllerWrap::cancelUnconfirmedPassword() {
|
||||
// _wrapped.with([=](Controller &controller) {
|
||||
// controller.cancelUnconfirmedPassword();
|
||||
// });
|
||||
//}
|
||||
|
||||
void ControllerWrap::startExport(const Settings &settings) {
|
||||
LOG(("Export Info: Started export to '%1'.").arg(settings.path));
|
||||
|
@ -27,20 +27,30 @@ struct PasswordCheckState {
|
||||
|
||||
struct ProcessingState {
|
||||
enum class Step {
|
||||
Initializing,
|
||||
LeftChannelsList,
|
||||
DialogsList,
|
||||
PersonalInfo,
|
||||
Userpics,
|
||||
Contacts,
|
||||
Sessions,
|
||||
LeftChannels,
|
||||
Dialogs,
|
||||
};
|
||||
enum class Item {
|
||||
Other,
|
||||
Photo,
|
||||
Video,
|
||||
VoiceMessage,
|
||||
VideoMessage,
|
||||
Sticker,
|
||||
GIF,
|
||||
File,
|
||||
};
|
||||
|
||||
Step step = Step::PersonalInfo;
|
||||
Step step = Step::Initializing;
|
||||
|
||||
std::vector<int> substepsInStep;
|
||||
|
||||
int entityIndex = 0;
|
||||
int entityCount = 1;
|
||||
@ -50,9 +60,11 @@ struct ProcessingState {
|
||||
int itemCount = 0;
|
||||
Item itemType = Item::Other;
|
||||
QString itemName;
|
||||
QString itemId;
|
||||
|
||||
int bytesLoaded = 0;
|
||||
int bytesCount = 0;
|
||||
QString objectId;
|
||||
|
||||
};
|
||||
|
||||
@ -79,16 +91,16 @@ using State = base::optional_variant<
|
||||
ErrorState,
|
||||
FinishedState>;
|
||||
|
||||
struct PasswordUpdate {
|
||||
enum class Type {
|
||||
CheckSucceed,
|
||||
WrongPassword,
|
||||
FloodLimit,
|
||||
RecoverUnavailable,
|
||||
};
|
||||
Type type = Type::WrongPassword;
|
||||
|
||||
};
|
||||
//struct PasswordUpdate {
|
||||
// enum class Type {
|
||||
// CheckSucceed,
|
||||
// WrongPassword,
|
||||
// FloodLimit,
|
||||
// RecoverUnavailable,
|
||||
// };
|
||||
// Type type = Type::WrongPassword;
|
||||
//
|
||||
//};
|
||||
|
||||
class ControllerWrap {
|
||||
public:
|
||||
@ -97,11 +109,11 @@ public:
|
||||
rpl::producer<State> state() const;
|
||||
|
||||
// Password step.
|
||||
void submitPassword(const QString &password);
|
||||
void requestPasswordRecover();
|
||||
rpl::producer<PasswordUpdate> passwordUpdate() const;
|
||||
void reloadPasswordState();
|
||||
void cancelUnconfirmedPassword();
|
||||
//void submitPassword(const QString &password);
|
||||
//void requestPasswordRecover();
|
||||
//rpl::producer<PasswordUpdate> passwordUpdate() const;
|
||||
//void reloadPasswordState();
|
||||
//void cancelUnconfirmedPassword();
|
||||
|
||||
// Processing step.
|
||||
void startExport(const Settings &settings);
|
||||
|
@ -39,16 +39,21 @@ struct MediaSettings {
|
||||
|
||||
struct Settings {
|
||||
enum class Type {
|
||||
PersonalInfo = 0x001,
|
||||
Userpics = 0x002,
|
||||
Contacts = 0x004,
|
||||
Sessions = 0x008,
|
||||
PersonalChats = 0x010,
|
||||
BotChats = 0x020,
|
||||
PrivateGroups = 0x040,
|
||||
PublicGroups = 0x080,
|
||||
PrivateChannels = 0x100,
|
||||
PublicChannels = 0x200,
|
||||
PersonalInfo = 0x001,
|
||||
Userpics = 0x002,
|
||||
Contacts = 0x004,
|
||||
Sessions = 0x008,
|
||||
PersonalChats = 0x010,
|
||||
BotChats = 0x020,
|
||||
PrivateGroups = 0x040,
|
||||
PublicGroups = 0x080,
|
||||
PrivateChannels = 0x100,
|
||||
PublicChannels = 0x200,
|
||||
|
||||
GroupsMask = PrivateGroups | PublicGroups,
|
||||
ChannelsMask = PrivateChannels | PublicChannels,
|
||||
GroupsChannelsMask = GroupsMask | ChannelsMask,
|
||||
AnyChatsMask = PersonalChats | BotChats | GroupsChannelsMask,
|
||||
};
|
||||
using Types = base::flags<Type>;
|
||||
friend inline constexpr auto is_flag_type(Type) { return true; };
|
||||
|
@ -47,10 +47,16 @@ public:
|
||||
|
||||
virtual bool writeDialogsStart(const Data::DialogsInfo &data) = 0;
|
||||
virtual bool writeDialogStart(const Data::DialogInfo &data) = 0;
|
||||
virtual bool writeMessagesSlice(const Data::MessagesSlice &data) = 0;
|
||||
virtual bool writeDialogSlice(const Data::MessagesSlice &data) = 0;
|
||||
virtual bool writeDialogEnd() = 0;
|
||||
virtual bool writeDialogsEnd() = 0;
|
||||
|
||||
virtual bool writeLeftChannelsStart(const Data::DialogsInfo &data) = 0;
|
||||
virtual bool writeLeftChannelStart(const Data::DialogInfo &data) = 0;
|
||||
virtual bool writeLeftChannelSlice(const Data::MessagesSlice &data) = 0;
|
||||
virtual bool writeLeftChannelEnd() = 0;
|
||||
virtual bool writeLeftChannelsEnd() = 0;
|
||||
|
||||
virtual bool finish() = 0;
|
||||
|
||||
virtual QString mainFilePath() = 0;
|
||||
|
@ -18,6 +18,10 @@ namespace Output {
|
||||
File::File(const QString &path) : _path(path) {
|
||||
}
|
||||
|
||||
int File::size() const {
|
||||
return _offset;
|
||||
}
|
||||
|
||||
bool File::empty() const {
|
||||
return !_offset;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ class File {
|
||||
public:
|
||||
File(const QString &path);
|
||||
|
||||
int size() const;
|
||||
bool empty() const;
|
||||
|
||||
enum class Result {
|
||||
|
@ -582,6 +582,49 @@ bool TextWriter::writeSessionsList(const Data::SessionsList &data) {
|
||||
}
|
||||
|
||||
bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) {
|
||||
return writeChatsStart(data, "Chats", "chats.txt");
|
||||
}
|
||||
|
||||
bool TextWriter::writeDialogStart(const Data::DialogInfo &data) {
|
||||
return writeChatStart(data);
|
||||
}
|
||||
|
||||
bool TextWriter::writeDialogSlice(const Data::MessagesSlice &data) {
|
||||
return writeChatSlice(data);
|
||||
}
|
||||
|
||||
bool TextWriter::writeDialogEnd() {
|
||||
return writeChatEnd();
|
||||
}
|
||||
|
||||
bool TextWriter::writeDialogsEnd() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextWriter::writeLeftChannelsStart(const Data::DialogsInfo &data) {
|
||||
return writeChatsStart(data, "Left chats", "left_chats.txt");
|
||||
}
|
||||
|
||||
bool TextWriter::writeLeftChannelStart(const Data::DialogInfo &data) {
|
||||
return writeChatStart(data);
|
||||
}
|
||||
|
||||
bool TextWriter::writeLeftChannelSlice(const Data::MessagesSlice &data) {
|
||||
return writeChatSlice(data);
|
||||
}
|
||||
|
||||
bool TextWriter::writeLeftChannelEnd() {
|
||||
return writeChatEnd();
|
||||
}
|
||||
|
||||
bool TextWriter::writeLeftChannelsEnd() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextWriter::writeChatsStart(
|
||||
const Data::DialogsInfo &data,
|
||||
const QByteArray &listName,
|
||||
const QString &fileName) {
|
||||
Expects(_result != nullptr);
|
||||
|
||||
if (data.list.empty()) {
|
||||
@ -620,7 +663,7 @@ bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) {
|
||||
}
|
||||
Unexpected("Dialog type in TypeString.");
|
||||
};
|
||||
const auto file = fileWithRelativePath("chats.txt");
|
||||
const auto file = fileWithRelativePath(fileName);
|
||||
auto list = std::vector<QByteArray>();
|
||||
list.reserve(data.list.size());
|
||||
auto index = 0;
|
||||
@ -637,27 +680,28 @@ bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto header = "Chats "
|
||||
"(" + Data::NumberToString(data.list.size()) + ") - chats.txt"
|
||||
const auto header = listName + " "
|
||||
"(" + Data::NumberToString(data.list.size()) + ") - "
|
||||
+ fileName.toUtf8()
|
||||
+ kLineBreak
|
||||
+ kLineBreak;
|
||||
return _result->writeBlock(header) == File::Result::Success;
|
||||
}
|
||||
|
||||
bool TextWriter::writeDialogStart(const Data::DialogInfo &data) {
|
||||
Expects(_dialog == nullptr);
|
||||
bool TextWriter::writeChatStart(const Data::DialogInfo &data) {
|
||||
Expects(_chat == nullptr);
|
||||
Expects(_dialogIndex < _dialogsCount);
|
||||
|
||||
const auto digits = Data::NumberToString(_dialogsCount - 1).size();
|
||||
const auto number = Data::NumberToString(++_dialogIndex, digits, '0');
|
||||
_dialog = fileWithRelativePath(data.relativePath + "messages.txt");
|
||||
_chat = fileWithRelativePath(data.relativePath + "messages.txt");
|
||||
_dialogEmpty = true;
|
||||
_dialogOnlyMy = data.onlyMyMessages;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextWriter::writeMessagesSlice(const Data::MessagesSlice &data) {
|
||||
Expects(_dialog != nullptr);
|
||||
bool TextWriter::writeChatSlice(const Data::MessagesSlice &data) {
|
||||
Expects(_chat != nullptr);
|
||||
Expects(!data.list.empty());
|
||||
|
||||
_dialogEmpty = false;
|
||||
@ -670,25 +714,21 @@ bool TextWriter::writeMessagesSlice(const Data::MessagesSlice &data) {
|
||||
data.peers,
|
||||
_settings.internalLinksDomain));
|
||||
}
|
||||
const auto full = _dialog->empty()
|
||||
const auto full = _chat->empty()
|
||||
? JoinList(kLineBreak, list)
|
||||
: kLineBreak + JoinList(kLineBreak, list);
|
||||
return _dialog->writeBlock(full) == File::Result::Success;
|
||||
return _chat->writeBlock(full) == File::Result::Success;
|
||||
}
|
||||
|
||||
bool TextWriter::writeDialogEnd() {
|
||||
Expects(_dialog != nullptr);
|
||||
bool TextWriter::writeChatEnd() {
|
||||
Expects(_chat != nullptr);
|
||||
|
||||
if (_dialogEmpty) {
|
||||
_dialog->writeBlock(_dialogOnlyMy
|
||||
_chat->writeBlock(_dialogOnlyMy
|
||||
? "No outgoing messages in this chat."
|
||||
: "No messages in this chat.");
|
||||
}
|
||||
_dialog = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextWriter::writeDialogsEnd() {
|
||||
_chat = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -30,10 +30,16 @@ public:
|
||||
|
||||
bool writeDialogsStart(const Data::DialogsInfo &data) override;
|
||||
bool writeDialogStart(const Data::DialogInfo &data) override;
|
||||
bool writeMessagesSlice(const Data::MessagesSlice &data) override;
|
||||
bool writeDialogSlice(const Data::MessagesSlice &data) override;
|
||||
bool writeDialogEnd() override;
|
||||
bool writeDialogsEnd() override;
|
||||
|
||||
bool writeLeftChannelsStart(const Data::DialogsInfo &data) override;
|
||||
bool writeLeftChannelStart(const Data::DialogInfo &data) override;
|
||||
bool writeLeftChannelSlice(const Data::MessagesSlice &data) override;
|
||||
bool writeLeftChannelEnd() override;
|
||||
bool writeLeftChannelsEnd() override;
|
||||
|
||||
bool finish() override;
|
||||
|
||||
QString mainFilePath() override;
|
||||
@ -43,6 +49,14 @@ private:
|
||||
QString pathWithRelativePath(const QString &path) const;
|
||||
std::unique_ptr<File> fileWithRelativePath(const QString &path) const;
|
||||
|
||||
bool writeChatsStart(
|
||||
const Data::DialogsInfo &data,
|
||||
const QByteArray &listName,
|
||||
const QString &fileName);
|
||||
bool writeChatStart(const Data::DialogInfo &data);
|
||||
bool writeChatSlice(const Data::MessagesSlice &data);
|
||||
bool writeChatEnd();
|
||||
|
||||
Settings _settings;
|
||||
|
||||
std::unique_ptr<File> _result;
|
||||
@ -52,7 +66,12 @@ private:
|
||||
int _dialogIndex = 0;
|
||||
bool _dialogOnlyMy = false;
|
||||
bool _dialogEmpty = true;
|
||||
std::unique_ptr<File> _dialog;
|
||||
|
||||
int _leftChannelsCount = 0;
|
||||
int _leftChannelIndex = 0;
|
||||
bool _leftChannelEmpty = true;
|
||||
|
||||
std::unique_ptr<File> _chat;
|
||||
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user