mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Support custom emoji in the message input field.
This commit is contained in:
parent
cb32c3957b
commit
2499955496
17 changed files with 192 additions and 173 deletions
|
@ -628,6 +628,7 @@ messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
|
|||
messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
|
||||
messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
|
||||
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
|
||||
messageEntityCustomEmoji#d4a00ed5 offset:int length:int stickerset:InputStickerSet document_id:long = MessageEntity;
|
||||
|
||||
inputChannelEmpty#ee8c1e86 = InputChannel;
|
||||
inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel;
|
||||
|
@ -742,7 +743,7 @@ draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
|
|||
draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
|
||||
|
||||
messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
|
||||
messages.featuredStickers#84c02310 hash:long count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
|
||||
messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
|
||||
|
||||
messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
|
||||
messages.recentStickers#88d37c56 hash:long packs:Vector<StickerPack> stickers:Vector<Document> dates:Vector<int> = messages.RecentStickers;
|
||||
|
@ -1792,9 +1793,8 @@ payments.getSavedInfo#227d824b = payments.SavedInfo;
|
|||
payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
|
||||
payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
|
||||
payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice;
|
||||
payments.assignAppStoreTransaction#fec13c6 flags:# restore:flags.0?true transaction_id:string receipt:bytes = Updates;
|
||||
payments.assignAppStoreTransaction#d5ccfd0 flags:# restore:flags.0?true receipt:bytes = Updates;
|
||||
payments.assignPlayMarketTransaction#4faa4aed purchase_token:string = Updates;
|
||||
payments.restorePlayMarketReceipt#d164e36a receipt:bytes = Updates;
|
||||
payments.canPurchasePremium#aa6a90c8 = Bool;
|
||||
payments.requestRecurringPayment#146e958d user_id:InputUser recurring_init_charge:string invoice_media:InputMedia = Updates;
|
||||
|
||||
|
@ -1853,4 +1853,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
|
|||
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||
|
||||
// LAYER 143
|
||||
// LAYER 144
|
||||
|
|
|
@ -8,15 +8,61 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_text_entities.h"
|
||||
|
||||
#include "main/main_session.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/stickers/data_stickers_set.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
using namespace TextUtilities;
|
||||
|
||||
[[nodiscard]] QString CustomEmojiEntityData(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDmessageEntityCustomEmoji &data) {
|
||||
return Data::SerializeCustomEmojiId({
|
||||
.selfId = session->userId().bare,
|
||||
.id = data.vdocument_id().v,
|
||||
.set = Data::FromInputSet(data.vstickerset()),
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MTPMessageEntity> CustomEmojiEntity(
|
||||
not_null<Main::Session*> session,
|
||||
MTPint offset,
|
||||
MTPint length,
|
||||
const QString &data) {
|
||||
const auto parsed = Data::ParseCustomEmojiData(data);
|
||||
if (!parsed.id || parsed.selfId != session->userId().bare) {
|
||||
return {};
|
||||
}
|
||||
return MTP_messageEntityCustomEmoji(
|
||||
offset,
|
||||
length,
|
||||
Data::InputStickerSet(parsed.set),
|
||||
MTP_long(parsed.id));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MTPMessageEntity> MentionNameEntity(
|
||||
not_null<Main::Session*> session,
|
||||
MTPint offset,
|
||||
MTPint length,
|
||||
const QString &data) {
|
||||
const auto parsed = MentionNameDataToFields(data);
|
||||
if (!parsed.userId || parsed.selfId != session->userId().bare) {
|
||||
return {};
|
||||
}
|
||||
return MTP_inputMessageEntityMentionName(
|
||||
offset,
|
||||
length,
|
||||
(parsed.userId == parsed.selfId
|
||||
? MTP_inputUserSelf()
|
||||
: MTP_inputUser(
|
||||
MTP_long(parsed.userId),
|
||||
MTP_long(parsed.accessHash))));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EntitiesInText EntitiesFromMTP(
|
||||
|
@ -34,34 +80,35 @@ EntitiesInText EntitiesFromMTP(
|
|||
case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityPhone: break; // Skipping phones.
|
||||
case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityMentionName: {
|
||||
case mtpc_messageEntityMentionName: if (session) {
|
||||
const auto &d = entity.c_messageEntityMentionName();
|
||||
const auto userId = UserId(d.vuser_id());
|
||||
const auto data = [&] {
|
||||
if (session) {
|
||||
if (const auto user = session->data().userLoaded(userId)) {
|
||||
return MentionNameDataFromFields({
|
||||
userId.bare,
|
||||
user->accessHash()
|
||||
});
|
||||
}
|
||||
}
|
||||
return MentionNameDataFromFields(userId.bare);
|
||||
}();
|
||||
const auto user = session->data().userLoaded(userId);
|
||||
const auto data = MentionNameDataFromFields({
|
||||
.selfId = session->userId().bare,
|
||||
.userId = userId.bare,
|
||||
.accessHash = user ? user->accessHash() : 0,
|
||||
});
|
||||
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
|
||||
} break;
|
||||
case mtpc_inputMessageEntityMentionName: {
|
||||
case mtpc_inputMessageEntityMentionName: if (session) {
|
||||
const auto &d = entity.c_inputMessageEntityMentionName();
|
||||
const auto data = [&] {
|
||||
if (session && d.vuser_id().type() == mtpc_inputUserSelf) {
|
||||
return MentionNameDataFromFields(session->userId().bare);
|
||||
} else if (d.vuser_id().type() == mtpc_inputUser) {
|
||||
auto &user = d.vuser_id().c_inputUser();
|
||||
const auto userId = UserId(user.vuser_id());
|
||||
return MentionNameDataFromFields({ userId.bare, user.vaccess_hash().v });
|
||||
}
|
||||
const auto data = d.vuser_id().match([&](
|
||||
const MTPDinputUserSelf &) {
|
||||
return MentionNameDataFromFields({
|
||||
.selfId = session->userId().bare,
|
||||
.userId = session->userId().bare,
|
||||
.accessHash = session->user()->accessHash(),
|
||||
});
|
||||
}, [&](const MTPDinputUser &data) {
|
||||
return MentionNameDataFromFields({
|
||||
.selfId = session->userId().bare,
|
||||
.userId = UserId(data.vuser_id()).bare,
|
||||
.accessHash = data.vaccess_hash().v,
|
||||
});
|
||||
}, [&](const auto &) {
|
||||
return QString();
|
||||
}();
|
||||
});
|
||||
if (!data.isEmpty()) {
|
||||
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
|
||||
}
|
||||
|
@ -73,9 +120,12 @@ EntitiesInText EntitiesFromMTP(
|
|||
case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, qs(d.vlanguage()) }); } break;
|
||||
case mtpc_messageEntityBankCard: break; // Skipping cards.
|
||||
case mtpc_messageEntityBankCard: break; // Skipping cards. // #TODO entities
|
||||
case mtpc_messageEntitySpoiler: { auto &d = entity.c_messageEntitySpoiler(); result.push_back({ EntityType::Spoiler, d.voffset().v, d.vlength().v }); } break;
|
||||
// #TODO entities
|
||||
case mtpc_messageEntityCustomEmoji: if (session) {
|
||||
const auto &d = entity.c_messageEntityCustomEmoji();
|
||||
result.push_back({ EntityType::CustomEmoji, d.voffset().v, d.vlength().v, CustomEmojiEntityData(session, d) });
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +150,8 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
|
|||
&& entity.type() != EntityType::Pre
|
||||
&& entity.type() != EntityType::Spoiler
|
||||
&& entity.type() != EntityType::MentionName
|
||||
&& entity.type() != EntityType::CustomUrl) {
|
||||
&& entity.type() != EntityType::CustomUrl
|
||||
&& entity.type() != EntityType::CustomEmoji) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -114,17 +165,8 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
|
|||
case EntityType::Cashtag: v.push_back(MTP_messageEntityCashtag(offset, length)); break;
|
||||
case EntityType::Mention: v.push_back(MTP_messageEntityMention(offset, length)); break;
|
||||
case EntityType::MentionName: {
|
||||
auto inputUser = [&](const QString &data) -> MTPInputUser {
|
||||
auto fields = MentionNameDataToFields(data);
|
||||
if (session && fields.userId == session->userId().bare) {
|
||||
return MTP_inputUserSelf();
|
||||
} else if (fields.userId) {
|
||||
return MTP_inputUser(MTP_long(fields.userId), MTP_long(fields.accessHash));
|
||||
}
|
||||
return MTP_inputUserEmpty();
|
||||
}(entity.data());
|
||||
if (inputUser.type() != mtpc_inputUserEmpty) {
|
||||
v.push_back(MTP_inputMessageEntityMentionName(offset, length, inputUser));
|
||||
if (const auto valid = MentionNameEntity(session, offset, length, entity.data())) {
|
||||
v.push_back(*valid);
|
||||
}
|
||||
} break;
|
||||
case EntityType::BotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break;
|
||||
|
@ -135,6 +177,11 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
|
|||
case EntityType::Code: v.push_back(MTP_messageEntityCode(offset, length)); break; // #TODO entities
|
||||
case EntityType::Pre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(entity.data()))); break;
|
||||
case EntityType::Spoiler: v.push_back(MTP_messageEntitySpoiler(offset, length)); break;
|
||||
case EntityType::CustomEmoji: {
|
||||
if (const auto valid = CustomEmojiEntity(session, offset, length, entity.data())) {
|
||||
v.push_back(*valid);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return MTP_vector<MTPMessageEntity>(std::move(v));
|
||||
|
|
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/ui_utility.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
@ -48,13 +49,14 @@ using EditLinkSelection = Ui::InputField::EditLinkSelection;
|
|||
|
||||
constexpr auto kParseLinksTimeout = crl::time(1000);
|
||||
|
||||
// For mention tags save and validate userId, ignore tags for different userId.
|
||||
class FieldTagMimeProcessor : public Ui::InputField::TagMimeProcessor {
|
||||
// For mention / custom emoji tags save and validate selfId,
|
||||
// ignore tags for different users.
|
||||
class FieldTagMimeProcessor final {
|
||||
public:
|
||||
explicit FieldTagMimeProcessor(
|
||||
not_null<Window::SessionController*> controller);
|
||||
|
||||
QString tagFromMimeTag(const QString &mimeTag) override;
|
||||
QString operator()(QStringView mimeTag);
|
||||
|
||||
private:
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
|
@ -66,17 +68,23 @@ FieldTagMimeProcessor::FieldTagMimeProcessor(
|
|||
: _controller(controller) {
|
||||
}
|
||||
|
||||
QString FieldTagMimeProcessor::tagFromMimeTag(const QString &mimeTag) {
|
||||
if (TextUtilities::IsMentionLink(mimeTag)) {
|
||||
const auto userId = _controller->session().userId();
|
||||
auto match = QRegularExpression(":(\\d+)$").match(mimeTag);
|
||||
if (!match.hasMatch()
|
||||
|| match.capturedView(1).toULongLong() != userId.bare) {
|
||||
return QString();
|
||||
QString FieldTagMimeProcessor::operator()(QStringView mimeTag) {
|
||||
const auto id = _controller->session().userId().bare;
|
||||
auto all = TextUtilities::SplitTags(mimeTag);
|
||||
for (auto i = all.begin(); i != all.end();) {
|
||||
const auto tag = *i;
|
||||
if (TextUtilities::IsMentionLink(tag)
|
||||
&& TextUtilities::MentionNameDataToFields(tag).selfId != id) {
|
||||
i = all.erase(i);
|
||||
} else if (Ui::InputField::IsCustomEmojiLink(tag)
|
||||
&& Data::ParseCustomEmojiData(
|
||||
Ui::InputField::CustomEmojiEntityData(tag)).selfId != id) {
|
||||
i = all.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
return mimeTag.mid(0, mimeTag.size() - match.capturedLength());
|
||||
}
|
||||
return mimeTag;
|
||||
return TextUtilities::JoinTag(all);
|
||||
}
|
||||
|
||||
//bool ValidateUrl(const QString &value) {
|
||||
|
@ -225,7 +233,9 @@ QString PrepareMentionTag(not_null<UserData*> user) {
|
|||
return TextUtilities::kMentionTagStart
|
||||
+ QString::number(user->id.value)
|
||||
+ '.'
|
||||
+ QString::number(user->accessHash());
|
||||
+ QString::number(user->accessHash())
|
||||
+ ':'
|
||||
+ QString::number(user->session().userId().bare);
|
||||
}
|
||||
|
||||
TextWithTags PrepareEditText(not_null<HistoryItem*> item) {
|
||||
|
@ -279,11 +289,19 @@ Fn<bool(
|
|||
void InitMessageField(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::InputField*> field) {
|
||||
field->setMinHeight(st::historySendSize.height() - 2 * st::historySendPadding);
|
||||
field->setMinHeight(
|
||||
st::historySendSize.height() - 2 * st::historySendPadding);
|
||||
field->setMaxHeight(st::historyComposeFieldMaxHeight);
|
||||
|
||||
field->setTagMimeProcessor(
|
||||
std::make_unique<FieldTagMimeProcessor>(controller));
|
||||
field->setTagMimeProcessor(FieldTagMimeProcessor(controller));
|
||||
field->setCustomEmojiFactory([=](QStringView data, Fn<void()> update) {
|
||||
return controller->session().data().customEmojiManager().create(
|
||||
data,
|
||||
std::move(update));
|
||||
}, [=] {
|
||||
return controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Any);
|
||||
});
|
||||
|
||||
field->document()->setDocumentMargin(4.);
|
||||
field->setAdditionalMargin(style::ConvertScale(4) - 4);
|
||||
|
|
|
@ -258,15 +258,6 @@ rpl::producer<> UiIntegration::forcePopupMenuHideRequests() {
|
|||
return Core::App().passcodeLockChanges() | rpl::to_empty;
|
||||
}
|
||||
|
||||
QString UiIntegration::convertTagToMimeTag(const QString &tagId) {
|
||||
if (TextUtilities::IsMentionLink(tagId)) {
|
||||
if (const auto session = Core::App().activeAccount().maybeSession()) {
|
||||
return tagId + ':' + QString::number(session->userId().bare);
|
||||
}
|
||||
}
|
||||
return tagId;
|
||||
}
|
||||
|
||||
const Ui::Emoji::One *UiIntegration::defaultEmojiVariant(
|
||||
const Ui::Emoji::One *emoji) {
|
||||
if (!emoji || !emoji->hasVariants()) {
|
||||
|
|
|
@ -54,7 +54,6 @@ public:
|
|||
const QString &url,
|
||||
const QVariant &context) override;
|
||||
rpl::producer<> forcePopupMenuHideRequests() override;
|
||||
QString convertTagToMimeTag(const QString &tagId) override;
|
||||
const Ui::Emoji::One *defaultEmojiVariant(
|
||||
const Ui::Emoji::One *emoji) override;
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> createCustomEmoji(
|
||||
|
|
|
@ -22,15 +22,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "apiwrap.h"
|
||||
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/stickers/data_stickers_set.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
struct CustomEmojiId {
|
||||
StickerSetIdentifier set;
|
||||
uint64 id = 0;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
using SizeTag = CustomEmojiManager::SizeTag;
|
||||
|
@ -52,42 +45,6 @@ using SizeTag = CustomEmojiManager::SizeTag;
|
|||
Unexpected("SizeTag value in CustomEmojiManager-SizeFromTag.");
|
||||
}
|
||||
|
||||
[[nodiscard]] QString SerializeCustomEmojiId(const CustomEmojiId &id) {
|
||||
return QString::number(id.id)
|
||||
+ '@'
|
||||
+ QString::number(id.set.id)
|
||||
+ ':'
|
||||
+ QString::number(id.set.accessHash);
|
||||
}
|
||||
|
||||
[[nodiscard]] QString SerializeCustomEmojiId(
|
||||
not_null<DocumentData*> document) {
|
||||
const auto sticker = document->sticker();
|
||||
return SerializeCustomEmojiId({
|
||||
sticker ? sticker->set : StickerSetIdentifier(),
|
||||
document->id,
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] CustomEmojiId ParseCustomEmojiData(QStringView data) {
|
||||
const auto parts = data.split('@');
|
||||
if (parts.size() != 2) {
|
||||
return {};
|
||||
}
|
||||
const auto id = parts[0].toULongLong();
|
||||
if (!id) {
|
||||
return {};
|
||||
}
|
||||
const auto second = parts[1].split(':');
|
||||
return {
|
||||
.set = {
|
||||
.id = second[0].toULongLong(),
|
||||
.accessHash = second[1].toULongLong(),
|
||||
},
|
||||
.id = id
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class CustomEmojiLoader final
|
||||
|
@ -392,7 +349,7 @@ CustomEmojiManager::CustomEmojiManager(not_null<Session*> owner)
|
|||
CustomEmojiManager::~CustomEmojiManager() = default;
|
||||
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
||||
const QString &data,
|
||||
QStringView data,
|
||||
Fn<void()> update) {
|
||||
const auto parsed = ParseCustomEmojiData(data);
|
||||
if (!parsed.id || !parsed.set.id) {
|
||||
|
@ -547,56 +504,46 @@ Session &CustomEmojiManager::owner() const {
|
|||
return *_owner;
|
||||
}
|
||||
|
||||
void FillTestCustomEmoji(
|
||||
not_null<Main::Session*> session,
|
||||
TextWithEntities &text) {
|
||||
auto &sets = session->data().stickers().sets();
|
||||
auto recentIt = sets.find(Data::Stickers::CloudRecentSetId);
|
||||
const auto pack = &session->emojiStickersPack();
|
||||
const auto begin = text.text.constData(), end = begin + text.text.size();
|
||||
for (auto ch = begin; ch != end;) {
|
||||
auto length = 0;
|
||||
if (const auto emoji = Ui::Emoji::Find(ch, end, &length)) {
|
||||
auto replace = (DocumentData*)nullptr;
|
||||
if (recentIt != sets.end()) {
|
||||
for (const auto document : recentIt->second->stickers) {
|
||||
if (const auto sticker = document->sticker()) {
|
||||
if (Ui::Emoji::Find(sticker->alt) == emoji) {
|
||||
replace = document;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (const auto found = pack->stickerForEmoji(emoji)) {
|
||||
Assert(found.document->sticker() != nullptr);
|
||||
if (!replace && found.document->sticker()->set.id) {
|
||||
replace = found.document;
|
||||
}
|
||||
}
|
||||
if (replace) {
|
||||
text.entities.push_back({
|
||||
EntityType::CustomEmoji,
|
||||
(ch - begin),
|
||||
length,
|
||||
SerializeCustomEmojiId({
|
||||
replace->sticker()->set,
|
||||
replace->id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
ch += length;
|
||||
} else if (ch->isHighSurrogate()
|
||||
&& (ch + 1 != end)
|
||||
&& (ch + 1)->isLowSurrogate()) {
|
||||
ch += 2;
|
||||
} else {
|
||||
++ch;
|
||||
}
|
||||
QString SerializeCustomEmojiId(const CustomEmojiId &id) {
|
||||
return QString::number(id.set.id)
|
||||
+ '.'
|
||||
+ QString::number(id.set.accessHash)
|
||||
+ ':'
|
||||
+ QString::number(id.selfId)
|
||||
+ '/'
|
||||
+ QString::number(id.id);
|
||||
}
|
||||
|
||||
QString SerializeCustomEmojiId(not_null<DocumentData*> document) {
|
||||
const auto sticker = document->sticker();
|
||||
return SerializeCustomEmojiId({
|
||||
.selfId = document->session().userId().bare,
|
||||
.id = document->id,
|
||||
.set = sticker ? sticker->set : StickerSetIdentifier(),
|
||||
});
|
||||
}
|
||||
|
||||
CustomEmojiId ParseCustomEmojiData(QStringView data) {
|
||||
const auto components = data.split('.');
|
||||
if (components.size() != 2) {
|
||||
return {};
|
||||
}
|
||||
ranges::stable_sort(
|
||||
text.entities,
|
||||
ranges::less(),
|
||||
&EntityInText::offset);
|
||||
const auto parts = components[1].split(':');
|
||||
if (parts.size() != 2) {
|
||||
return {};
|
||||
}
|
||||
const auto endings = parts[1].split('/');
|
||||
if (endings.size() != 2) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
.selfId = endings[0].toULongLong(),
|
||||
.id = endings[1].toULongLong(),
|
||||
.set = {
|
||||
.id = components[0].toULongLong(),
|
||||
.accessHash = parts[0].toULongLong(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "data/stickers/data_stickers_set.h"
|
||||
#include "ui/text/custom_emoji_instance.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
@ -20,9 +21,14 @@ class Session;
|
|||
namespace Data {
|
||||
|
||||
class Session;
|
||||
struct CustomEmojiId;
|
||||
class CustomEmojiLoader;
|
||||
|
||||
struct CustomEmojiId {
|
||||
uint64 selfId = 0;
|
||||
uint64 id = 0;
|
||||
StickerSetIdentifier set;
|
||||
};
|
||||
|
||||
class CustomEmojiManager final : public base::has_weak_ptr {
|
||||
public:
|
||||
enum class SizeTag {
|
||||
|
@ -34,7 +40,7 @@ public:
|
|||
~CustomEmojiManager();
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create(
|
||||
const QString &data,
|
||||
QStringView data,
|
||||
Fn<void()> update);
|
||||
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
|
@ -76,8 +82,9 @@ private:
|
|||
|
||||
};
|
||||
|
||||
void FillTestCustomEmoji(
|
||||
not_null<Main::Session*> session,
|
||||
TextWithEntities &text);
|
||||
[[nodiscard]] QString SerializeCustomEmojiId(const CustomEmojiId &id);
|
||||
[[nodiscard]] QString SerializeCustomEmojiId(
|
||||
not_null<DocumentData*> document);
|
||||
[[nodiscard]] CustomEmojiId ParseCustomEmojiData(QStringView data);
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -155,7 +155,8 @@ std::vector<TextPart> ParseText(
|
|||
[](const MTPDmessageEntityBlockquote&) {
|
||||
return Type::Blockquote; },
|
||||
[](const MTPDmessageEntityBankCard&) { return Type::BankCard; },
|
||||
[](const MTPDmessageEntitySpoiler&) { return Type::Spoiler; });
|
||||
[](const MTPDmessageEntitySpoiler&) { return Type::Spoiler; },
|
||||
[](const MTPDmessageEntityCustomEmoji&) { return Type::CustomEmoji; });
|
||||
part.text = mid(start, length);
|
||||
part.additional = entity.match(
|
||||
[](const MTPDmessageEntityPre &data) {
|
||||
|
@ -164,6 +165,8 @@ std::vector<TextPart> ParseText(
|
|||
return ParseString(data.vurl());
|
||||
}, [](const MTPDmessageEntityMentionName &data) {
|
||||
return NumberToString(data.vuser_id().v);
|
||||
}, [](const MTPDmessageEntityCustomEmoji &data) {
|
||||
return NumberToString(data.vdocument_id().v);
|
||||
}, [](const auto &) { return Utf8String(); });
|
||||
|
||||
result.push_back(std::move(part));
|
||||
|
|
|
@ -549,6 +549,7 @@ struct TextPart {
|
|||
Blockquote,
|
||||
BankCard,
|
||||
Spoiler,
|
||||
CustomEmoji,
|
||||
};
|
||||
Type type = Type::Text;
|
||||
Utf8String text;
|
||||
|
|
|
@ -274,6 +274,9 @@ QByteArray FormatText(
|
|||
"onclick=\"ShowSpoiler(this)\">"
|
||||
"<span aria-hidden=\"true\">"
|
||||
+ text + "</span></span>";
|
||||
case Type::CustomEmoji: return SerializeString("{custom_emoji}")
|
||||
+ text // TODO custom-emoji
|
||||
+ SerializeString("{/custom_emoji}");
|
||||
}
|
||||
Unexpected("Type in text entities serialization.");
|
||||
}) | ranges::to_vector);
|
||||
|
|
|
@ -181,17 +181,21 @@ QByteArray SerializeText(
|
|||
case Type::Blockquote: return "blockquote";
|
||||
case Type::BankCard: return "bank_card";
|
||||
case Type::Spoiler: return "spoiler";
|
||||
case Type::CustomEmoji: return "custom_emoji";
|
||||
}
|
||||
Unexpected("Type in SerializeText.");
|
||||
}();
|
||||
const auto additionalName = (part.type == Type::MentionName)
|
||||
? "user_id"
|
||||
: (part.type == Type::CustomEmoji)
|
||||
? "document_id"
|
||||
: (part.type == Type::Pre)
|
||||
? "language"
|
||||
: (part.type == Type::TextUrl)
|
||||
? "href"
|
||||
: "none";
|
||||
const auto additionalValue = (part.type == Type::MentionName)
|
||||
const auto additionalValue = (part.type == Type::MentionName
|
||||
|| part.type == Type::CustomEmoji)
|
||||
? part.additional
|
||||
: (part.type == Type::Pre || part.type == Type::TextUrl)
|
||||
? SerializeString(part.additional)
|
||||
|
|
|
@ -337,7 +337,6 @@ HistoryMessage::HistoryMessage(
|
|||
&history->session(),
|
||||
data.ventities().value_or_empty())
|
||||
};
|
||||
Data::FillTestCustomEmoji(&history->session(), textWithEntities);
|
||||
setText(_media ? textWithEntities : EnsureNonEmpty(textWithEntities));
|
||||
if (const auto groupedId = data.vgrouped_id()) {
|
||||
setGroupId(
|
||||
|
|
|
@ -25,7 +25,7 @@ struct LoadedPart {
|
|||
|
||||
class Loader {
|
||||
public:
|
||||
static constexpr auto kPartSize = 128 * 1024;
|
||||
static constexpr auto kPartSize = int64(128 * 1024);
|
||||
|
||||
[[nodiscard]] virtual Storage::Cache::Key baseCacheKey() const = 0;
|
||||
[[nodiscard]] virtual int64 size() const = 0;
|
||||
|
|
|
@ -127,7 +127,7 @@ bytes::const_span ParseCachedMap(
|
|||
if (size > maxSize) {
|
||||
return {};
|
||||
}
|
||||
for (auto offset = 0; offset < size; offset += kPartSize) {
|
||||
for (auto offset = int64(); offset < size; offset += kPartSize) {
|
||||
const auto part = data.subspan(
|
||||
offset,
|
||||
std::min(kPartSize, size - offset));
|
||||
|
|
|
@ -107,7 +107,7 @@ void StreamedFileDownloader::requestPart() {
|
|||
++_partsRequested;
|
||||
}
|
||||
|
||||
QByteArray StreamedFileDownloader::readLoadedPart(int offset) {
|
||||
QByteArray StreamedFileDownloader::readLoadedPart(int64 offset) {
|
||||
Expects(offset >= 0 && offset < _fullSize);
|
||||
Expects(!(offset % kPartSize));
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
uint64 objId() const override;
|
||||
Data::FileOrigin fileOrigin() const override;
|
||||
|
||||
QByteArray readLoadedPart(int offset);
|
||||
QByteArray readLoadedPart(int64 offset);
|
||||
|
||||
private:
|
||||
void startLoading() override;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 32cf0968a14f7ef7756834f72500fc58fef50c6d
|
||||
Subproject commit 6bd7518109850d650a174b74e5582367555390da
|
Loading…
Add table
Reference in a new issue