mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Download stickers for custom emoji in export.
This commit is contained in:
parent
d57e752ae9
commit
d80cf5d149
6 changed files with 167 additions and 8 deletions
|
@ -52,6 +52,16 @@ function ShowMentionName() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ShowNotLoadedEmoji() {
|
||||||
|
ShowToast("This custom emoji is not included, change data exporting settings to download.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ShowNotAvailableEmoji() {
|
||||||
|
ShowToast("This custom emoji is not available.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function ShowSpoiler(target) {
|
function ShowSpoiler(target) {
|
||||||
if (target.classList.contains("hidden")) {
|
if (target.classList.contains("hidden")) {
|
||||||
target.classList.toggle("hidden");
|
target.classList.toggle("hidden");
|
||||||
|
|
|
@ -340,6 +340,12 @@ struct ParseMediaContext {
|
||||||
UserId botId = 0;
|
UserId botId = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Document ParseDocument(
|
||||||
|
ParseMediaContext &context,
|
||||||
|
const MTPDocument &data,
|
||||||
|
const QString &suggestedFolder,
|
||||||
|
TimeId date);
|
||||||
|
|
||||||
Media ParseMedia(
|
Media ParseMedia(
|
||||||
ParseMediaContext &context,
|
ParseMediaContext &context,
|
||||||
const MTPMessageMedia &data,
|
const MTPMessageMedia &data,
|
||||||
|
@ -560,6 +566,10 @@ struct TextPart {
|
||||||
Type type = Type::Text;
|
Type type = Type::Text;
|
||||||
Utf8String text;
|
Utf8String text;
|
||||||
Utf8String additional;
|
Utf8String additional;
|
||||||
|
|
||||||
|
[[nodiscard]] static Utf8String UnavailableEmoji() {
|
||||||
|
return "(unavailable)";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MessageId {
|
struct MessageId {
|
||||||
|
@ -619,6 +629,7 @@ struct FileOrigin {
|
||||||
int split = 0;
|
int split = 0;
|
||||||
MTPInputPeer peer;
|
MTPInputPeer peer;
|
||||||
int32 messageId = 0;
|
int32 messageId = 0;
|
||||||
|
uint64 customEmojiId = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Message ParseMessage(
|
Message ParseMessage(
|
||||||
|
|
|
@ -30,6 +30,7 @@ constexpr auto kMessagesSliceLimit = 100;
|
||||||
constexpr auto kTopPeerSliceLimit = 100;
|
constexpr auto kTopPeerSliceLimit = 100;
|
||||||
constexpr auto kFileMaxSize = 4000 * int64(1024 * 1024);
|
constexpr auto kFileMaxSize = 4000 * int64(1024 * 1024);
|
||||||
constexpr auto kLocationCacheSize = 100'000;
|
constexpr auto kLocationCacheSize = 100'000;
|
||||||
|
constexpr auto kMaxEmojiPerRequest = 100;
|
||||||
|
|
||||||
struct LocationKey {
|
struct LocationKey {
|
||||||
uint64 type;
|
uint64 type;
|
||||||
|
@ -1468,13 +1469,79 @@ void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) {
|
||||||
Expects(_chatProcess != nullptr);
|
Expects(_chatProcess != nullptr);
|
||||||
Expects(!_chatProcess->slice.has_value());
|
Expects(!_chatProcess->slice.has_value());
|
||||||
|
|
||||||
|
collectMessagesCustomEmoji(slice);
|
||||||
|
|
||||||
if (slice.list.empty()) {
|
if (slice.list.empty()) {
|
||||||
_chatProcess->lastSlice = true;
|
_chatProcess->lastSlice = true;
|
||||||
}
|
}
|
||||||
_chatProcess->slice = std::move(slice);
|
_chatProcess->slice = std::move(slice);
|
||||||
_chatProcess->fileIndex = 0;
|
_chatProcess->fileIndex = 0;
|
||||||
|
|
||||||
loadNextMessageFile();
|
resolveCustomEmoji();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::collectMessagesCustomEmoji(const Data::MessagesSlice &slice) {
|
||||||
|
for (const auto &message : slice.list) {
|
||||||
|
for (const auto &part : message.text) {
|
||||||
|
if (part.type == Data::TextPart::Type::CustomEmoji) {
|
||||||
|
if (const auto id = part.additional.toULongLong()) {
|
||||||
|
if (!_resolvedCustomEmoji.contains(id)) {
|
||||||
|
_unresolvedCustomEmoji.emplace(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::resolveCustomEmoji() {
|
||||||
|
if (_unresolvedCustomEmoji.empty()) {
|
||||||
|
loadNextMessageFile();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto count = std::min(
|
||||||
|
int(_unresolvedCustomEmoji.size()),
|
||||||
|
kMaxEmojiPerRequest);
|
||||||
|
auto v = QVector<MTPlong>();
|
||||||
|
v.reserve(count);
|
||||||
|
const auto till = end(_unresolvedCustomEmoji);
|
||||||
|
const auto from = end(_unresolvedCustomEmoji) - count;
|
||||||
|
for (auto i = from; i != till; ++i) {
|
||||||
|
v.push_back(MTP_long(*i));
|
||||||
|
}
|
||||||
|
_unresolvedCustomEmoji.erase(from, till);
|
||||||
|
const auto finalize = [=] {
|
||||||
|
for (const auto &id : v) {
|
||||||
|
if (_resolvedCustomEmoji.contains(id.v)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_resolvedCustomEmoji.emplace(
|
||||||
|
id.v,
|
||||||
|
Data::Document{
|
||||||
|
.file = {
|
||||||
|
.skipReason = Data::File::SkipReason::Unavailable,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolveCustomEmoji();
|
||||||
|
};
|
||||||
|
mainRequest(MTPmessages_GetCustomEmojiDocuments(
|
||||||
|
MTP_vector<MTPlong>(v)
|
||||||
|
)).fail([=](const MTP::Error &error) {
|
||||||
|
LOG(("Export Error: Failed to get documents for emoji."));
|
||||||
|
finalize();
|
||||||
|
return true;
|
||||||
|
}).done([=](const MTPVector<MTPDocument> &result) {
|
||||||
|
for (const auto &entry : result.v) {
|
||||||
|
auto document = Data::ParseDocument(
|
||||||
|
_chatProcess->context,
|
||||||
|
entry,
|
||||||
|
_chatProcess->info.relativePath,
|
||||||
|
TimeId());
|
||||||
|
_resolvedCustomEmoji.emplace(document.id, std::move(document));
|
||||||
|
}
|
||||||
|
finalize();
|
||||||
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::Message *ApiWrap::currentFileMessage() const {
|
Data::Message *ApiWrap::currentFileMessage() const {
|
||||||
|
@ -1501,6 +1568,44 @@ Data::FileOrigin ApiWrap::currentFileMessageOrigin() const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ApiWrap::messageCustomEmojiReady(Data::Message &message) {
|
||||||
|
for (auto &part : message.text) {
|
||||||
|
if (part.type == Data::TextPart::Type::CustomEmoji) {
|
||||||
|
if (const auto id = part.additional.toULongLong()) {
|
||||||
|
const auto i = _resolvedCustomEmoji.find(id);
|
||||||
|
if (i == end(_resolvedCustomEmoji)) {
|
||||||
|
part.additional = Data::TextPart::UnavailableEmoji();
|
||||||
|
} else {
|
||||||
|
auto &file = i->second.file;
|
||||||
|
const auto fileProgress = [=](FileProgress value) {
|
||||||
|
return loadMessageEmojiProgress(value);
|
||||||
|
};
|
||||||
|
const auto ready = processFileLoad(
|
||||||
|
file,
|
||||||
|
{ .customEmojiId = id },
|
||||||
|
fileProgress,
|
||||||
|
[=](const QString &path) {
|
||||||
|
loadMessageEmojiDone(id, path);
|
||||||
|
});
|
||||||
|
if (!ready) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
using SkipReason = Data::File::SkipReason;
|
||||||
|
if (file.skipReason == SkipReason::Unavailable) {
|
||||||
|
part.additional = Data::TextPart::UnavailableEmoji();
|
||||||
|
} else if (file.skipReason == SkipReason::FileType
|
||||||
|
|| file.skipReason == SkipReason::FileSize) {
|
||||||
|
part.additional = QByteArray();
|
||||||
|
} else {
|
||||||
|
part.additional = file.relativePath.toUtf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::loadNextMessageFile() {
|
void ApiWrap::loadNextMessageFile() {
|
||||||
Expects(_chatProcess != nullptr);
|
Expects(_chatProcess != nullptr);
|
||||||
Expects(_chatProcess->slice.has_value());
|
Expects(_chatProcess->slice.has_value());
|
||||||
|
@ -1508,10 +1613,13 @@ void ApiWrap::loadNextMessageFile() {
|
||||||
for (auto &list = _chatProcess->slice->list
|
for (auto &list = _chatProcess->slice->list
|
||||||
; _chatProcess->fileIndex < list.size()
|
; _chatProcess->fileIndex < list.size()
|
||||||
; ++_chatProcess->fileIndex) {
|
; ++_chatProcess->fileIndex) {
|
||||||
const auto &message = list[_chatProcess->fileIndex];
|
auto &message = list[_chatProcess->fileIndex];
|
||||||
if (Data::SkipMessageByDate(message, *_settings)) {
|
if (Data::SkipMessageByDate(message, *_settings)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!messageCustomEmojiReady(message)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto fileProgress = [=](FileProgress value) {
|
const auto fileProgress = [=](FileProgress value) {
|
||||||
return loadMessageFileProgress(value);
|
return loadMessageFileProgress(value);
|
||||||
};
|
};
|
||||||
|
@ -1618,6 +1726,21 @@ void ApiWrap::loadMessageThumbDone(const QString &relativePath) {
|
||||||
loadNextMessageFile();
|
loadNextMessageFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ApiWrap::loadMessageEmojiProgress(FileProgress progress) {
|
||||||
|
return loadMessageFileProgress(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::loadMessageEmojiDone(uint64 id, const QString &relativePath) {
|
||||||
|
const auto i = _resolvedCustomEmoji.find(id);
|
||||||
|
if (i != end(_resolvedCustomEmoji)) {
|
||||||
|
i->second.file.relativePath = relativePath;
|
||||||
|
if (relativePath.isEmpty()) {
|
||||||
|
i->second.file.skipReason = Data::File::SkipReason::Unavailable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadNextMessageFile();
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::finishMessages() {
|
void ApiWrap::finishMessages() {
|
||||||
Expects(_chatProcess != nullptr);
|
Expects(_chatProcess != nullptr);
|
||||||
Expects(!_chatProcess->slice.has_value());
|
Expects(!_chatProcess->slice.has_value());
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Export {
|
||||||
namespace Data {
|
namespace Data {
|
||||||
struct File;
|
struct File;
|
||||||
struct Chat;
|
struct Chat;
|
||||||
|
struct Document;
|
||||||
struct FileLocation;
|
struct FileLocation;
|
||||||
struct PersonalInfo;
|
struct PersonalInfo;
|
||||||
struct UserpicsInfo;
|
struct UserpicsInfo;
|
||||||
|
@ -156,12 +157,17 @@ private:
|
||||||
int addOffset,
|
int addOffset,
|
||||||
int limit,
|
int limit,
|
||||||
FnMut<void(MTPmessages_Messages&&)> done);
|
FnMut<void(MTPmessages_Messages&&)> done);
|
||||||
|
void collectMessagesCustomEmoji(const Data::MessagesSlice &slice);
|
||||||
|
void resolveCustomEmoji();
|
||||||
void loadMessagesFiles(Data::MessagesSlice &&slice);
|
void loadMessagesFiles(Data::MessagesSlice &&slice);
|
||||||
void loadNextMessageFile();
|
void loadNextMessageFile();
|
||||||
|
bool messageCustomEmojiReady(Data::Message &message);
|
||||||
bool loadMessageFileProgress(FileProgress value);
|
bool loadMessageFileProgress(FileProgress value);
|
||||||
void loadMessageFileDone(const QString &relativePath);
|
void loadMessageFileDone(const QString &relativePath);
|
||||||
bool loadMessageThumbProgress(FileProgress value);
|
bool loadMessageThumbProgress(FileProgress value);
|
||||||
void loadMessageThumbDone(const QString &relativePath);
|
void loadMessageThumbDone(const QString &relativePath);
|
||||||
|
bool loadMessageEmojiProgress(FileProgress progress);
|
||||||
|
void loadMessageEmojiDone(uint64 id, const QString &relativePath);
|
||||||
void finishMessagesSlice();
|
void finishMessagesSlice();
|
||||||
void finishMessages();
|
void finishMessages();
|
||||||
|
|
||||||
|
@ -227,6 +233,8 @@ private:
|
||||||
std::unique_ptr<LeftChannelsProcess> _leftChannelsProcess;
|
std::unique_ptr<LeftChannelsProcess> _leftChannelsProcess;
|
||||||
std::unique_ptr<DialogsProcess> _dialogsProcess;
|
std::unique_ptr<DialogsProcess> _dialogsProcess;
|
||||||
std::unique_ptr<ChatProcess> _chatProcess;
|
std::unique_ptr<ChatProcess> _chatProcess;
|
||||||
|
base::flat_set<uint64> _unresolvedCustomEmoji;
|
||||||
|
base::flat_map<uint64, Data::Document> _resolvedCustomEmoji;
|
||||||
QVector<MTPMessageRange> _splits;
|
QVector<MTPMessageRange> _splits;
|
||||||
|
|
||||||
rpl::event_stream<MTP::Error> _errors;
|
rpl::event_stream<MTP::Error> _errors;
|
||||||
|
|
|
@ -220,7 +220,8 @@ QByteArray JoinList(
|
||||||
|
|
||||||
QByteArray FormatText(
|
QByteArray FormatText(
|
||||||
const std::vector<Data::TextPart> &data,
|
const std::vector<Data::TextPart> &data,
|
||||||
const QString &internalLinksDomain) {
|
const QString &internalLinksDomain,
|
||||||
|
const QString &relativeLinkBase) {
|
||||||
return JoinList(QByteArray(), ranges::views::all(
|
return JoinList(QByteArray(), ranges::views::all(
|
||||||
data
|
data
|
||||||
) | ranges::views::transform([&](const Data::TextPart &part) {
|
) | ranges::views::transform([&](const Data::TextPart &part) {
|
||||||
|
@ -274,9 +275,15 @@ QByteArray FormatText(
|
||||||
"onclick=\"ShowSpoiler(this)\">"
|
"onclick=\"ShowSpoiler(this)\">"
|
||||||
"<span aria-hidden=\"true\">"
|
"<span aria-hidden=\"true\">"
|
||||||
+ text + "</span></span>";
|
+ text + "</span></span>";
|
||||||
case Type::CustomEmoji: return SerializeString("{custom_emoji}")
|
case Type::CustomEmoji: return (part.additional.isEmpty()
|
||||||
+ text // TODO custom-emoji
|
? "<a href=\"\" onclick=\"return ShowNotLoadedEmoji();\">"
|
||||||
+ SerializeString("{/custom_emoji}");
|
: (part.additional == Data::TextPart::UnavailableEmoji())
|
||||||
|
? "<a href=\"\" onclick=\"return ShowNotAvailableEmoji();\">"
|
||||||
|
: ("<a href = \""
|
||||||
|
+ (relativeLinkBase + part.additional).toUtf8()
|
||||||
|
+ "\">"))
|
||||||
|
+ text
|
||||||
|
+ "</a>";
|
||||||
}
|
}
|
||||||
Unexpected("Type in text entities serialization.");
|
Unexpected("Type in text entities serialization.");
|
||||||
}) | ranges::to_vector);
|
}) | ranges::to_vector);
|
||||||
|
@ -1257,7 +1264,7 @@ auto HtmlWriter::Wrap::pushMessage(
|
||||||
|
|
||||||
block.append(pushMedia(message, basePath, peers, internalLinksDomain));
|
block.append(pushMedia(message, basePath, peers, internalLinksDomain));
|
||||||
|
|
||||||
const auto text = FormatText(message.text, internalLinksDomain);
|
const auto text = FormatText(message.text, internalLinksDomain, _base);
|
||||||
if (!text.isEmpty()) {
|
if (!text.isEmpty()) {
|
||||||
block.append(pushDiv("text"));
|
block.append(pushDiv("text"));
|
||||||
block.append(text);
|
block.append(text);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9b0f4df00715f4dfaac81e17148ca37df26fb301
|
Subproject commit 4768e7ee03aa22f64f73dc13016d5bd94a047496
|
Loading…
Add table
Reference in a new issue