mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Display topic name in chats list.
This commit is contained in:
parent
fdf4129e5e
commit
34a2c5c8ce
28 changed files with 223 additions and 172 deletions
BIN
Telegram/Resources/icons/dialogs/dialogs_topic_arrow.png
Normal file
BIN
Telegram/Resources/icons/dialogs/dialogs_topic_arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 250 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_topic_arrow@2x.png
Normal file
BIN
Telegram/Resources/icons/dialogs/dialogs_topic_arrow@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 293 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_topic_arrow@3x.png
Normal file
BIN
Telegram/Resources/icons/dialogs/dialogs_topic_arrow@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 416 B |
|
@ -2049,6 +2049,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_dialogs_text_from_wrapped" = "{from}:";
|
||||
"lng_dialogs_text_media" = "{media_part} {caption}";
|
||||
"lng_dialogs_text_media_wrapped" = "{media},";
|
||||
"lng_dialogs_text_from_in_topic" = "{from} {topic}";
|
||||
"lng_dialogs_show_all_chats" = "Show all chats";
|
||||
"lng_dialogs_hide_muted_chats" = "Hide muted chats";
|
||||
"lng_dialogs_skip_archive_in_search" = "Skip results from archive";
|
||||
|
|
|
@ -21,9 +21,7 @@ using namespace TextUtilities;
|
|||
|
||||
[[nodiscard]] QString CustomEmojiEntityData(
|
||||
const MTPDmessageEntityCustomEmoji &data) {
|
||||
return Data::SerializeCustomEmojiId({
|
||||
.id = data.vdocument_id().v,
|
||||
});
|
||||
return Data::SerializeCustomEmojiId(data.vdocument_id().v);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MTPMessageEntity> CustomEmojiEntity(
|
||||
|
@ -31,13 +29,13 @@ using namespace TextUtilities;
|
|||
MTPint length,
|
||||
const QString &data) {
|
||||
const auto parsed = Data::ParseCustomEmojiData(data);
|
||||
if (!parsed.id) {
|
||||
if (!parsed) {
|
||||
return {};
|
||||
}
|
||||
return MTP_messageEntityCustomEmoji(
|
||||
offset,
|
||||
length,
|
||||
MTP_long(parsed.id));
|
||||
MTP_long(parsed));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MTPMessageEntity> MentionNameEntity(
|
||||
|
|
|
@ -87,11 +87,11 @@ QString FieldTagMimeProcessor::operator()(QStringView mimeTag) {
|
|||
} else if (Ui::InputField::IsCustomEmojiLink(tag)) {
|
||||
const auto data = Ui::InputField::CustomEmojiEntityData(tag);
|
||||
const auto emoji = Data::ParseCustomEmojiData(data);
|
||||
if (!emoji.id) {
|
||||
if (!emoji) {
|
||||
i = all.erase(i);
|
||||
continue;
|
||||
} else if (!_session->premium()) {
|
||||
const auto document = _session->data().document(emoji.id);
|
||||
const auto document = _session->data().document(emoji);
|
||||
if (document->isPremiumEmoji()) {
|
||||
if (!_allowPremiumEmoji
|
||||
|| premiumSkipped
|
||||
|
|
|
@ -225,6 +225,13 @@ void Forum::applyReceivedTopics(
|
|||
).first->second.get()
|
||||
: i->second.get();
|
||||
raw->applyTopic(data);
|
||||
if (creating) {
|
||||
if (const auto last = _history->chatListMessage()
|
||||
; last && last->topicRootId() == rootId) {
|
||||
_history->lastItemDialogsView().itemInvalidated(last);
|
||||
_history->updateChatListEntry();
|
||||
}
|
||||
}
|
||||
if (callback) {
|
||||
callback(raw);
|
||||
}
|
||||
|
|
|
@ -139,6 +139,14 @@ QImage ForumTopicIconFrame(
|
|||
return background;
|
||||
}
|
||||
|
||||
TextWithEntities ForumTopicIconWithTitle(
|
||||
DocumentId iconId,
|
||||
const QString &title) {
|
||||
return iconId
|
||||
? Data::SingleCustomEmoji(iconId).append(title)
|
||||
: TextWithEntities{ title };
|
||||
}
|
||||
|
||||
ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
|
||||
: Thread(&forum->history()->owner(), Type::ForumTopic)
|
||||
, _forum(forum)
|
||||
|
@ -570,6 +578,10 @@ QString ForumTopic::title() const {
|
|||
return _title;
|
||||
}
|
||||
|
||||
TextWithEntities ForumTopic::titleWithIcon() const {
|
||||
return ForumTopicIconWithTitle(_iconId, _title);
|
||||
}
|
||||
|
||||
void ForumTopic::applyTitle(const QString &title) {
|
||||
if (_title == title) {
|
||||
return;
|
||||
|
|
|
@ -44,6 +44,9 @@ class Forum;
|
|||
int32 colorId,
|
||||
const QString &title,
|
||||
const style::ForumTopicIcon &st);
|
||||
[[nodiscard]] TextWithEntities ForumTopicIconWithTitle(
|
||||
DocumentId iconId,
|
||||
const QString &title);
|
||||
|
||||
class ForumTopic final : public Thread {
|
||||
public:
|
||||
|
@ -107,6 +110,7 @@ public:
|
|||
[[nodiscard]] MsgId lastKnownServerMessageId() const;
|
||||
|
||||
[[nodiscard]] QString title() const;
|
||||
[[nodiscard]] TextWithEntities titleWithIcon() const;
|
||||
void applyTitle(const QString &title);
|
||||
[[nodiscard]] DocumentId iconId() const;
|
||||
void applyIconId(DocumentId iconId);
|
||||
|
|
|
@ -15,9 +15,7 @@ QString ReactionEntityData(const ReactionId &id) {
|
|||
if (id.empty()) {
|
||||
return {};
|
||||
} else if (const auto custom = id.custom()) {
|
||||
return SerializeCustomEmojiId({
|
||||
.id = custom,
|
||||
});
|
||||
return SerializeCustomEmojiId(custom);
|
||||
}
|
||||
return u"default:"_q + id.emoji();
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "chat_helpers/stickers_lottie.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/text/text_custom_emoji.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
@ -91,7 +92,7 @@ class CustomEmojiLoader final
|
|||
public:
|
||||
CustomEmojiLoader(
|
||||
not_null<Session*> owner,
|
||||
const CustomEmojiId id,
|
||||
DocumentId id,
|
||||
SizeTag tag,
|
||||
int sizeOverride);
|
||||
CustomEmojiLoader(
|
||||
|
@ -144,7 +145,7 @@ private:
|
|||
|
||||
[[nodiscard]] static std::variant<Resolve, Lookup, Load> InitialState(
|
||||
not_null<Session*> owner,
|
||||
const CustomEmojiId &id);
|
||||
DocumentId id);
|
||||
|
||||
std::variant<Resolve, Lookup, Load> _state;
|
||||
ushort _sizeOverride = 0;
|
||||
|
@ -154,7 +155,7 @@ private:
|
|||
|
||||
CustomEmojiLoader::CustomEmojiLoader(
|
||||
not_null<Session*> owner,
|
||||
const CustomEmojiId id,
|
||||
DocumentId id,
|
||||
SizeTag tag,
|
||||
int sizeOverride)
|
||||
: _state(InitialState(owner, id))
|
||||
|
@ -368,9 +369,9 @@ void CustomEmojiLoader::check() {
|
|||
|
||||
auto CustomEmojiLoader::InitialState(
|
||||
not_null<Session*> owner,
|
||||
const CustomEmojiId &id)
|
||||
DocumentId id)
|
||||
-> std::variant<Resolve, Lookup, Load> {
|
||||
const auto document = owner->document(id.id);
|
||||
const auto document = owner->document(id);
|
||||
if (document->sticker()) {
|
||||
return Lookup{ document };
|
||||
}
|
||||
|
@ -504,8 +505,8 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
|||
SizeTag tag,
|
||||
int sizeOverride) {
|
||||
const auto parsed = ParseCustomEmojiData(data);
|
||||
return parsed.id
|
||||
? create(parsed.id, std::move(update), tag, sizeOverride)
|
||||
return parsed
|
||||
? create(parsed, std::move(update), tag, sizeOverride)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
|
@ -532,7 +533,7 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
|||
void CustomEmojiManager::resolve(
|
||||
QStringView data,
|
||||
not_null<Listener*> listener) {
|
||||
resolve(ParseCustomEmojiData(data).id, listener);
|
||||
resolve(ParseCustomEmojiData(data), listener);
|
||||
}
|
||||
|
||||
void CustomEmojiManager::resolve(
|
||||
|
@ -619,7 +620,7 @@ auto CustomEmojiManager::createLoaderWithSetId(
|
|||
) -> LoaderWithSetId {
|
||||
auto result = std::make_unique<CustomEmojiLoader>(
|
||||
_owner,
|
||||
CustomEmojiId{ .id = documentId },
|
||||
documentId,
|
||||
tag,
|
||||
sizeOverride);
|
||||
if (const auto document = result->document()) {
|
||||
|
@ -882,18 +883,24 @@ int FrameSizeFromTag(SizeTag tag) {
|
|||
return Ui::Text::AdjustCustomEmojiSize(emoji / factor) * factor;
|
||||
}
|
||||
|
||||
QString SerializeCustomEmojiId(const CustomEmojiId &id) {
|
||||
return QString::number(id.id);
|
||||
QString SerializeCustomEmojiId(DocumentId id) {
|
||||
return QString::number(id);
|
||||
}
|
||||
|
||||
QString SerializeCustomEmojiId(not_null<DocumentData*> document) {
|
||||
return SerializeCustomEmojiId({
|
||||
.id = document->id,
|
||||
});
|
||||
return SerializeCustomEmojiId(document->id);
|
||||
}
|
||||
|
||||
CustomEmojiId ParseCustomEmojiData(QStringView data) {
|
||||
return { .id = data.toULongLong() };
|
||||
DocumentId ParseCustomEmojiData(QStringView data) {
|
||||
return data.toULongLong();
|
||||
}
|
||||
|
||||
TextWithEntities SingleCustomEmoji(DocumentId id) {
|
||||
return Ui::Text::SingleCustomEmoji(SerializeCustomEmojiId(id));
|
||||
}
|
||||
|
||||
TextWithEntities SingleCustomEmoji(not_null<DocumentData*> document) {
|
||||
return SingleCustomEmoji(document->id);
|
||||
}
|
||||
|
||||
bool AllowEmojiWithoutPremium(not_null<PeerData*> peer) {
|
||||
|
|
|
@ -21,10 +21,6 @@ namespace Data {
|
|||
class Session;
|
||||
class CustomEmojiLoader;
|
||||
|
||||
struct CustomEmojiId {
|
||||
DocumentId id = 0;
|
||||
};
|
||||
|
||||
enum class CustomEmojiSizeTag : uchar {
|
||||
Normal,
|
||||
Large,
|
||||
|
@ -176,10 +172,14 @@ private:
|
|||
|
||||
[[nodiscard]] int FrameSizeFromTag(CustomEmojiManager::SizeTag tag);
|
||||
|
||||
[[nodiscard]] QString SerializeCustomEmojiId(const CustomEmojiId &id);
|
||||
[[nodiscard]] QString SerializeCustomEmojiId(DocumentId id);
|
||||
[[nodiscard]] QString SerializeCustomEmojiId(
|
||||
not_null<DocumentData*> document);
|
||||
[[nodiscard]] CustomEmojiId ParseCustomEmojiData(QStringView data);
|
||||
[[nodiscard]] DocumentId ParseCustomEmojiData(QStringView data);
|
||||
|
||||
[[nodiscard]] TextWithEntities SingleCustomEmoji(DocumentId id);
|
||||
[[nodiscard]] TextWithEntities SingleCustomEmoji(
|
||||
not_null<DocumentData*> document);
|
||||
|
||||
[[nodiscard]] bool AllowEmojiWithoutPremium(not_null<PeerData*> peer);
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ defaultDialogRow: DialogRow {
|
|||
nameLeft: 68px;
|
||||
nameTop: 10px;
|
||||
textLeft: 68px;
|
||||
textTop: 35px;
|
||||
textTop: 34px;
|
||||
}
|
||||
|
||||
dialogsOnlineBadgeStroke: 2px;
|
||||
|
@ -460,3 +460,7 @@ chooseTopicListItem: PeerListItem(defaultPeerListItem) {
|
|||
chooseTopicList: PeerList(defaultPeerList) {
|
||||
item: chooseTopicListItem;
|
||||
}
|
||||
|
||||
dialogsTopicArrow: icon{{ "dialogs/dialogs_topic_arrow", dialogsTextFgService }};
|
||||
dialogsTopicArrowSkip: 13px;
|
||||
dialogsTopicArrowTop: 4px;
|
||||
|
|
|
@ -921,7 +921,7 @@ void RowPainter::Paint(
|
|||
view->prepare(
|
||||
item,
|
||||
[=] { entry->updateChatListEntry(); },
|
||||
{});
|
||||
{ .ignoreTopic = (!history || !peer->isForum()) });
|
||||
}
|
||||
view->paint(p, rect, context);
|
||||
}
|
||||
|
|
|
@ -18,19 +18,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/painter.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "lang/lang_text_entity.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
|
||||
namespace {
|
||||
|
||||
template <ushort kTag>
|
||||
struct TextWithTagOffset {
|
||||
TextWithTagOffset(QString text) : text(text) {
|
||||
TextWithTagOffset(TextWithEntities text) : text(std::move(text)) {
|
||||
}
|
||||
TextWithTagOffset(QString text) : text({ std::move(text) }) {
|
||||
}
|
||||
static TextWithTagOffset FromString(const QString &text) {
|
||||
return { text };
|
||||
return { { text } };
|
||||
}
|
||||
|
||||
QString text;
|
||||
TextWithEntities text;
|
||||
int offset = -1;
|
||||
};
|
||||
|
||||
|
@ -52,12 +55,12 @@ TextWithTagOffset<kTag> ReplaceTag<TextWithTagOffset<kTag>>::Call(
|
|||
ushort tag,
|
||||
const TextWithTagOffset<kTag> &replacement) {
|
||||
const auto replacementPosition = FindTagReplacementPosition(
|
||||
original.text,
|
||||
original.text.text,
|
||||
tag);
|
||||
if (replacementPosition < 0) {
|
||||
return std::move(original);
|
||||
}
|
||||
original.text = ReplaceTag<QString>::Replace(
|
||||
original.text = ReplaceTag<TextWithEntities>::Replace(
|
||||
std::move(original.text),
|
||||
replacement.text,
|
||||
replacementPosition);
|
||||
|
@ -65,7 +68,8 @@ TextWithTagOffset<kTag> ReplaceTag<TextWithTagOffset<kTag>>::Call(
|
|||
original.offset = replacementPosition;
|
||||
} else if (original.offset > replacementPosition) {
|
||||
constexpr auto kReplaceCommandLength = 4;
|
||||
original.offset += replacement.text.size() - kReplaceCommandLength;
|
||||
const auto replacementSize = replacement.text.text.size();
|
||||
original.offset += replacementSize - kReplaceCommandLength;
|
||||
}
|
||||
return std::move(original);
|
||||
}
|
||||
|
@ -105,6 +109,7 @@ struct MessageView::LoadingContext {
|
|||
|
||||
MessageView::MessageView()
|
||||
: _senderCache(st::dialogsTextWidthMin)
|
||||
, _topicCache(st::dialogsTextWidthMin)
|
||||
, _textCache(st::dialogsTextWidthMin) {
|
||||
}
|
||||
|
||||
|
@ -130,28 +135,46 @@ void MessageView::prepare(
|
|||
ToPreviewOptions options) {
|
||||
options.existing = &_imagesCache;
|
||||
auto preview = item->toPreview(options);
|
||||
if (!preview.images.empty() && preview.imagesInTextPosition > 0) {
|
||||
auto sender = ::Ui::Text::Mid(
|
||||
preview.text,
|
||||
0,
|
||||
preview.imagesInTextPosition);
|
||||
TextUtilities::Trim(sender);
|
||||
_senderCache.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
std::move(sender),
|
||||
DialogTextOptions());
|
||||
preview.text = ::Ui::Text::Mid(
|
||||
preview.text,
|
||||
preview.imagesInTextPosition);
|
||||
} else {
|
||||
_senderCache = { st::dialogsTextWidthMin };
|
||||
}
|
||||
TextUtilities::Trim(preview.text);
|
||||
const auto hasImages = !preview.images.empty();
|
||||
const auto hasArrow = (preview.arrowInTextPosition > 0)
|
||||
&& (preview.imagesInTextPosition > preview.arrowInTextPosition);
|
||||
const auto history = item->history();
|
||||
const auto context = Core::MarkedTextContext{
|
||||
.session = &history->session(),
|
||||
.customEmojiRepaint = customEmojiRepaint,
|
||||
};
|
||||
const auto senderTill = (preview.arrowInTextPosition > 0)
|
||||
? preview.arrowInTextPosition
|
||||
: preview.imagesInTextPosition;
|
||||
if ((hasImages || hasArrow) && senderTill > 0) {
|
||||
auto sender = Text::Mid(preview.text, 0, senderTill);
|
||||
TextUtilities::Trim(sender);
|
||||
_senderCache.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
std::move(sender),
|
||||
DialogTextOptions());
|
||||
const auto topicTill = preview.imagesInTextPosition;
|
||||
if (hasArrow && hasImages) {
|
||||
auto topic = Text::Mid(
|
||||
preview.text,
|
||||
senderTill,
|
||||
topicTill - senderTill);
|
||||
TextUtilities::Trim(topic);
|
||||
_topicCache.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
std::move(topic),
|
||||
DialogTextOptions(),
|
||||
context);
|
||||
preview.text = Text::Mid(preview.text, topicTill);
|
||||
} else {
|
||||
preview.text = Text::Mid(preview.text, senderTill);
|
||||
_topicCache = { st::dialogsTextWidthMin };
|
||||
}
|
||||
} else {
|
||||
_topicCache = { st::dialogsTextWidthMin };
|
||||
_senderCache = { st::dialogsTextWidthMin };
|
||||
}
|
||||
TextUtilities::Trim(preview.text);
|
||||
_textCache.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
DialogsPreviewText(std::move(preview.text)),
|
||||
|
@ -193,16 +216,46 @@ void MessageView::paint(
|
|||
: st::dialogsTextPalette);
|
||||
|
||||
auto rect = geometry;
|
||||
const auto lines = rect.height() / st::dialogsTextFont->height;
|
||||
if (!_senderCache.isEmpty()) {
|
||||
_senderCache.draw(p, {
|
||||
.position = rect.topLeft(),
|
||||
.availableWidth = rect.width(),
|
||||
.palette = palette,
|
||||
.elisionLines = rect.height() / st::dialogsTextFont->height,
|
||||
.elisionLines = lines,
|
||||
});
|
||||
const auto skip = st::dialogsMiniPreviewSkip
|
||||
+ st::dialogsMiniPreviewRight;
|
||||
rect.setLeft(rect.x() + _senderCache.maxWidth() + skip);
|
||||
rect.setLeft(rect.x() + _senderCache.maxWidth());
|
||||
if (!_topicCache.isEmpty() || _imagesCache.empty()) {
|
||||
const auto skip = st::dialogsTopicArrowSkip;
|
||||
if (rect.width() >= skip) {
|
||||
const auto &icon = st::dialogsTopicArrow;
|
||||
icon.paint(
|
||||
p,
|
||||
rect.x() + (skip - icon.width()) / 2,
|
||||
rect.y() + st::dialogsTopicArrowTop,
|
||||
geometry.width());
|
||||
}
|
||||
rect.setLeft(rect.x() + skip);
|
||||
}
|
||||
if (!_topicCache.isEmpty()) {
|
||||
if (!rect.isEmpty()) {
|
||||
_topicCache.draw(p, {
|
||||
.position = rect.topLeft(),
|
||||
.availableWidth = rect.width(),
|
||||
.palette = palette,
|
||||
.spoiler = Text::DefaultSpoilerCache(),
|
||||
.now = context.now,
|
||||
.paused = context.paused,
|
||||
.elisionLines = lines,
|
||||
});
|
||||
}
|
||||
rect.setLeft(rect.x() + _topicCache.maxWidth());
|
||||
}
|
||||
if (!_imagesCache.empty()) {
|
||||
const auto skip = st::dialogsMiniPreviewSkip
|
||||
+ st::dialogsMiniPreviewRight;
|
||||
rect.setLeft(rect.x() + skip);
|
||||
}
|
||||
}
|
||||
for (const auto &image : _imagesCache) {
|
||||
if (rect.width() < st::dialogsMiniPreview) {
|
||||
|
@ -229,30 +282,48 @@ void MessageView::paint(
|
|||
.spoiler = Text::DefaultSpoilerCache(),
|
||||
.now = context.now,
|
||||
.paused = context.paused,
|
||||
.elisionLines = rect.height() / st::dialogsTextFont->height,
|
||||
.elisionLines = lines,
|
||||
});
|
||||
}
|
||||
|
||||
HistoryView::ItemPreview PreviewWithSender(
|
||||
HistoryView::ItemPreview &&preview,
|
||||
const TextWithEntities &sender) {
|
||||
const auto textWithOffset = tr::lng_dialogs_text_with_from(
|
||||
const QString &sender,
|
||||
TextWithEntities topic) {
|
||||
auto senderWithOffset = topic.empty()
|
||||
? TextWithTagOffset<lt_from>::FromString(sender)
|
||||
: tr::lng_dialogs_text_from_in_topic(
|
||||
tr::now,
|
||||
lt_from,
|
||||
{ sender },
|
||||
lt_topic,
|
||||
std::move(topic),
|
||||
TextWithTagOffset<lt_from>::FromString);
|
||||
auto wrappedWithOffset = tr::lng_dialogs_text_from_wrapped(
|
||||
tr::now,
|
||||
lt_from,
|
||||
std::move(senderWithOffset.text),
|
||||
TextWithTagOffset<lt_from>::FromString);
|
||||
const auto wrappedSize = wrappedWithOffset.text.text.size();
|
||||
auto fullWithOffset = tr::lng_dialogs_text_with_from(
|
||||
tr::now,
|
||||
lt_from_part,
|
||||
sender.text,
|
||||
lt_message,
|
||||
preview.text.text,
|
||||
TextWithTagOffset<lt_from_part>::FromString);
|
||||
preview.text = tr::lng_dialogs_text_with_from(
|
||||
tr::now,
|
||||
lt_from_part,
|
||||
sender,
|
||||
Ui::Text::PlainLink(std::move(wrappedWithOffset.text)),
|
||||
lt_message,
|
||||
std::move(preview.text),
|
||||
Ui::Text::WithEntities);
|
||||
preview.imagesInTextPosition = (textWithOffset.offset < 0)
|
||||
TextWithTagOffset<lt_from_part>::FromString);
|
||||
preview.text = std::move(fullWithOffset.text);
|
||||
preview.arrowInTextPosition = (fullWithOffset.offset < 0
|
||||
|| wrappedWithOffset.offset < 0
|
||||
|| senderWithOffset.offset < 0)
|
||||
? -1
|
||||
: (fullWithOffset.offset
|
||||
+ wrappedWithOffset.offset
|
||||
+ senderWithOffset.offset
|
||||
+ sender.size());
|
||||
preview.imagesInTextPosition = (fullWithOffset.offset < 0)
|
||||
? 0
|
||||
: textWithOffset.offset + sender.text.size();
|
||||
: (fullWithOffset.offset + wrappedSize);
|
||||
return std::move(preview);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ private:
|
|||
|
||||
mutable const HistoryItem *_textCachedFor = nullptr;
|
||||
mutable Text::String _senderCache;
|
||||
mutable Text::String _topicCache;
|
||||
mutable Text::String _textCache;
|
||||
mutable std::vector<ItemPreviewImage> _imagesCache;
|
||||
mutable std::unique_ptr<LoadingContext> _loadingContext;
|
||||
|
@ -65,6 +66,7 @@ private:
|
|||
|
||||
[[nodiscard]] HistoryView::ItemPreview PreviewWithSender(
|
||||
HistoryView::ItemPreview &&preview,
|
||||
const TextWithEntities &sender);
|
||||
const QString &sender,
|
||||
TextWithEntities topic);
|
||||
|
||||
} // namespace Dialogs::Ui
|
||||
|
|
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_text_entities.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_message_reaction_id.h"
|
||||
|
@ -624,23 +625,10 @@ TextWithEntities GenerateDefaultBannedRightsChangeText(
|
|||
not_null<ChannelData*> channel,
|
||||
const MTPForumTopic &topic) {
|
||||
return topic.match([&](const MTPDforumTopic &data) {
|
||||
const auto wrapIcon = [](DocumentId id) {
|
||||
return TextWithEntities{
|
||||
"@",
|
||||
{ EntityInText(
|
||||
EntityType::CustomEmoji,
|
||||
0,
|
||||
1,
|
||||
Data::SerializeCustomEmojiId({ .id = id }))
|
||||
},
|
||||
};
|
||||
};
|
||||
auto result = (data.vicon_emoji_id() && data.vicon_emoji_id()->v)
|
||||
? wrapIcon(data.vicon_emoji_id()->v)
|
||||
: TextWithEntities();
|
||||
result.append(qs(data.vtitle()));
|
||||
return Ui::Text::Link(
|
||||
std::move(result),
|
||||
Data::ForumTopicIconWithTitle(
|
||||
data.vicon_emoji_id().value_or_empty(),
|
||||
qs(data.vtitle())),
|
||||
u"internal:url:https://t.me/c/%1/%2"_q.arg(
|
||||
peerToChannel(channel->id).bare).arg(
|
||||
data.vid().v));
|
||||
|
|
|
@ -1365,9 +1365,11 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const {
|
|||
if (!sender) {
|
||||
return result;
|
||||
}
|
||||
const auto fromWrapped = Ui::Text::PlainLink(
|
||||
tr::lng_dialogs_text_from_wrapped(tr::now, lt_from, *sender));
|
||||
return Dialogs::Ui::PreviewWithSender(std::move(result), fromWrapped);
|
||||
const auto topic = options.ignoreTopic ? nullptr : this->topic();
|
||||
return Dialogs::Ui::PreviewWithSender(
|
||||
std::move(result),
|
||||
*sender,
|
||||
topic ? topic->titleWithIcon() : TextWithEntities());
|
||||
}
|
||||
|
||||
TextWithEntities HistoryItem::inReplyText() const {
|
||||
|
|
|
@ -636,33 +636,19 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
|||
return result;
|
||||
};
|
||||
|
||||
const auto wrapTopicIcon = [](DocumentId id) {
|
||||
return TextWithEntities{
|
||||
"@",
|
||||
{ EntityInText(
|
||||
EntityType::CustomEmoji,
|
||||
0,
|
||||
1,
|
||||
Data::SerializeCustomEmojiId({ .id = id }))
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
auto prepareTopicCreate = [&](const MTPDmessageActionTopicCreate &action) {
|
||||
auto result = PreparedText{};
|
||||
auto title = TextWithEntities{
|
||||
qs(action.vtitle())
|
||||
};
|
||||
if (const auto icon = action.vicon_emoji_id().value_or_empty()) {
|
||||
title = wrapTopicIcon(icon).append(' ').append(std::move(title));
|
||||
}
|
||||
const auto topicUrl = u"internal:url:https://t.me/c/%1/%2"_q
|
||||
.arg(peerToChannel(history()->peer->id).bare)
|
||||
.arg(id.bare);
|
||||
result.text = tr::lng_action_topic_created(
|
||||
tr::now,
|
||||
lt_topic,
|
||||
Ui::Text::Link(std::move(title), topicUrl),
|
||||
Ui::Text::Link(
|
||||
Data::ForumTopicIconWithTitle(
|
||||
action.vicon_emoji_id().value_or_empty(),
|
||||
qs(action.vtitle())),
|
||||
topicUrl),
|
||||
Ui::Text::WithEntities);
|
||||
return result;
|
||||
};
|
||||
|
@ -684,7 +670,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
|||
lt_link,
|
||||
{ tr::lng_action_topic_placeholder(tr::now) },
|
||||
lt_emoji,
|
||||
wrapTopicIcon(iconId),
|
||||
Data::SingleCustomEmoji(iconId),
|
||||
Ui::Text::WithEntities);
|
||||
} else {
|
||||
result.links.push_back(fromLink());
|
||||
|
@ -699,14 +685,6 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
|||
}
|
||||
} else {
|
||||
result.links.push_back(fromLink());
|
||||
auto title = TextWithEntities{
|
||||
qs(*action.vtitle())
|
||||
};
|
||||
if (const auto icon = action.vicon_emoji_id().value_or_empty()) {
|
||||
title = wrapTopicIcon(icon)
|
||||
.append(' ')
|
||||
.append(std::move(title));
|
||||
}
|
||||
result.text = tr::lng_action_topic_renamed(
|
||||
tr::now,
|
||||
lt_from,
|
||||
|
@ -714,7 +692,9 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
|||
lt_link,
|
||||
{ tr::lng_action_topic_placeholder(tr::now) },
|
||||
lt_title,
|
||||
std::move(title),
|
||||
Data::ForumTopicIconWithTitle(
|
||||
action.vicon_emoji_id().value_or_empty(),
|
||||
qs(*action.vtitle())),
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
if (result.text.empty()) {
|
||||
|
|
|
@ -1311,7 +1311,7 @@ std::vector<StickerSetIdentifier> CollectEmojiPacks(
|
|||
for (const auto &entity : item->originalText().entities) {
|
||||
if (entity.type() == EntityType::CustomEmoji) {
|
||||
const auto data = Data::ParseCustomEmojiData(entity.data());
|
||||
push(data.id);
|
||||
push(data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -690,17 +690,6 @@ auto Element::contextDependentServiceText() -> TextWithLinks {
|
|||
return {};
|
||||
}
|
||||
const auto from = item->from();
|
||||
const auto wrapIcon = [](DocumentId id) {
|
||||
return TextWithEntities{
|
||||
"@",
|
||||
{ EntityInText(
|
||||
EntityType::CustomEmoji,
|
||||
0,
|
||||
1,
|
||||
Data::SerializeCustomEmojiId({ .id = id }))
|
||||
},
|
||||
};
|
||||
};
|
||||
const auto topicUrl = u"internal:url:https://t.me/c/%1/%2"_q
|
||||
.arg(peerToChannel(peerId).bare)
|
||||
.arg(topicRootId.bare);
|
||||
|
@ -715,11 +704,9 @@ auto Element::contextDependentServiceText() -> TextWithLinks {
|
|||
const auto wrapTopic = [&](
|
||||
const QString &title,
|
||||
std::optional<DocumentId> iconId) {
|
||||
auto result = TextWithEntities{ title };
|
||||
auto full = (iconId && *iconId)
|
||||
? wrapIcon(*iconId).append(' ').append(std::move(result))
|
||||
: result;
|
||||
return Ui::Text::Link(std::move(full), topicUrl);
|
||||
return Ui::Text::Link(
|
||||
Data::ForumTopicIconWithTitle(iconId.value_or(0), title),
|
||||
topicUrl);
|
||||
};
|
||||
const auto wrapParentTopic = [&] {
|
||||
const auto forum = history()->asForum();
|
||||
|
@ -783,7 +770,7 @@ auto Element::contextDependentServiceText() -> TextWithLinks {
|
|||
lt_link,
|
||||
placeholderLink(),
|
||||
lt_emoji,
|
||||
wrapIcon(iconId),
|
||||
Data::SingleCustomEmoji(iconId),
|
||||
Ui::Text::WithEntities),
|
||||
{ from->createOpenLink() },
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@ struct ItemPreviewImage {
|
|||
struct ItemPreview {
|
||||
TextWithEntities text;
|
||||
std::vector<ItemPreviewImage> images;
|
||||
int arrowInTextPosition = -1;
|
||||
int imagesInTextPosition = 0;
|
||||
std::any loadingContext;
|
||||
};
|
||||
|
@ -31,7 +32,7 @@ struct ToPreviewOptions {
|
|||
bool hideCaption = false;
|
||||
bool generateImages = true;
|
||||
bool ignoreGroup = false;
|
||||
bool ignoreSpoilers = false;
|
||||
bool ignoreTopic = true;
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -92,7 +92,7 @@ CustomEmoji::CustomEmoji(
|
|||
tag));
|
||||
} else {
|
||||
const auto &data = element.entityData;
|
||||
const auto id = Data::ParseCustomEmojiData(data).id;
|
||||
const auto id = Data::ParseCustomEmojiData(data);
|
||||
const auto document = owner->document(id);
|
||||
if (document->sticker()) {
|
||||
_lines.back().push_back(createStickerPart(document));
|
||||
|
|
|
@ -16,25 +16,28 @@ TextWithEntities ReplaceTag<TextWithEntities>::Call(TextWithEntities &&original,
|
|||
if (replacementPosition < 0) {
|
||||
return std::move(original);
|
||||
}
|
||||
return Replace(std::move(original), replacement, replacementPosition);
|
||||
}
|
||||
|
||||
TextWithEntities ReplaceTag<TextWithEntities>::Replace(TextWithEntities &&original, const TextWithEntities &replacement, int start) {
|
||||
auto result = TextWithEntities();
|
||||
result.text = ReplaceTag<QString>::Replace(std::move(original.text), replacement.text, replacementPosition);
|
||||
result.text = ReplaceTag<QString>::Replace(std::move(original.text), replacement.text, start);
|
||||
auto originalEntitiesCount = original.entities.size();
|
||||
auto replacementEntitiesCount = replacement.entities.size();
|
||||
if (originalEntitiesCount != 0 || replacementEntitiesCount != 0) {
|
||||
result.entities.reserve(originalEntitiesCount + replacementEntitiesCount);
|
||||
|
||||
auto replacementEnd = replacementPosition + int(replacement.text.size());
|
||||
auto replacementEnd = start + int(replacement.text.size());
|
||||
auto replacementEntity = replacement.entities.cbegin();
|
||||
auto addReplacementEntitiesUntil = [&replacementEntity, &replacement, &result, replacementPosition, replacementEnd](int untilPosition) {
|
||||
auto addReplacementEntitiesUntil = [&](int untilPosition) {
|
||||
while (replacementEntity != replacement.entities.cend()) {
|
||||
auto newOffset = replacementPosition + replacementEntity->offset();
|
||||
auto newOffset = start + replacementEntity->offset();
|
||||
if (newOffset >= untilPosition) {
|
||||
return;
|
||||
}
|
||||
auto newEnd = newOffset + replacementEntity->length();
|
||||
newOffset = std::clamp(newOffset, replacementPosition, replacementEnd);
|
||||
newEnd = std::clamp(newEnd, replacementPosition, replacementEnd);
|
||||
newOffset = std::clamp(newOffset, start, replacementEnd);
|
||||
newEnd = std::clamp(newEnd, start, replacementEnd);
|
||||
if (auto newLength = newEnd - newOffset) {
|
||||
result.entities.push_back({ replacementEntity->type(), newOffset, newLength, replacementEntity->data() });
|
||||
}
|
||||
|
@ -46,10 +49,10 @@ TextWithEntities ReplaceTag<TextWithEntities>::Call(TextWithEntities &&original,
|
|||
// Transform the entity by the replacement.
|
||||
auto offset = entity.offset();
|
||||
auto end = offset + entity.length();
|
||||
if (offset > replacementPosition) {
|
||||
if (offset > start) {
|
||||
offset = offset + replacement.text.size() - kTagReplacementSize;
|
||||
}
|
||||
if (end > replacementPosition) {
|
||||
if (end > start) {
|
||||
end = end + replacement.text.size() - kTagReplacementSize;
|
||||
}
|
||||
offset = std::clamp(offset, 0, int(result.text.size()));
|
||||
|
|
|
@ -27,6 +27,7 @@ struct ReplaceTag;
|
|||
template <>
|
||||
struct ReplaceTag<TextWithEntities> {
|
||||
static TextWithEntities Call(TextWithEntities &&original, ushort tag, const TextWithEntities &replacement);
|
||||
static TextWithEntities Replace(TextWithEntities &&original, const TextWithEntities &replacement, int start);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -848,7 +848,7 @@ TextWithEntities Manager::ComposeReactionEmoji(
|
|||
EntityType::CustomEmoji,
|
||||
0,
|
||||
text.size(),
|
||||
Data::SerializeCustomEmojiId(Data::CustomEmojiId{ id }))
|
||||
Data::SerializeCustomEmojiId(id))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -968,24 +968,9 @@ void Notification::updateNotifyDisplay() {
|
|||
|
||||
const auto topicWithChat = [&]() -> TextWithEntities {
|
||||
const auto name = _history->peer->name();
|
||||
const auto wrapIcon = [](DocumentId id) {
|
||||
return TextWithEntities{
|
||||
"@",
|
||||
{ EntityInText(
|
||||
EntityType::CustomEmoji,
|
||||
0,
|
||||
1,
|
||||
Data::SerializeCustomEmojiId({.id = id }))
|
||||
},
|
||||
};
|
||||
};
|
||||
if (!_topic) {
|
||||
return { name };
|
||||
}
|
||||
auto start = _topic->iconId()
|
||||
? wrapIcon(_topic->iconId())
|
||||
: TextWithEntities();
|
||||
return start.append(_topic->title() + u" ("_q + name + ')');
|
||||
return _topic
|
||||
? _topic->titleWithIcon().append(u" ("_q + name + ')')
|
||||
: TextWithEntities{ name };
|
||||
};
|
||||
auto title = options.hideNameAndPhoto
|
||||
? TextWithEntities{ u"Telegram Desktop"_q }
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit c3616927ebfcbe98375189be87821bea202d9587
|
||||
Subproject commit 4539d0bab4ffc335de79b77ee9632f6a7b59d48b
|
Loading…
Add table
Reference in a new issue