Allow viewing emoji sets used in a message.

This commit is contained in:
John Preston 2022-07-18 16:56:14 +03:00
parent fce4452af5
commit bb7249f280
11 changed files with 266 additions and 22 deletions

View file

@ -1821,7 +1821,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stickers_count#other" = "{count} stickers"; "lng_stickers_count#other" = "{count} stickers";
"lng_masks_count#one" = "{count} mask"; "lng_masks_count#one" = "{count} mask";
"lng_masks_count#other" = "{count} masks"; "lng_masks_count#other" = "{count} masks";
"lng_custom_emoji_count#one" = "{count} emoji";
"lng_custom_emoji_count#other" = "{count} emoji";
"lng_stickers_attached_sets" = "Sets of attached stickers"; "lng_stickers_attached_sets" = "Sets of attached stickers";
"lng_custom_emoji_used_sets" = "Sets of used emoji";
"lng_stickers_group_set" = "Group sticker set"; "lng_stickers_group_set" = "Group sticker set";
"lng_stickers_remove_group_set" = "Remove group sticker set?"; "lng_stickers_remove_group_set" = "Remove group sticker set?";
"lng_stickers_group_from_your" = "Choose from your stickers"; "lng_stickers_group_from_your" = "Choose from your stickers";
@ -2147,8 +2150,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_delete_all_files" = "Delete all files"; "lng_context_delete_all_files" = "Delete all files";
"lng_context_save_custom_sound" = "Save for notifications"; "lng_context_save_custom_sound" = "Save for notifications";
"lng_context_animated_emoji" = "This message contains emoji from {name} pack."; "lng_context_animated_emoji" = "This message contains emoji from **{name} pack**.";
"lng_context_animated_emoji_many" = "This message contains emoji from {name} packs."; "lng_context_animated_emoji_many#one" = "This message contains emoji from **{count} pack**.";
"lng_context_animated_emoji_many#other" = "This message contains emoji from **{count} packs**.";
"lng_downloads_section" = "Downloads"; "lng_downloads_section" = "Downloads";
"lng_downloads_view_in_chat" = "View in chat"; "lng_downloads_view_in_chat" = "View in chat";

View file

