mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
Track all single-emoji messages.
This commit is contained in:
parent
7d2896dd42
commit
7e4c9f98a6
Telegram/SourceFiles
@ -23,20 +23,58 @@ constexpr auto kRefreshTimeout = TimeId(7200);
|
||||
|
||||
EmojiPack::EmojiPack(not_null<Main::Session*> session) : _session(session) {
|
||||
refresh();
|
||||
|
||||
session->data().itemRemoved(
|
||||
) | rpl::filter([](not_null<const HistoryItem*> item) {
|
||||
return item->isSingleEmoji();
|
||||
}) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||
remove(item);
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
bool EmojiPack::add(not_null<HistoryItem*> item, const QString &text) {
|
||||
auto length = 0;
|
||||
const auto trimmed = text.trimmed();
|
||||
if (const auto emoji = Ui::Emoji::Find(trimmed, &length)) {
|
||||
if (length == trimmed.size()) {
|
||||
_items[emoji].emplace(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EmojiPack::remove(not_null<const HistoryItem*> item) {
|
||||
if (!item->isSingleEmoji()) {
|
||||
return false;
|
||||
}
|
||||
auto length = 0;
|
||||
const auto trimmed = item->originalString().trimmed();
|
||||
const auto emoji = Ui::Emoji::Find(trimmed, &length);
|
||||
Assert(emoji != nullptr);
|
||||
Assert(length == trimmed.size());
|
||||
const auto i = _items.find(emoji);
|
||||
Assert(i != end(_items));
|
||||
const auto j = i->second.find(item);
|
||||
Assert(j != end(i->second));
|
||||
i->second.erase(j);
|
||||
if (i->second.empty()) {
|
||||
_items.erase(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DocumentData *EmojiPack::stickerForEmoji(not_null<HistoryItem*> item) {
|
||||
const auto text = item->originalText().text.trimmed();
|
||||
auto length = 0;
|
||||
const auto emoji = Ui::Emoji::Find(text, &length);
|
||||
if (!emoji || length != text.size()) {
|
||||
if (!item->isSingleEmoji()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto length = 0;
|
||||
const auto trimmed = item->originalString().trimmed();
|
||||
const auto emoji = Ui::Emoji::Find(trimmed, &length);
|
||||
Assert(emoji != nullptr);
|
||||
Assert(length == trimmed.size());
|
||||
const auto i = _map.find(emoji);
|
||||
if (i != end(_map)) {
|
||||
return i->second;
|
||||
}
|
||||
return nullptr;
|
||||
return (i != end(_map)) ? i->second.get() : nullptr;
|
||||
}
|
||||
|
||||
void EmojiPack::refresh() {
|
||||
@ -46,42 +84,86 @@ void EmojiPack::refresh() {
|
||||
_requestId = _session->api().request(MTPmessages_GetStickerSet(
|
||||
MTP_inputStickerSetAnimatedEmoji()
|
||||
)).done([=](const MTPmessages_StickerSet &result) {
|
||||
_requestId = 0;
|
||||
refreshDelayed();
|
||||
result.match([&](const MTPDmessages_stickerSet &data) {
|
||||
auto map = base::flat_map<uint64, not_null<DocumentData*>>();
|
||||
for (const auto &sticker : data.vdocuments().v) {
|
||||
const auto document = _session->data().processDocument(
|
||||
sticker);
|
||||
if (document->sticker()) {
|
||||
map.emplace(document->id, document);
|
||||
}
|
||||
}
|
||||
for (const auto &pack : data.vpacks().v) {
|
||||
pack.match([&](const MTPDstickerPack &data) {
|
||||
const auto emoji = [&] {
|
||||
return Ui::Emoji::Find(qs(data.vemoticon()));
|
||||
}();
|
||||
const auto document = [&]() -> DocumentData* {
|
||||
for (const auto &id : data.vdocuments().v) {
|
||||
const auto i = map.find(id.v);
|
||||
if (i != end(map)) {
|
||||
return i->second.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
if (emoji && document) {
|
||||
_map.emplace_or_assign(emoji, document);
|
||||
}
|
||||
});
|
||||
}
|
||||
applySet(data);
|
||||
});
|
||||
int a = 0;
|
||||
}).fail([=](const RPCError &error) {
|
||||
_requestId = 0;
|
||||
refreshDelayed();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void EmojiPack::applySet(const MTPDmessages_stickerSet &data) {
|
||||
const auto stickers = collectStickers(data.vdocuments().v);
|
||||
auto was = base::take(_map);
|
||||
|
||||
for (const auto &pack : data.vpacks().v) {
|
||||
pack.match([&](const MTPDstickerPack &data) {
|
||||
applyPack(data, stickers);
|
||||
});
|
||||
}
|
||||
|
||||
for (const auto &[emoji, document] : _map) {
|
||||
const auto i = was.find(emoji);
|
||||
if (i == end(was)) {
|
||||
refreshItems(emoji);
|
||||
} else {
|
||||
if (i->second != document) {
|
||||
refreshItems(i->first);
|
||||
}
|
||||
was.erase(i);
|
||||
}
|
||||
}
|
||||
for (const auto &[emoji, Document] : was) {
|
||||
refreshItems(emoji);
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiPack::refreshItems(EmojiPtr emoji) {
|
||||
const auto i = _items.find(emoji);
|
||||
if (i == end(_items)) {
|
||||
return;
|
||||
}
|
||||
for (const auto &item : i->second) {
|
||||
_session->data().requestItemViewRefresh(item);
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiPack::applyPack(
|
||||
const MTPDstickerPack &data,
|
||||
const base::flat_map<uint64, not_null<DocumentData*>> &map) {
|
||||
const auto emoji = [&] {
|
||||
return Ui::Emoji::Find(qs(data.vemoticon()));
|
||||
}();
|
||||
const auto document = [&]() -> DocumentData * {
|
||||
for (const auto &id : data.vdocuments().v) {
|
||||
const auto i = map.find(id.v);
|
||||
if (i != end(map)) {
|
||||
return i->second.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
if (emoji && document) {
|
||||
_map.emplace_or_assign(emoji, document);
|
||||
}
|
||||
}
|
||||
|
||||
base::flat_map<uint64, not_null<DocumentData*>> EmojiPack::collectStickers(
|
||||
const QVector<MTPDocument> &list) const {
|
||||
auto result = base::flat_map<uint64, not_null<DocumentData*>>();
|
||||
for (const auto &sticker : list) {
|
||||
const auto document = _session->data().processDocument(
|
||||
sticker);
|
||||
if (document->sticker()) {
|
||||
result.emplace(document->id, document);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void EmojiPack::refreshDelayed() {
|
||||
App::CallDelayed(kRefreshTimeout, _session, [=] {
|
||||
refresh();
|
||||
|
@ -20,15 +20,26 @@ class EmojiPack final {
|
||||
public:
|
||||
explicit EmojiPack(not_null<Main::Session*> session);
|
||||
|
||||
bool add(not_null<HistoryItem*> item, const QString &text);
|
||||
bool remove(not_null<const HistoryItem*> item);
|
||||
|
||||
[[nodiscard]] DocumentData *stickerForEmoji(not_null<HistoryItem*> item);
|
||||
|
||||
private:
|
||||
void refresh();
|
||||
void refreshDelayed();
|
||||
void applySet(const MTPDmessages_stickerSet &data);
|
||||
void applyPack(
|
||||
const MTPDstickerPack &data,
|
||||
const base::flat_map<uint64, not_null<DocumentData*>> &map);
|
||||
base::flat_map<uint64, not_null<DocumentData*>> collectStickers(
|
||||
const QVector<MTPDocument> &list) const;
|
||||
void refreshItems(EmojiPtr emoji);
|
||||
|
||||
not_null<Main::Session*> _session;
|
||||
base::flat_set<not_null<HistoryItem*>> _notLoaded;
|
||||
base::flat_map<EmojiPtr, not_null<DocumentData*>> _map;
|
||||
base::flat_map<EmojiPtr, base::flat_set<not_null<HistoryItem*>>> _items;
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
@ -1680,7 +1680,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
const auto media = (view ? view->media() : nullptr);
|
||||
const auto mediaHasTextForCopy = media && media->hasTextForCopy();
|
||||
if (const auto document = media ? media->getDocument() : nullptr) {
|
||||
if (document->sticker()) {
|
||||
if (!item->isSingleEmoji() && document->sticker()) {
|
||||
if (document->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
|
||||
_menu->addAction(document->isStickerSetInstalled() ? tr::lng_context_pack_info(tr::now) : tr::lng_context_pack_add(tr::now), [=] {
|
||||
showStickerPackInfo(document);
|
||||
|
@ -139,19 +139,19 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool definesReplyKeyboard() const;
|
||||
MTPDreplyKeyboardMarkup::Flags replyKeyboardFlags() const;
|
||||
[[nodiscard]] bool definesReplyKeyboard() const;
|
||||
[[nodiscard]] MTPDreplyKeyboardMarkup::Flags replyKeyboardFlags() const;
|
||||
|
||||
bool hasSwitchInlineButton() const {
|
||||
[[nodiscard]] bool hasSwitchInlineButton() const {
|
||||
return _flags & MTPDmessage_ClientFlag::f_has_switch_inline_button;
|
||||
}
|
||||
bool hasTextLinks() const {
|
||||
[[nodiscard]] bool hasTextLinks() const {
|
||||
return _flags & MTPDmessage_ClientFlag::f_has_text_links;
|
||||
}
|
||||
bool isGroupEssential() const {
|
||||
[[nodiscard]] bool isGroupEssential() const {
|
||||
return _flags & MTPDmessage_ClientFlag::f_is_group_essential;
|
||||
}
|
||||
bool isLocalUpdateMedia() const {
|
||||
[[nodiscard]] bool isLocalUpdateMedia() const {
|
||||
return _flags & MTPDmessage_ClientFlag::f_is_local_update_media;
|
||||
}
|
||||
void setIsLocalUpdateMedia(bool flag) {
|
||||
@ -161,22 +161,25 @@ public:
|
||||
_flags &= ~MTPDmessage_ClientFlag::f_is_local_update_media;
|
||||
}
|
||||
}
|
||||
bool isGroupMigrate() const {
|
||||
[[nodiscard]] bool isGroupMigrate() const {
|
||||
return isGroupEssential() && isEmpty();
|
||||
}
|
||||
bool hasViews() const {
|
||||
[[nodiscard]] bool isSingleEmoji() const {
|
||||
return _flags & MTPDmessage_ClientFlag::f_single_emoji;
|
||||
}
|
||||
[[nodiscard]] bool hasViews() const {
|
||||
return _flags & MTPDmessage::Flag::f_views;
|
||||
}
|
||||
bool isPost() const {
|
||||
[[nodiscard]] bool isPost() const {
|
||||
return _flags & MTPDmessage::Flag::f_post;
|
||||
}
|
||||
bool isSilent() const {
|
||||
[[nodiscard]] bool isSilent() const {
|
||||
return _flags & MTPDmessage::Flag::f_silent;
|
||||
}
|
||||
bool isSending() const {
|
||||
[[nodiscard]] bool isSending() const {
|
||||
return _flags & MTPDmessage_ClientFlag::f_sending;
|
||||
}
|
||||
bool hasFailed() const {
|
||||
[[nodiscard]] bool hasFailed() const {
|
||||
return _flags & MTPDmessage_ClientFlag::f_failed;
|
||||
}
|
||||
void sendFailed();
|
||||
@ -224,6 +227,9 @@ public:
|
||||
virtual QString inReplyText() const {
|
||||
return inDialogsText(DrawInDialog::WithoutSender);
|
||||
}
|
||||
virtual QString originalString() const {
|
||||
return QString();
|
||||
}
|
||||
virtual TextWithEntities originalText() const {
|
||||
return TextWithEntities();
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_service.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/view/history_view_context_menu.h" // For CopyPostLink().
|
||||
#include "chat_helpers/stickers_emoji_pack.h"
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
@ -1083,6 +1084,7 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
|
||||
}
|
||||
}
|
||||
|
||||
clearSingleEmoji();
|
||||
if (_media && _media->consumeMessageText(textWithEntities)) {
|
||||
setEmptyText();
|
||||
} else {
|
||||
@ -1095,8 +1097,10 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
|
||||
// just replace it with something so that UI won't look buggy.
|
||||
_text.setMarkedText(
|
||||
st::messageTextStyle,
|
||||
{ QString::fromUtf8("\xF0\x9F\x98\x94"), EntitiesInText() },
|
||||
{ QString::fromUtf8(":-("), EntitiesInText() },
|
||||
Ui::ItemTextOptions(this));
|
||||
} else if (!_media) {
|
||||
checkSingleEmoji(textWithEntities.text);
|
||||
}
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
@ -1113,6 +1117,20 @@ void HistoryMessage::setEmptyText() {
|
||||
_textHeight = 0;
|
||||
}
|
||||
|
||||
void HistoryMessage::clearSingleEmoji() {
|
||||
if (!(_flags & MTPDmessage_ClientFlag::f_single_emoji)) {
|
||||
return;
|
||||
}
|
||||
history()->session().emojiStickersPack().remove(this);
|
||||
_flags &= ~MTPDmessage_ClientFlag::f_single_emoji;
|
||||
}
|
||||
|
||||
void HistoryMessage::checkSingleEmoji(const QString &text) {
|
||||
if (history()->session().emojiStickersPack().add(this, text)) {
|
||||
_flags |= MTPDmessage_ClientFlag::f_single_emoji;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryMessage::setReplyMarkup(const MTPReplyMarkup *markup) {
|
||||
if (!markup) {
|
||||
if (_flags & MTPDmessage::Flag::f_reply_markup) {
|
||||
@ -1155,6 +1173,10 @@ void HistoryMessage::setReplyMarkup(const MTPReplyMarkup *markup) {
|
||||
}
|
||||
}
|
||||
|
||||
QString HistoryMessage::originalString() const {
|
||||
return emptyText() ? QString() : _text.toString();
|
||||
}
|
||||
|
||||
TextWithEntities HistoryMessage::originalText() const {
|
||||
if (emptyText()) {
|
||||
return { QString(), EntitiesInText() };
|
||||
|
@ -29,8 +29,7 @@ QString GetErrorTextForForward(
|
||||
bool ignoreSlowmodeCountdown = false);
|
||||
void FastShareMessage(not_null<HistoryItem*> item);
|
||||
|
||||
class HistoryMessage
|
||||
: public HistoryItem {
|
||||
class HistoryMessage : public HistoryItem {
|
||||
public:
|
||||
HistoryMessage(
|
||||
not_null<History*> history,
|
||||
@ -134,6 +133,7 @@ public:
|
||||
[[nodiscard]] Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||
|
||||
void setText(const TextWithEntities &textWithEntities) override;
|
||||
[[nodiscard]] QString originalString() const override;
|
||||
[[nodiscard]] TextWithEntities originalText() const override;
|
||||
[[nodiscard]] TextForMimeData clipboardText() const override;
|
||||
[[nodiscard]] bool textHasLinks() const override;
|
||||
@ -164,6 +164,9 @@ private:
|
||||
return _flags & MTPDmessage::Flag::f_legacy;
|
||||
}
|
||||
|
||||
void clearSingleEmoji();
|
||||
void checkSingleEmoji(const QString &text);
|
||||
|
||||
// For an invoice button we replace the button text with a "Receipt" key.
|
||||
// It should show the receipt for the payed invoice. Still let mobile apps do that.
|
||||
void replaceBuyWithReceiptInMarkup();
|
||||
|
@ -63,8 +63,11 @@ enum class MTPDmessage_ClientFlag : uint32 {
|
||||
// message was an outgoing message and failed to be sent
|
||||
f_failed = (1U << 22),
|
||||
|
||||
// message has no media and only a single emoji text
|
||||
f_single_emoji = (1U << 21),
|
||||
|
||||
// update this when adding new client side flags
|
||||
MIN_FIELD = (1U << 22),
|
||||
MIN_FIELD = (1U << 21),
|
||||
};
|
||||
DEFINE_MTP_CLIENT_FLAGS(MTPDmessage)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user