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_masks_count#one" = "{count} mask";
"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_custom_emoji_used_sets" = "Sets of used emoji";
"lng_stickers_group_set" = "Group sticker set";
"lng_stickers_remove_group_set" = "Remove group sticker set?";
"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_save_custom_sound" = "Save for notifications";
"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" = "This message contains emoji from **{name} pack**.";
"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_view_in_chat" = "View in chat";

View File

@ -152,6 +152,7 @@ private:
bool isRecentSet() const;
bool isMasksSet() const;
bool isEmojiSet() const;
bool isWebm() const;
bool isInstalled() const;
bool isUnread() const;
@ -419,32 +420,56 @@ StickersBox::StickersBox(
, _section(Section::Attached)
, _isMasks(false)
, _attached(0, this, controller, Section::Attached)
, _attachedType(Data::StickersType::Stickers)
, _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 {
return _controller->session();
}
void StickersBox::showAttachedStickers() {
const auto stickers = &session().data().stickers();
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) {
const auto setData = stickerSet.match([&](const auto &data) {
return data.vset().match([&](const MTPDstickerSet &data) {
return &data;
});
});
if (const auto set = session().data().stickers().feedSet(*setData)) {
if (_attached.widget()->appendSet(set)) {
addedSet = true;
if (set->stickers.isEmpty()
|| (set->flags & SetFlag::NotLoaded)) {
session().api().scheduleStickerSetRequest(
set->id,
set->accessHash);
}
}
if (const auto set = stickers->feedSet(*setData)) {
add(set);
}
}
for (const auto &setId : _emojiSets) {
const auto i = stickers->sets().find(setId.id);
if (i != end(stickers->sets())) {
add(i->second.get());
}
}
if (addedSet) {
@ -547,7 +572,9 @@ void StickersBox::prepare() {
} else if (_section == Section::Archived) {
requestArchivedSets();
} 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 (archivedSetsOrder().isEmpty()) {
@ -1063,6 +1090,10 @@ bool StickersBox::Inner::Row::isMasksSet() const {
return (set->flags & SetFlag::Masks);
}
bool StickersBox::Inner::Row::isEmojiSet() const {
return (set->flags & SetFlag::Emoji);
}
bool StickersBox::Inner::Row::isWebm() const {
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)
? tr::lng_contacts_loading(tr::now)
: row->isEmojiSet()
? tr::lng_custom_emoji_count(tr::now, lt_count, row->count)
: row->isMasksSet()
? tr::lng_masks_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 {
class DocumentMedia;
enum class StickersType : uchar;
} // namespace Data
namespace Lottie {
@ -70,6 +71,10 @@ public:
QWidget*,
not_null<Window::SessionController*> controller,
const MTPVector<MTPStickerSetCovered> &attachedSets);
StickersBox(
QWidget*,
not_null<Window::SessionController*> controller,
const std::vector<StickerSetIdentifier> &emojiSets);
~StickersBox();
[[nodiscard]] Main::Session &session() const;
@ -157,7 +162,9 @@ private:
Tab _attached;
Tab *_tab = nullptr;
const Data::StickersType _attachedType = {};
const MTPVector<MTPStickerSetCovered> _attachedSets;
const std::vector<StickerSetIdentifier> _emojiSets;
ChannelData *_megagroupSet = nullptr;

View File

@ -415,6 +415,12 @@ std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader(
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() {
auto ids = QVector<MTPlong>();
ids.reserve(std::min(kMaxPerRequest, int(_pendingForRequest.size())));
@ -440,6 +446,7 @@ void CustomEmojiManager::request() {
}
}
}
requestSetFor(document);
}
requestFinished();
}).fail([=] {
@ -448,6 +455,30 @@ void CustomEmojiManager::request() {
}).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() {
_requestId = 0;
if (!_pendingForRequest.empty()) {

View File

@ -49,6 +49,8 @@ public:
DocumentId documentId,
SizeTag tag);
[[nodiscard]] QString lookupSetName(uint64 setId);
[[nodiscard]] Main::Session &session() const;
[[nodiscard]] Session &owner() const;
@ -65,6 +67,7 @@ private:
Ui::CustomEmoji::RepaintRequest request);
void scheduleRepaintTimer();
void invokeRepaints();
void requestSetFor(not_null<DocumentData*> document);
const not_null<Session*> _owner;
@ -81,6 +84,7 @@ private:
crl::time _repaintNext = 0;
base::Timer _repaintTimer;
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);
if (pack.isEmpty()) {
const auto removeIndex = (isMasks
const auto removeIndex = (isEmoji
? emojiSetsOrder()
: isMasks
? maskSetsOrder()
: setsOrder()).indexOf(set->id);
if (removeIndex >= 0) {
(isMasks
(isEmoji
? emojiSetsOrderRef()
: isMasks
? maskSetsOrderRef()
: setsOrderRef()).removeAt(removeIndex);
}
@ -1453,10 +1458,12 @@ StickersSet *Stickers::feedSetFull(const MTPDmessages_stickerSet &d) {
if (set) {
const auto isArchived = !!(set->flags & SetFlag::Archived);
if (isMasks) {
session().local().writeInstalledMasks();
} else if (set->flags & SetFlag::Installed) {
if (!isArchived) {
if ((set->flags & SetFlag::Installed) && !isArchived) {
if (isEmoji) {
session().local().writeInstalledCustomEmoji();
} else if (isMasks) {
session().local().writeInstalledMasks();
} else {
session().local().writeInstalledStickers();
}
}
@ -1464,7 +1471,9 @@ StickersSet *Stickers::feedSetFull(const MTPDmessages_stickerSet &d) {
session().local().writeFeaturedStickers();
}
if (wasArchived != isArchived) {
if (isMasks) {
if (isEmoji) {
} else if (isMasks) {
session().local().writeArchivedMasks();
} else {
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) {
HistoryView::AddWhoReactedAction(
_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/reactions/message_reactions_list.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/menu/menu_item_base.h"
#include "ui/image/image.h"
#include "ui/toast/toast.h"
#include "ui/text/text_utilities.h"
#include "ui/controls/delete_message_context_action.h"
#include "ui/controls/who_reacted_context_action.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/report_messages_box.h"
#include "boxes/sticker_set_box.h"
#include "boxes/stickers_box.h"
#include "data/data_photo.h"
#include "data/data_photo_media.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_scheduled_messages.h"
#include "data/data_message_reactions.h"
#include "data/stickers/data_custom_emoji.h"
#include "core/file_utilities.h"
#include "core/click_handler_types.h"
#include "base/platform/base_platform_info.h"
@ -1210,4 +1214,121 @@ void ShowWhoReactedMenu(
}, 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

View File

@ -79,4 +79,12 @@ void ShowWhoReactedMenu(
not_null<Window::SessionController*> controller,
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

View File

@ -2128,7 +2128,14 @@ void Account::writeInstalledCustomEmoji() {
using SetFlag = Data::StickersSetFlag;
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::Write;

View File

@ -1087,3 +1087,13 @@ msgServiceGiftBoxButtonPadding: margins(0px, 17px, 0px, 17px);
msgServiceGiftBoxTitlePadding: margins(0px, 126px, 0px, 2px);
msgServiceGiftBoxStickerTop: -30px;
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);