@ -152,6 +152,7 @@ private:
bool isRecentSet() const; bool isRecentSet() const;
bool isMasksSet() const; bool isMasksSet() const;
bool isEmojiSet() const;
bool isWebm() const; bool isWebm() const;
bool isInstalled() const; bool isInstalled() const;
bool isUnread() const; bool isUnread() const;
@ -419,32 +420,56 @@ StickersBox::StickersBox(
, _section(Section::Attached) , _section(Section::Attached)
, _isMasks(false) , _isMasks(false)
, _attached(0, this, controller, Section::Attached) , _attached(0, this, controller, Section::Attached)
, _attachedType(Data::StickersType::Stickers)
, _attachedSets(attachedSets) { , _attachedSets(attachedSets) {
} }
StickersBox::StickersBox(
QWidget*,
not_null<Window::SessionController*> controller,
const std::vector<StickerSetIdentifier> &emojiSets)
: _controller(controller)
, _api(&controller->session().mtp())
, _section(Section::Attached)
, _isMasks(false)
, _attached(0, this, controller, Section::Attached)
, _attachedType(Data::StickersType::Emoji)
, _emojiSets(emojiSets) {
}
Main::Session &StickersBox::session() const { Main::Session &StickersBox::session() const {
return _controller->session(); return _controller->session();
} }
void StickersBox::showAttachedStickers() { void StickersBox::showAttachedStickers() {
const auto stickers = &session().data().stickers();
auto addedSet = false; auto addedSet = false;
const auto add = [&](not_null<StickersSet*> set) {
if (_attached.widget()->appendSet(set)) {
addedSet = true;
if (set->stickers.isEmpty()
|| (set->flags & SetFlag::NotLoaded)) {
session().api().scheduleStickerSetRequest(
set->id,
set->accessHash);
}
}
};
for (const auto &stickerSet : _attachedSets.v) { for (const auto &stickerSet : _attachedSets.v) {
const auto setData = stickerSet.match([&](const auto &data) { const auto setData = stickerSet.match([&](const auto &data) {
return data.vset().match([&](const MTPDstickerSet &data) { return data.vset().match([&](const MTPDstickerSet &data) {
return &data; return &data;
}); });
}); });
if (const auto set = stickers->feedSet(*setData)) {
if (const auto set = session().data().stickers().feedSet(*setData)) { add(set);
if (_attached.widget()->appendSet(set)) { }
addedSet = true; }
if (set->stickers.isEmpty() for (const auto &setId : _emojiSets) {
|| (set->flags & SetFlag::NotLoaded)) { const auto i = stickers->sets().find(setId.id);
session().api().scheduleStickerSetRequest( if (i != end(stickers->sets())) {
set->id, add(i->second.get());
set->accessHash);
}
}
} }
} }
if (addedSet) { if (addedSet) {
@ -547,7 +572,9 @@ void StickersBox::prepare() {
} else if (_section == Section::Archived) { } else if (_section == Section::Archived) {
requestArchivedSets(); requestArchivedSets();
} else if (_section == Section::Attached) { } else if (_section == Section::Attached) {
setTitle(tr::lng_stickers_attached_sets()); setTitle(_attachedType == Data::StickersType::Emoji
? tr::lng_custom_emoji_used_sets()
: tr::lng_stickers_attached_sets());
} }
if (_tabs) { if (_tabs) {
if (archivedSetsOrder().isEmpty()) { if (archivedSetsOrder().isEmpty()) {
@ -1063,6 +1090,10 @@ bool StickersBox::Inner::Row::isMasksSet() const {
return (set->flags & SetFlag::Masks); return (set->flags & SetFlag::Masks);
} }
bool StickersBox::Inner::Row::isEmojiSet() const {
return (set->flags & SetFlag::Emoji);
}
bool StickersBox::Inner::Row::isWebm() const { bool StickersBox::Inner::Row::isWebm() const {
return (set->flags & SetFlag::Webm); return (set->flags & SetFlag::Webm);
} }
@ -1331,6 +1362,8 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
const auto statusText = (row->count == 0) const auto statusText = (row->count == 0)
? tr::lng_contacts_loading(tr::now) ? tr::lng_contacts_loading(tr::now)
: row->isEmojiSet()
? tr::lng_custom_emoji_count(tr::now, lt_count, row->count)
: row->isMasksSet() : row->isMasksSet()
? tr::lng_masks_count(tr::now, lt_count, row->count) ? tr::lng_masks_count(tr::now, lt_count, row->count)
: tr::lng_stickers_count(tr::now, lt_count, row->count); : tr::lng_stickers_count(tr::now, lt_count, row->count);

View file

@ -37,6 +37,7 @@ class Session;
namespace Data { namespace Data {
class DocumentMedia; class DocumentMedia;
enum class StickersType : uchar;
} // namespace Data } // namespace Data
namespace Lottie { namespace Lottie {
@ -70,6 +71,10 @@ public:
QWidget*, QWidget*,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
const MTPVector<MTPStickerSetCovered> &attachedSets); const MTPVector<MTPStickerSetCovered> &attachedSets);
StickersBox(
QWidget*,
not_null<Window::SessionController*> controller,
const std::vector<StickerSetIdentifier> &emojiSets);
~StickersBox(); ~StickersBox();
[[nodiscard]] Main::Session &session() const; [[nodiscard]] Main::Session &session() const;
@ -157,7 +162,9 @@ private:
Tab _attached; Tab _attached;
Tab *_tab = nullptr; Tab *_tab = nullptr;
const Data::StickersType _attachedType = {};
const MTPVector<MTPStickerSetCovered> _attachedSets; const MTPVector<MTPStickerSetCovered> _attachedSets;
const std::vector<StickerSetIdentifier> _emojiSets;
ChannelData *_megagroupSet = nullptr; ChannelData *_megagroupSet = nullptr;

View file

@ -415,6 +415,12 @@ std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader(
return result; return result;
} }
QString CustomEmojiManager::lookupSetName(uint64 setId) {
const auto &sets = _owner->stickers().sets();
const auto i = sets.find(setId);
return (i != end(sets)) ? i->second->title : QString();
}
void CustomEmojiManager::request() { void CustomEmojiManager::request() {
auto ids = QVector<MTPlong>(); auto ids = QVector<MTPlong>();
ids.reserve(std::min(kMaxPerRequest, int(_pendingForRequest.size()))); ids.reserve(std::min(kMaxPerRequest, int(_pendingForRequest.size())));
@ -440,6 +446,7 @@ void CustomEmojiManager::request() {
} }
} }
} }
requestSetFor(document);
} }
requestFinished(); requestFinished();
}).fail([=] { }).fail([=] {
@ -448,6 +455,30 @@ void CustomEmojiManager::request() {
}).send(); }).send();
} }
void CustomEmojiManager::requestSetFor(not_null<DocumentData*> document) {
const auto sticker = document->sticker();
if (!sticker || !sticker->set.id) {
return;
}
const auto &sets = document->owner().stickers().sets();
const auto i = sets.find(sticker->set.id);
if (i != end(sets)) {
return;
}
const auto session = &document->session();
session->api().scheduleStickerSetRequest(
sticker->set.id,
sticker->set.accessHash);
if (_requestSetsScheduled) {
return;
}
_requestSetsScheduled = true;
crl::on_main(this, [=] {
_requestSetsScheduled = false;
session->api().requestStickerSets();
});
}
void CustomEmojiManager::requestFinished() { void CustomEmojiManager::requestFinished() {
_requestId = 0; _requestId = 0;
if (!_pendingForRequest.empty()) { if (!_pendingForRequest.empty()) {

View file

@ -49,6 +49,8 @@ public:
DocumentId documentId, DocumentId documentId,
SizeTag tag); SizeTag tag);
[[nodiscard]] QString lookupSetName(uint64 setId);
[[nodiscard]] Main::Session &session() const; [[nodiscard]] Main::Session &session() const;
[[nodiscard]] Session &owner() const; [[nodiscard]] Session &owner() const;
@ -65,6 +67,7 @@ private:
Ui::CustomEmoji::RepaintRequest request); Ui::CustomEmoji::RepaintRequest request);
void scheduleRepaintTimer(); void scheduleRepaintTimer();
void invokeRepaints(); void invokeRepaints();
void requestSetFor(not_null<DocumentData*> document);
const not_null<Session*> _owner; const not_null<Session*> _owner;
@ -81,6 +84,7 @@ private:
crl::time _repaintNext = 0; crl::time _repaintNext = 0;
base::Timer _repaintTimer; base::Timer _repaintTimer;
bool _repaintTimerScheduled = false; bool _repaintTimerScheduled = false;
bool _requestSetsScheduled = false;
}; };

