Track all single-emoji messages.

This commit is contained in:
John Preston 2019-08-01 22:14:19 +01:00
parent 7d2896dd42
commit 7e4c9f98a6
7 changed files with 180 additions and 53 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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