mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-11 08:48:14 +00:00
Context-aware phrases in topic service messages.
This commit is contained in:
parent
97d8aa0a0d
commit
aac91a19ca
@ -1508,12 +1508,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_webview_data_done" = "You have just successfully transferred data from the «{text}» button to the bot.";
|
||||
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
||||
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
||||
"lng_action_topic_created" = "Topic created";
|
||||
"lng_action_topic_renamed" = "Topic renamed to «{title}»";
|
||||
"lng_action_topic_icon_changed" = "Topic icon changed to {emoji}";
|
||||
"lng_action_topic_icon_removed" = "Topic icon removed";
|
||||
"lng_action_topic_closed" = "Topic closed";
|
||||
"lng_action_topic_reopened" = "Topic reopened";
|
||||
"lng_action_topic_created_inside" = "Topic created";
|
||||
"lng_action_topic_closed_inside" = "Topics closed";
|
||||
"lng_action_topic_reopened_inside" = "Topic reopened";
|
||||
"lng_action_topic_created" = "{topic} — was created";
|
||||
"lng_action_topic_closed" = "{topic} — was closed";
|
||||
"lng_action_topic_reopened" = "{topic} — was reopened";
|
||||
"lng_action_topic_placeholder" = "topic";
|
||||
"lng_action_topic_renamed" = "{from} renamed the {link} to «{title}»";
|
||||
"lng_action_topic_icon_changed" = "{from} changed the {link} icon to {emoji}";
|
||||
"lng_action_topic_icon_removed" = "{from} removed the {link} icon";
|
||||
|
||||
"lng_premium_gift_duration_months#one" = "for {count} month";
|
||||
"lng_premium_gift_duration_months#other" = "for {count} months";
|
||||
@ -2019,6 +2023,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_replies_header#one" = "{count} reply";
|
||||
"lng_replies_header#other" = "{count} replies";
|
||||
"lng_replies_header_none" = "Replies";
|
||||
"lng_replies_view_topic" = "View in Topic";
|
||||
"lng_comments_header#one" = "{count} comment";
|
||||
"lng_comments_header#other" = "{count} comments";
|
||||
"lng_comments_header_none" = "Comments";
|
||||
@ -3530,7 +3535,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_forum_topic_closed" = "This topic is now closed.";
|
||||
"lng_forum_topic_delete" = "Delete";
|
||||
"lng_forum_topic_delete_sure" = "Are you sure you want to delete this topic?";
|
||||
"lng_forum_topic_created_title_my" = "Almost done!";
|
||||
"lng_forum_topic_created_body_my" = "Send the first message to start this topic.";
|
||||
"lng_forum_topic_created_title" = "Topic started!";
|
||||
"lng_forum_topic_created_body" = "Send a message to open the discussion";
|
||||
"lng_forum_topics_switch" = "Topics";
|
||||
"lng_forum_topics_not_enough#one" = "Only groups with more than **{count} member** can have topics enabled.";
|
||||
"lng_forum_topics_not_enough#other" = "Only groups with more than **{count} members** can have topics enabled.";
|
||||
"lng_forum_topics_no_discussion" = "Topics can't be enabled in discussion groups at the moment.";
|
||||
"lng_forum_no_topics" = "No topics currently created in this forum.";
|
||||
"lng_forum_create_topic" = "Create topic";
|
||||
"lng_forum_discard_sure" = "Are you sure you want to discard this topic?";
|
||||
|
@ -639,16 +639,11 @@ TextWithEntities GenerateDefaultBannedRightsChangeText(
|
||||
? wrapIcon(data.vicon_emoji_id()->v)
|
||||
: TextWithEntities();
|
||||
result.append(qs(data.vtitle()));
|
||||
result.entities.insert(
|
||||
result.entities.begin(),
|
||||
EntityInText(
|
||||
EntityType::CustomUrl,
|
||||
0,
|
||||
result.text.size(),
|
||||
u"https://t.me/c/%1?topic=%2"_q.arg(
|
||||
peerToChannel(channel->id).bare).arg(
|
||||
data.vid().v)));
|
||||
return result;
|
||||
return Ui::Text::Link(
|
||||
std::move(result),
|
||||
u"internal:url:https://t.me/c/%1?topic=%2"_q.arg(
|
||||
peerToChannel(channel->id).bare).arg(
|
||||
data.vid().v));
|
||||
}, [](const MTPDforumTopicDeleted &) {
|
||||
return TextWithEntities{ u"Deleted"_q };
|
||||
});
|
||||
|
@ -2109,7 +2109,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
? item->id
|
||||
: item->replyToTop();
|
||||
const auto phrase = topicRootId
|
||||
? u"View in Thread"_q // #TODO lang-forum
|
||||
? tr::lng_replies_view_topic(tr::now)
|
||||
: (repliesCount > 0)
|
||||
? tr::lng_replies_view(
|
||||
tr::now,
|
||||
|
@ -638,7 +638,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||
|
||||
auto prepareTopicCreate = [&](const MTPDmessageActionTopicCreate &action) {
|
||||
auto result = PreparedText{};
|
||||
result.text = { tr::lng_action_topic_created(tr::now) };
|
||||
result.text = { tr::lng_action_topic_created_inside(tr::now) };
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -657,23 +657,34 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||
};
|
||||
if (const auto closed = action.vclosed()) {
|
||||
result.text = { mtpIsTrue(*closed)
|
||||
? tr::lng_action_topic_closed(tr::now)
|
||||
: tr::lng_action_topic_reopened(tr::now) };
|
||||
? tr::lng_action_topic_closed_inside(tr::now)
|
||||
: tr::lng_action_topic_reopened_inside(tr::now) };
|
||||
} else if (!action.vtitle()) {
|
||||
if (const auto icon = action.vicon_emoji_id()) {
|
||||
if (const auto iconId = icon->v) {
|
||||
result.links.push_back(fromLink());
|
||||
result.text = tr::lng_action_topic_icon_changed(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLinkText(), // Link 1.
|
||||
lt_link,
|
||||
{ tr::lng_action_topic_placeholder(tr::now) },
|
||||
lt_emoji,
|
||||
wrapIcon(iconId),
|
||||
Ui::Text::WithEntities);
|
||||
} else {
|
||||
result.text = {
|
||||
tr::lng_action_topic_icon_removed(tr::now)
|
||||
};
|
||||
result.links.push_back(fromLink());
|
||||
result.text = tr::lng_action_topic_icon_removed(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLinkText(), // Link 1.
|
||||
lt_link,
|
||||
{ tr::lng_action_topic_placeholder(tr::now) },
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.links.push_back(fromLink());
|
||||
auto title = TextWithEntities{
|
||||
qs(*action.vtitle())
|
||||
};
|
||||
@ -682,6 +693,10 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||
}
|
||||
result.text = tr::lng_action_topic_renamed(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLinkText(), // Link 1.
|
||||
lt_link,
|
||||
{ tr::lng_action_topic_placeholder(tr::now) },
|
||||
lt_title,
|
||||
std::move(title),
|
||||
Ui::Text::WithEntities);
|
||||
@ -996,7 +1011,7 @@ HistoryService::PreparedText HistoryService::preparePinnedText() {
|
||||
original = Ui::Text::Mid(original, 0, cutAt).append(
|
||||
Ui::kQEllipsis);
|
||||
}
|
||||
original = Ui::Text::Wrapped(
|
||||
original = Ui::Text::Link(
|
||||
Ui::Text::Filtered(
|
||||
std::move(original),
|
||||
{
|
||||
@ -1005,8 +1020,7 @@ HistoryService::PreparedText HistoryService::preparePinnedText() {
|
||||
EntityType::Italic,
|
||||
EntityType::CustomEmoji,
|
||||
}),
|
||||
EntityType::CustomUrl,
|
||||
Ui::Text::Link({}, 2).entities.front().data());
|
||||
2);
|
||||
result.text = tr::lng_action_pinned_message(
|
||||
tr::now,
|
||||
lt_from,
|
||||
@ -1478,23 +1492,44 @@ void HistoryService::createFromMtp(const MTPDmessage &message) {
|
||||
}
|
||||
|
||||
void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||
const auto type = message.vaction().type();
|
||||
const auto &action = message.vaction();
|
||||
const auto type = action.type();
|
||||
if (type == mtpc_messageActionPinMessage) {
|
||||
UpdateComponents(HistoryServicePinned::Bit());
|
||||
} else if (type == mtpc_messageActionTopicCreate
|
||||
|| type == mtpc_messageActionTopicEdit) {
|
||||
UpdateComponents(HistoryServiceTopicInfo::Bit());
|
||||
Get<HistoryServiceTopicInfo>()->topicPost = true;
|
||||
const auto info = Get<HistoryServiceTopicInfo>();
|
||||
info->topicPost = true;
|
||||
if (type == mtpc_messageActionTopicEdit) {
|
||||
const auto &data = action.c_messageActionTopicEdit();
|
||||
if (const auto title = data.vtitle()) {
|
||||
info->title = qs(*title);
|
||||
info->renamed = true;
|
||||
}
|
||||
if (const auto icon = data.vicon_emoji_id()) {
|
||||
info->iconId = icon->v;
|
||||
info->reiconed = true;
|
||||
}
|
||||
if (const auto closed = data.vclosed()) {
|
||||
info->closed = mtpIsTrue(*closed);
|
||||
info->reopened = !info->closed;
|
||||
}
|
||||
} else {
|
||||
const auto &data = action.c_messageActionTopicCreate();
|
||||
info->title = qs(data.vtitle());
|
||||
info->iconId = data.vicon_emoji_id().value_or_empty();
|
||||
}
|
||||
} else if (type == mtpc_messageActionSetChatTheme) {
|
||||
setupChatThemeChange();
|
||||
} else if (type == mtpc_messageActionSetMessagesTTL) {
|
||||
setupTTLChange();
|
||||
} else if (type == mtpc_messageActionGameScore) {
|
||||
const auto &data = message.vaction().c_messageActionGameScore();
|
||||
const auto &data = action.c_messageActionGameScore();
|
||||
UpdateComponents(HistoryServiceGameScore::Bit());
|
||||
Get<HistoryServiceGameScore>()->score = data.vscore().v;
|
||||
} else if (type == mtpc_messageActionPaymentSent) {
|
||||
const auto &data = message.vaction().c_messageActionPaymentSent();
|
||||
const auto &data = action.c_messageActionPaymentSent();
|
||||
UpdateComponents(HistoryServicePayment::Bit());
|
||||
const auto amount = data.vtotal_amount().v;
|
||||
const auto currency = qs(data.vcurrency());
|
||||
@ -1521,10 +1556,10 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||
|| type == mtpc_messageActionGroupCallScheduled) {
|
||||
const auto started = (type == mtpc_messageActionGroupCall);
|
||||
const auto &callData = started
|
||||
? message.vaction().c_messageActionGroupCall().vcall()
|
||||
: message.vaction().c_messageActionGroupCallScheduled().vcall();
|
||||
? action.c_messageActionGroupCall().vcall()
|
||||
: action.c_messageActionGroupCallScheduled().vcall();
|
||||
const auto duration = started
|
||||
? message.vaction().c_messageActionGroupCall().vduration()
|
||||
? action.c_messageActionGroupCall().vduration()
|
||||
: tl::conditional<MTPint>();
|
||||
if (duration) {
|
||||
RemoveComponents(HistoryServiceOngoingCall::Bit());
|
||||
@ -1535,7 +1570,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||
call->link = GroupCallClickHandler(history()->peer, call->id);
|
||||
}
|
||||
} else if (type == mtpc_messageActionInviteToGroupCall) {
|
||||
const auto &data = message.vaction().c_messageActionInviteToGroupCall();
|
||||
const auto &data = action.c_messageActionInviteToGroupCall();
|
||||
const auto id = CallIdFromInput(data.vcall());
|
||||
const auto peer = history()->peer;
|
||||
const auto has = PeerHasThisCall(peer, id);
|
||||
@ -1586,7 +1621,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||
}
|
||||
});
|
||||
}
|
||||
setMessageByAction(message.vaction());
|
||||
setMessageByAction(action);
|
||||
}
|
||||
|
||||
const std::vector<ClickHandlerPtr> &HistoryService::customTextLinks() const {
|
||||
|
@ -30,6 +30,12 @@ struct HistoryServicePinned
|
||||
struct HistoryServiceTopicInfo
|
||||
: public RuntimeComponent<HistoryServiceTopicInfo, HistoryItem>
|
||||
, public HistoryServiceDependentData {
|
||||
QString title;
|
||||
DocumentId iconId = 0;
|
||||
bool closed = false;
|
||||
bool reopened = false;
|
||||
bool reiconed = false;
|
||||
bool renamed = false;
|
||||
};
|
||||
|
||||
struct HistoryServiceGameScore
|
||||
|
@ -633,7 +633,7 @@ bool AddViewRepliesAction(
|
||||
: item->replyToTop();
|
||||
const auto highlightId = topicRootId ? item->id : 0;
|
||||
const auto phrase = topicRootId
|
||||
? u"View in Thread"_q // #TODO lang-forum
|
||||
? tr::lng_replies_view_topic(tr::now)
|
||||
: (repliesCount > 0)
|
||||
? tr::lng_replies_view(
|
||||
tr::now,
|
||||
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/view/history_view_spoiler_click_handler.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_service.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
@ -37,10 +38,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/item_text_options.h"
|
||||
#include "ui/painter.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_groups.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_sponsored_messages.h"
|
||||
#include "data/data_message_reactions.h"
|
||||
@ -657,6 +661,141 @@ int Element::textHeightFor(int textWidth) {
|
||||
return _textHeight;
|
||||
}
|
||||
|
||||
auto Element::contextDependentServiceText() -> TextWithLinks {
|
||||
if (_delegate->elementContext() == Context::Replies) {
|
||||
return {};
|
||||
}
|
||||
const auto item = data();
|
||||
const auto info = item->Get<HistoryServiceTopicInfo>();
|
||||
if (!info) {
|
||||
return {};
|
||||
}
|
||||
const auto peerId = item->history()->peer->id;
|
||||
const auto topicRootId = item->topicRootId();
|
||||
if (!topicRootId || !peerIsChannel(peerId)) {
|
||||
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?topic=%2"_q
|
||||
.arg(peerToChannel(peerId).bare)
|
||||
.arg(topicRootId.bare);
|
||||
const auto fromLink = [&](int index) {
|
||||
return Ui::Text::Link(from->name(), index);
|
||||
};
|
||||
const auto placeholderLink = [&] {
|
||||
return Ui::Text::Link(
|
||||
tr::lng_action_topic_placeholder(tr::now),
|
||||
topicUrl);
|
||||
};
|
||||
const auto wrapTopic = [&](
|
||||
const QString &title,
|
||||
std::optional<DocumentId> iconId) {
|
||||
auto result = TextWithEntities{ title };
|
||||
auto full = iconId
|
||||
? wrapIcon(*iconId).append(' ').append(std::move(result))
|
||||
: result;
|
||||
return Ui::Text::Link(std::move(full), topicUrl);
|
||||
};
|
||||
const auto wrapParentTopic = [&] {
|
||||
const auto forum = history()->peer->forum();
|
||||
if (!forum || forum->topicDeleted(topicRootId)) {
|
||||
return wrapTopic(
|
||||
tr::lng_deleted_message(tr::now),
|
||||
std::nullopt);
|
||||
} else if (const auto topic = forum->topicFor(topicRootId)) {
|
||||
return wrapTopic(topic->title(), topic->iconId());
|
||||
} else {
|
||||
forum->requestTopic(topicRootId, crl::guard(this, [=] {
|
||||
itemTextUpdated();
|
||||
history()->owner().requestViewResize(this);
|
||||
}));
|
||||
return wrapTopic(
|
||||
tr::lng_profile_loading(tr::now),
|
||||
std::nullopt);
|
||||
}
|
||||
};
|
||||
|
||||
if (info->closed) {
|
||||
return {
|
||||
tr::lng_action_topic_closed(
|
||||
tr::now,
|
||||
lt_topic,
|
||||
wrapParentTopic(),
|
||||
Ui::Text::WithEntities),
|
||||
};
|
||||
} else if (info->reopened) {
|
||||
return {
|
||||
tr::lng_action_topic_reopened(
|
||||
tr::now,
|
||||
lt_topic,
|
||||
wrapParentTopic(),
|
||||
Ui::Text::WithEntities),
|
||||
};
|
||||
} else if (info->renamed) {
|
||||
return {
|
||||
tr::lng_action_topic_renamed(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLink(1),
|
||||
lt_link,
|
||||
placeholderLink(),
|
||||
lt_title,
|
||||
wrapTopic(
|
||||
info->title,
|
||||
(info->reiconed
|
||||
? info->iconId
|
||||
: std::optional<DocumentId>())),
|
||||
Ui::Text::WithEntities),
|
||||
{ from->createOpenLink() },
|
||||
};
|
||||
} else if (info->reiconed) {
|
||||
if (const auto iconId = info->iconId) {
|
||||
return {
|
||||
tr::lng_action_topic_icon_changed(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLink(1),
|
||||
lt_link,
|
||||
placeholderLink(),
|
||||
lt_emoji,
|
||||
wrapIcon(iconId),
|
||||
Ui::Text::WithEntities),
|
||||
{ from->createOpenLink() },
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
tr::lng_action_topic_icon_removed(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLink(1),
|
||||
lt_link,
|
||||
placeholderLink(),
|
||||
Ui::Text::WithEntities),
|
||||
{ from->createOpenLink() },
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
tr::lng_action_topic_created(
|
||||
tr::now,
|
||||
lt_topic,
|
||||
wrapTopic(info->title, info->iconId),
|
||||
Ui::Text::WithEntities),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void Element::validateText() {
|
||||
const auto item = data();
|
||||
const auto &text = item->_text;
|
||||
@ -668,13 +807,20 @@ void Element::validateText() {
|
||||
.customEmojiRepaint = [=] { customEmojiRepaint(); },
|
||||
};
|
||||
if (_flags & Flag::ServiceMessage) {
|
||||
const auto contextDependentText = contextDependentServiceText();
|
||||
const auto &markedText = contextDependentText.text.empty()
|
||||
? text
|
||||
: contextDependentText.text;
|
||||
const auto &customLinks = contextDependentText.text.empty()
|
||||
? item->customTextLinks()
|
||||
: contextDependentText.links;
|
||||
_text.setMarkedText(
|
||||
st::serviceTextStyle,
|
||||
text,
|
||||
markedText,
|
||||
Ui::ItemTextServiceOptions(),
|
||||
context);
|
||||
auto linkIndex = 0;
|
||||
for (const auto &link : item->customTextLinks()) {
|
||||
for (const auto &link : customLinks) {
|
||||
// Link indices start with 1.
|
||||
_text.setLink(++linkIndex, link);
|
||||
}
|
||||
|
@ -515,6 +515,12 @@ private:
|
||||
|
||||
void refreshMedia(Element *replacing);
|
||||
|
||||
struct TextWithLinks {
|
||||
TextWithEntities text;
|
||||
std::vector<ClickHandlerPtr> links;
|
||||
};
|
||||
[[nodiscard]] TextWithLinks contextDependentServiceText();
|
||||
|
||||
const not_null<ElementDelegate*> _delegate;
|
||||
const not_null<HistoryItem*> _data;
|
||||
HistoryBlock *_block = nullptr;
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit c199a1722fae72e254753f3095444a3c82a2a704
|
||||
Subproject commit 7f1dd3c351f534d0564c41376176eb0cd1be4184
|
Loading…
Reference in New Issue
Block a user