View file

@ -1410,13 +1410,18 @@ StickersSet *Stickers::feedSetFull(const MTPDmessages_stickerSet &d) {
} }
} }
const auto isEmoji = !!(set->flags & SetFlag::Emoji);
const auto isMasks = !!(set->flags & SetFlag::Masks); const auto isMasks = !!(set->flags & SetFlag::Masks);
if (pack.isEmpty()) { if (pack.isEmpty()) {
const auto removeIndex = (isMasks const auto removeIndex = (isEmoji
? emojiSetsOrder()
: isMasks
? maskSetsOrder() ? maskSetsOrder()
: setsOrder()).indexOf(set->id); : setsOrder()).indexOf(set->id);
if (removeIndex >= 0) { if (removeIndex >= 0) {
(isMasks (isEmoji
? emojiSetsOrderRef()
: isMasks
? maskSetsOrderRef() ? maskSetsOrderRef()
: setsOrderRef()).removeAt(removeIndex); : setsOrderRef()).removeAt(removeIndex);
} }
@ -1453,10 +1458,12 @@ StickersSet *Stickers::feedSetFull(const MTPDmessages_stickerSet &d) {
if (set) { if (set) {
const auto isArchived = !!(set->flags & SetFlag::Archived); const auto isArchived = !!(set->flags & SetFlag::Archived);
if (isMasks) { if ((set->flags & SetFlag::Installed) && !isArchived) {
session().local().writeInstalledMasks(); if (isEmoji) {
} else if (set->flags & SetFlag::Installed) { session().local().writeInstalledCustomEmoji();
if (!isArchived) { } else if (isMasks) {
session().local().writeInstalledMasks();
} else {
session().local().writeInstalledStickers(); session().local().writeInstalledStickers();
} }
} }
@ -1464,7 +1471,9 @@ StickersSet *Stickers::feedSetFull(const MTPDmessages_stickerSet &d) {
session().local().writeFeaturedStickers(); session().local().writeFeaturedStickers();
} }
if (wasArchived != isArchived) { if (wasArchived != isArchived) {
if (isMasks) { if (isEmoji) {
} else if (isMasks) {
session().local().writeArchivedMasks(); session().local().writeArchivedMasks();
} else { } else {
session().local().writeArchivedStickers(); session().local().writeArchivedStickers();

View file

@ -2412,6 +2412,16 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
} }
} }
auto emojiPackIds = _dragStateItem
? HistoryView::CollectEmojiPacks(_dragStateItem)
: std::vector<StickerSetIdentifier>();
if (!emojiPackIds.empty()) {
HistoryView::AddEmojiPacksAction(
_menu,
this,
std::move(emojiPackIds),
_controller);
}
if (hasWhoReactedItem) { if (hasWhoReactedItem) {
HistoryView::AddWhoReactedAction( HistoryView::AddWhoReactedAction(
_menu, _menu,

View file

@ -26,8 +26,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_web_page.h" #include "history/view/media/history_view_web_page.h"
#include "history/view/reactions/message_reactions_list.h" #include "history/view/reactions/message_reactions_list.h"
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
#include "ui/widgets/menu/menu_item_base.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/text/text_utilities.h"
#include "ui/controls/delete_message_context_action.h" #include "ui/controls/delete_message_context_action.h"
#include "ui/controls/who_reacted_context_action.h" #include "ui/controls/who_reacted_context_action.h"
#include "ui/boxes/report_box.h" #include "ui/boxes/report_box.h"
@ -37,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/delete_messages_box.h" #include "boxes/delete_messages_box.h"
#include "boxes/report_messages_box.h" #include "boxes/report_messages_box.h"
#include "boxes/sticker_set_box.h" #include "boxes/sticker_set_box.h"
#include "boxes/stickers_box.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_photo_media.h" #include "data/data_photo_media.h"
#include "data/data_document.h" #include "data/data_document.h"
@ -48,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_scheduled_messages.h" #include "data/data_scheduled_messages.h"
#include "data/data_message_reactions.h" #include "data/data_message_reactions.h"
#include "data/stickers/data_custom_emoji.h"
#include "core/file_utilities.h" #include "core/file_utilities.h"
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
@ -1210,4 +1214,121 @@ void ShowWhoReactedMenu(
}, lifetime); }, lifetime);
} }
std::vector<StickerSetIdentifier> CollectEmojiPacks(
not_null<HistoryItem*> item) {
auto result = std::vector<StickerSetIdentifier>();
const auto owner = &item->history()->owner();
for (const auto &entity : item->originalText().entities) {
if (entity.type() == EntityType::CustomEmoji) {
const auto data = Data::ParseCustomEmojiData(entity.data());
if (const auto set = owner->document(data.id)->sticker()) {
if (set->set.id
&& !ranges::contains(
result,
set->set.id,
&StickerSetIdentifier::id)) {
result.push_back(set->set);
}
}
}
}
return result;
}
void AddEmojiPacksAction(
not_null<Ui::PopupMenu*> menu,
not_null<QWidget*> context,
std::vector<StickerSetIdentifier> packIds,
not_null<Window::SessionController*> controller) {
class Item final : public Ui::Menu::ItemBase {
public:
Item(
not_null<RpWidget*> parent,
const style::Menu &st,
TextWithEntities &&about)
: Ui::Menu::ItemBase(parent, st)
, _st(st)
, _text(base::make_unique_q<Ui::FlatLabel>(
this,
rpl::single(std::move(about)),
st::historyHasCustomEmoji))
, _dummyAction(new QAction(parent)) {
enableMouseSelecting();
_text->setAttribute(Qt::WA_TransparentForMouseEvents);
parent->widthValue() | rpl::start_with_next([=](int width) {
const auto top = st::historyHasCustomEmojiPosition.y();
const auto skip = st::historyHasCustomEmojiPosition.x();
_text->resizeToWidth(width - 2 * skip);
_text->moveToLeft(skip, top);
resize(width, contentHeight());
}, lifetime());
}
not_null<QAction*> action() const override {
return _dummyAction;
}
bool isEnabled() const override {
return true;
}
private:
int contentHeight() const override {
const auto skip = st::historyHasCustomEmojiPosition.y();
return skip + _text->height() + skip;
}
void paintEvent(QPaintEvent *e) override {
auto p = QPainter(this);
const auto selected = isSelected();
p.fillRect(rect(), selected ? _st.itemBgOver : _st.itemBg);
RippleButton::paintRipple(p, 0, 0);
}
const style::Menu &_st;
const base::unique_qptr<Ui::FlatLabel> _text;
const not_null<QAction*> _dummyAction;
};
const auto count = int(packIds.size());
const auto manager = &controller->session().data().customEmojiManager();
const auto name = (count == 1)
? TextWithEntities{ manager->lookupSetName(packIds[0].id) }
: TextWithEntities();
if (!menu->empty()) {
menu->addSeparator();
}
auto button = base::make_unique_q<Item>(
menu,
menu->st().menu,
(name.text.isEmpty()
? tr::lng_context_animated_emoji_many(
tr::now,
lt_count,
count,
Ui::Text::RichLangValue)
: tr::lng_context_animated_emoji(
tr::now,
lt_name,
TextWithEntities{ name },
Ui::Text::RichLangValue)));
const auto weak = base::make_weak(controller.get());
button->setClickedCallback([=] {
const auto strong = weak.get();
if (!strong) {
return;
} else if (packIds.size() > 1) {
strong->show(Box<StickersBox>(strong, packIds));
return;
}
// Single used emoji pack.
strong->show(
Box<StickerSetBox>(strong, packIds.front()),
Ui::LayerOption::KeepOther);
});
menu->addAction(std::move(button));
}
} // namespace HistoryView } // namespace HistoryView

View file

@ -79,4 +79,12 @@ void ShowWhoReactedMenu(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
rpl::lifetime &lifetime); rpl::lifetime &lifetime);
[[nodiscard]] std::vector<StickerSetIdentifier> CollectEmojiPacks(
not_null<HistoryItem*> item);
void AddEmojiPacksAction(
not_null<Ui::PopupMenu*> menu,
not_null<QWidget*> context,
std::vector<StickerSetIdentifier> packIds,
not_null<Window::SessionController*> controller);
} // namespace HistoryView } // namespace HistoryView

