Support custom emoji in the message input field.

This commit is contained in:
John Preston 2022-06-29 18:56:59 +04:00
parent cb32c3957b
commit 2499955496
17 changed files with 192 additions and 173 deletions

View File

@ -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

View File

@ -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));

View File

@ -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);

View File

@ -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()) {

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -549,6 +549,7 @@ struct TextPart {
Blockquote,
BankCard,
Spoiler,
CustomEmoji,
};
Type type = Type::Text;
Utf8String text;

View File

@ -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);

View File

@ -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)

View File

@ -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(

View File

@ -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;

View File

@ -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));

View File

@ -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));

View File

@ -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