View file

@ -2128,7 +2128,14 @@ void Account::writeInstalledCustomEmoji() {
using SetFlag = Data::StickersSetFlag; using SetFlag = Data::StickersSetFlag;
writeStickerSets(_installedCustomEmojiKey, [](const Data::StickersSet &set) { writeStickerSets(_installedCustomEmojiKey, [](const Data::StickersSet &set) {
if (!(set.flags & SetFlag::Emoji) || set.stickers.isEmpty()) { if (!(set.flags & SetFlag::Emoji)) {
return StickerSetCheckResult::Skip;
} else if (set.flags & SetFlag::NotLoaded) {
// waiting to receive
return StickerSetCheckResult::Abort;
} else if (!(set.flags & SetFlag::Installed)
|| (set.flags & SetFlag::Archived)
|| set.stickers.isEmpty()) {
return StickerSetCheckResult::Skip; return StickerSetCheckResult::Skip;
} }
return StickerSetCheckResult::Write; return StickerSetCheckResult::Write;

View file

@ -1087,3 +1087,13 @@ msgServiceGiftBoxButtonPadding: margins(0px, 17px, 0px, 17px);
msgServiceGiftBoxTitlePadding: margins(0px, 126px, 0px, 2px); msgServiceGiftBoxTitlePadding: margins(0px, 126px, 0px, 2px);
msgServiceGiftBoxStickerTop: -30px; msgServiceGiftBoxStickerTop: -30px;
msgServiceGiftBoxStickerSize: size(150px, 150px); msgServiceGiftBoxStickerSize: size(150px, 150px);
historyHasCustomEmoji: FlatLabel(defaultFlatLabel) {
style: TextStyle(defaultTextStyle) {
font: font(12px);
linkFont: font(12px underline);
linkFontOver: font(12px underline);
}
minWidth: 80px;
}
historyHasCustomEmojiPosition: point(12px, 4px);