mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Allow wearing collectibles from emoji status.
This commit is contained in:
parent
d0132c0f7b
commit
fecddb5203
23 changed files with 278 additions and 68 deletions
|
@ -2373,6 +2373,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_group_stickers_add" = "Choose sticker set";
|
"lng_group_stickers_add" = "Choose sticker set";
|
||||||
"lng_group_emoji" = "Select Emoji Pack";
|
"lng_group_emoji" = "Select Emoji Pack";
|
||||||
"lng_group_emoji_description" = "Choose an emoji pack that will be available to all members within the group.";
|
"lng_group_emoji_description" = "Choose an emoji pack that will be available to all members within the group.";
|
||||||
|
"lng_collectible_emoji" = "Collectibles";
|
||||||
|
|
||||||
"lng_premium" = "Premium";
|
"lng_premium" = "Premium";
|
||||||
"lng_premium_free" = "Free";
|
"lng_premium_free" = "Free";
|
||||||
|
|
|
@ -265,7 +265,7 @@ struct IconSelector {
|
||||||
const auto manager = &controller->session().data().customEmojiManager();
|
const auto manager = &controller->session().data().customEmojiManager();
|
||||||
|
|
||||||
auto factory = [=](DocumentId id, Fn<void()> repaint)
|
auto factory = [=](DocumentId id, Fn<void()> repaint)
|
||||||
-> std::unique_ptr<Ui::Text::CustomEmoji> {
|
-> std::unique_ptr<Ui::Text::CustomEmoji> {
|
||||||
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
||||||
if (id == kDefaultIconId) {
|
if (id == kDefaultIconId) {
|
||||||
return std::make_unique<DefaultIconEmoji>(
|
return std::make_unique<DefaultIconEmoji>(
|
||||||
|
@ -288,7 +288,7 @@ struct IconSelector {
|
||||||
.show = controller->uiShow(),
|
.show = controller->uiShow(),
|
||||||
.mode = EmojiListWidget::Mode::TopicIcon,
|
.mode = EmojiListWidget::Mode::TopicIcon,
|
||||||
.paused = Window::PausedIn(controller, PauseReason::Layer),
|
.paused = Window::PausedIn(controller, PauseReason::Layer),
|
||||||
.customRecentList = recent(),
|
.customRecentList = DocumentListToRecent(recent()),
|
||||||
.customRecentFactory = std::move(factory),
|
.customRecentFactory = std::move(factory),
|
||||||
.st = &st::reactPanelEmojiPan,
|
.st = &st::reactPanelEmojiPan,
|
||||||
}),
|
}),
|
||||||
|
@ -297,7 +297,7 @@ struct IconSelector {
|
||||||
icons->requestDefaultIfUnknown();
|
icons->requestDefaultIfUnknown();
|
||||||
icons->defaultUpdates(
|
icons->defaultUpdates(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
selector->provideRecent(recent());
|
selector->provideRecent(DocumentListToRecent(recent()));
|
||||||
}, selector->lifetime());
|
}, selector->lifetime());
|
||||||
|
|
||||||
placeFooter(selector->createFooter());
|
placeFooter(selector->createFooter());
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "base/event_filter.h"
|
#include "base/event_filter.h"
|
||||||
|
#include "chat_helpers/emoji_list_widget.h"
|
||||||
#include "chat_helpers/tabbed_panel.h"
|
#include "chat_helpers/tabbed_panel.h"
|
||||||
#include "chat_helpers/tabbed_selector.h"
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
#include "core/ui_integration.h"
|
#include "core/ui_integration.h"
|
||||||
|
@ -491,7 +492,8 @@ object_ptr<Ui::RpWidget> AddReactionsSelector(
|
||||||
panelList.erase(
|
panelList.erase(
|
||||||
ranges::remove(panelList, paid->selectAnimation->id),
|
ranges::remove(panelList, paid->selectAnimation->id),
|
||||||
end(panelList));
|
end(panelList));
|
||||||
panel->selector()->provideRecentEmoji(panelList);
|
panel->selector()->provideRecentEmoji(
|
||||||
|
ChatHelpers::DocumentListToRecent(panelList));
|
||||||
panel->setDesiredHeightValues(
|
panel->setDesiredHeightValues(
|
||||||
1.,
|
1.,
|
||||||
st::emojiPanMinHeight / 2,
|
st::emojiPanMinHeight / 2,
|
||||||
|
|
|
@ -40,6 +40,7 @@ TabbedSearch {
|
||||||
|
|
||||||
ComposeIcons {
|
ComposeIcons {
|
||||||
settings: icon;
|
settings: icon;
|
||||||
|
collectibles: icon;
|
||||||
|
|
||||||
recent: icon;
|
recent: icon;
|
||||||
recentActive: icon;
|
recentActive: icon;
|
||||||
|
@ -587,6 +588,7 @@ sendBoxAlbumGroupButtonMediaDelete: icon {{ "send_media/send_media_delete", roun
|
||||||
|
|
||||||
defaultComposeIcons: ComposeIcons {
|
defaultComposeIcons: ComposeIcons {
|
||||||
settings: icon {{ "emoji/emoji_settings", emojiIconFg }};
|
settings: icon {{ "emoji/emoji_settings", emojiIconFg }};
|
||||||
|
collectibles: icon {{ "menu/unique", emojiIconFg }};
|
||||||
|
|
||||||
recent: icon {{ "emoji/emoji_recent", emojiIconFg }};
|
recent: icon {{ "emoji/emoji_recent", emojiIconFg }};
|
||||||
recentActive: icon {{ "emoji/emoji_recent", emojiSubIconFgActive }};
|
recentActive: icon {{ "emoji/emoji_recent", emojiSubIconFgActive }};
|
||||||
|
|
|
@ -18,6 +18,7 @@ struct ComposeFeatures {
|
||||||
bool attachBotsMenu : 1 = true;
|
bool attachBotsMenu : 1 = true;
|
||||||
bool inlineBots : 1 = true;
|
bool inlineBots : 1 = true;
|
||||||
bool megagroupSet : 1 = true;
|
bool megagroupSet : 1 = true;
|
||||||
|
bool collectibleStatus : 1 = false;
|
||||||
bool stickersSettings : 1 = true;
|
bool stickersSettings : 1 = true;
|
||||||
bool openStickerSets : 1 = true;
|
bool openStickerSets : 1 = true;
|
||||||
bool autocompleteHashtags : 1 = true;
|
bool autocompleteHashtags : 1 = true;
|
||||||
|
|
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/sticker_set_box.h"
|
#include "boxes/sticker_set_box.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "layout/layout_position.h"
|
#include "layout/layout_position.h"
|
||||||
|
#include "data/data_emoji_statuses.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
@ -134,6 +135,7 @@ struct EmojiListWidget::CustomEmojiInstance {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EmojiListWidget::RecentOne {
|
struct EmojiListWidget::RecentOne {
|
||||||
|
std::shared_ptr<Data::EmojiStatusCollectible> collectible;
|
||||||
Ui::Text::CustomEmoji *custom = nullptr;
|
Ui::Text::CustomEmoji *custom = nullptr;
|
||||||
RecentEmojiId id;
|
RecentEmojiId id;
|
||||||
mutable QImage premiumLock;
|
mutable QImage premiumLock;
|
||||||
|
@ -447,6 +449,13 @@ void EmojiColorPicker::drawVariant(QPainter &p, int variant) {
|
||||||
w.y() + _innerPosition.y());
|
w.y() + _innerPosition.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<EmojiStatusId> DocumentListToRecent(
|
||||||
|
const std::vector<DocumentId> &documents) {
|
||||||
|
return documents | ranges::views::transform([](DocumentId id) {
|
||||||
|
return EmojiStatusId{ .documentId = id };
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
}
|
||||||
|
|
||||||
EmojiListWidget::EmojiListWidget(
|
EmojiListWidget::EmojiListWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
|
@ -510,6 +519,11 @@ EmojiListWidget::EmojiListWidget(
|
||||||
refreshCustom();
|
refreshCustom();
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
} else if (_mode == Mode::EmojiStatus && _features.collectibleStatus) {
|
||||||
|
session().data().emojiStatuses().collectiblesUpdates(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
refreshCustom();
|
||||||
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
_customSingleSize = Data::FrameSizeFromTag(
|
_customSingleSize = Data::FrameSizeFromTag(
|
||||||
|
@ -656,7 +670,8 @@ void EmojiListWidget::applyNextSearchQuery() {
|
||||||
void EmojiListWidget::showPreview() {
|
void EmojiListWidget::showPreview() {
|
||||||
if (const auto over = std::get_if<OverEmoji>(&_pressed)) {
|
if (const auto over = std::get_if<OverEmoji>(&_pressed)) {
|
||||||
if (const auto custom = lookupCustomEmoji(over)) {
|
if (const auto custom = lookupCustomEmoji(over)) {
|
||||||
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
|
const auto document = custom.document;
|
||||||
|
_show->showMediaPreview(document->stickerSetOrigin(), document);
|
||||||
_previewShown = true;
|
_previewShown = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -706,7 +721,7 @@ void EmojiListWidget::appendPremiumSearchResults() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiListWidget::provideRecent(
|
void EmojiListWidget::provideRecent(
|
||||||
const std::vector<DocumentId> &customRecentList) {
|
const std::vector<EmojiStatusId> &customRecentList) {
|
||||||
clearSelection();
|
clearSelection();
|
||||||
fillRecentFrom(customRecentList);
|
fillRecentFrom(customRecentList);
|
||||||
resizeToWidth(width());
|
resizeToWidth(width());
|
||||||
|
@ -1094,7 +1109,8 @@ void EmojiListWidget::fillRecent() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) {
|
void EmojiListWidget::fillRecentFrom(
|
||||||
|
const std::vector<EmojiStatusId> &list) {
|
||||||
const auto test = session().isTestMode();
|
const auto test = session().isTestMode();
|
||||||
_recent.clear();
|
_recent.clear();
|
||||||
_recent.reserve(list.size());
|
_recent.reserve(list.size());
|
||||||
|
@ -1113,11 +1129,17 @@ void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) {
|
||||||
});
|
});
|
||||||
_recentCustomIds.emplace(fakeId);
|
_recentCustomIds.emplace(fakeId);
|
||||||
} else {
|
} else {
|
||||||
|
const auto documentId = id.collectible
|
||||||
|
? id.collectible->documentId
|
||||||
|
: id.documentId;
|
||||||
_recent.push_back({
|
_recent.push_back({
|
||||||
.custom = resolveCustomRecent(id),
|
.collectible = id.collectible,
|
||||||
.id = { RecentEmojiDocument{ .id = id, .test = test } },
|
.custom = resolveCustomRecent(documentId),
|
||||||
|
.id = {
|
||||||
|
RecentEmojiDocument{ .id = documentId, .test = test },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
_recentCustomIds.emplace(id);
|
_recentCustomIds.emplace(documentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1158,8 +1180,12 @@ void EmojiListWidget::fillRecentMenu(
|
||||||
const auto over = OverEmoji{ section, index };
|
const auto over = OverEmoji{ section, index };
|
||||||
const auto emoji = lookupOverEmoji(&over);
|
const auto emoji = lookupOverEmoji(&over);
|
||||||
const auto custom = lookupCustomEmoji(&over);
|
const auto custom = lookupCustomEmoji(&over);
|
||||||
if (custom && custom->sticker()) {
|
if (custom.collectible) {
|
||||||
const auto sticker = custom->sticker();
|
return;
|
||||||
|
}
|
||||||
|
const auto document = custom.document;
|
||||||
|
if (document && document->sticker()) {
|
||||||
|
const auto sticker = document->sticker();
|
||||||
const auto emoji = sticker->alt;
|
const auto emoji = sticker->alt;
|
||||||
const auto setId = sticker->set.id;
|
const auto setId = sticker->set.id;
|
||||||
if (!emoji.isEmpty()) {
|
if (!emoji.isEmpty()) {
|
||||||
|
@ -1168,7 +1194,7 @@ void EmojiListWidget::fillRecentMenu(
|
||||||
EntityType::CustomEmoji,
|
EntityType::CustomEmoji,
|
||||||
0,
|
0,
|
||||||
int(emoji.size()),
|
int(emoji.size()),
|
||||||
Data::SerializeCustomEmojiId(custom)
|
Data::SerializeCustomEmojiId(document)
|
||||||
});
|
});
|
||||||
addAction(tr::lng_emoji_copy(tr::now), [=] {
|
addAction(tr::lng_emoji_copy(tr::now), [=] {
|
||||||
TextUtilities::SetClipboardText(data);
|
TextUtilities::SetClipboardText(data);
|
||||||
|
@ -1192,8 +1218,8 @@ void EmojiListWidget::fillRecentMenu(
|
||||||
auto id = RecentEmojiId{ emoji };
|
auto id = RecentEmojiId{ emoji };
|
||||||
if (custom) {
|
if (custom) {
|
||||||
id.data = RecentEmojiDocument{
|
id.data = RecentEmojiDocument{
|
||||||
.id = custom->id,
|
.id = custom.document->id,
|
||||||
.test = custom->session().isTestMode(),
|
.test = custom.document->session().isTestMode(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
addAction(tr::lng_emoji_remove_recent(tr::now), crl::guard(this, [=] {
|
addAction(tr::lng_emoji_remove_recent(tr::now), crl::guard(this, [=] {
|
||||||
|
@ -1229,7 +1255,7 @@ void EmojiListWidget::fillEmojiStatusMenu(
|
||||||
int section,
|
int section,
|
||||||
int index) {
|
int index) {
|
||||||
const auto chosen = lookupCustomEmoji(index, section);
|
const auto chosen = lookupCustomEmoji(index, section);
|
||||||
if (!chosen) {
|
if (!chosen || chosen.collectible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto selectWith = [=](TimeId scheduled) {
|
const auto selectWith = [=](TimeId scheduled) {
|
||||||
|
@ -1464,6 +1490,27 @@ void EmojiListWidget::drawCollapsedBadge(
|
||||||
text);
|
text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiListWidget::drawCollectible(
|
||||||
|
QPainter &p,
|
||||||
|
QPoint position,
|
||||||
|
Data::EmojiStatusCollectible *collectible) {
|
||||||
|
if (!collectible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto inner = QRect(position, st::emojiPanArea);
|
||||||
|
auto gradient = QRadialGradient(inner.center(), inner.height() / 2);
|
||||||
|
gradient.setStops({
|
||||||
|
{ 0., collectible->centerColor },
|
||||||
|
{ 1., collectible->edgeColor },
|
||||||
|
});
|
||||||
|
p.setBrush(gradient);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.drawRoundedRect(
|
||||||
|
inner,
|
||||||
|
st::emojiPanRadius,
|
||||||
|
st::emojiPanRadius);
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiListWidget::drawRecent(
|
void EmojiListWidget::drawRecent(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
const ExpandingContext &context,
|
const ExpandingContext &context,
|
||||||
|
@ -1500,12 +1547,14 @@ void EmojiListWidget::drawRecent(
|
||||||
|
|
||||||
auto q = Painter(&_premiumMarkFrameCache);
|
auto q = Painter(&_premiumMarkFrameCache);
|
||||||
_emojiPaintContext->position = QPoint();
|
_emojiPaintContext->position = QPoint();
|
||||||
|
drawCollectible(q, position, recent.collectible.get());
|
||||||
custom->paint(q, *_emojiPaintContext);
|
custom->paint(q, *_emojiPaintContext);
|
||||||
q.end();
|
q.end();
|
||||||
|
|
||||||
p.drawImage(exactPosition, _premiumMarkFrameCache);
|
p.drawImage(exactPosition, _premiumMarkFrameCache);
|
||||||
} else {
|
} else {
|
||||||
_emojiPaintContext->position = exactPosition;
|
_emojiPaintContext->position = exactPosition;
|
||||||
|
drawCollectible(p, position, recent.collectible.get());
|
||||||
custom->paint(p, *_emojiPaintContext);
|
custom->paint(p, *_emojiPaintContext);
|
||||||
}
|
}
|
||||||
} else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) {
|
} else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) {
|
||||||
|
@ -1560,6 +1609,7 @@ void EmojiListWidget::drawCustom(
|
||||||
_emojiPaintContext->position = position
|
_emojiPaintContext->position = position
|
||||||
+ _innerPosition
|
+ _innerPosition
|
||||||
+ _customPosition;
|
+ _customPosition;
|
||||||
|
drawCollectible(p, position, entry.collectible.get());
|
||||||
entry.custom->paint(p, *_emojiPaintContext);
|
entry.custom->paint(p, *_emojiPaintContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1573,12 +1623,14 @@ bool EmojiListWidget::checkPickerHide() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentData *EmojiListWidget::lookupCustomEmoji(
|
EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
|
||||||
const OverEmoji *over) const {
|
const OverEmoji *over) const {
|
||||||
return over ? lookupCustomEmoji(over->index, over->section) : nullptr;
|
return over
|
||||||
|
? lookupCustomEmoji(over->index, over->section)
|
||||||
|
: ResolvedCustom();
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentData *EmojiListWidget::lookupCustomEmoji(
|
EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
|
||||||
int index,
|
int index,
|
||||||
int section) const {
|
int section) const {
|
||||||
if (_searchMode) {
|
if (_searchMode) {
|
||||||
|
@ -1586,22 +1638,23 @@ DocumentData *EmojiListWidget::lookupCustomEmoji(
|
||||||
const auto document = std::get_if<RecentEmojiDocument>(
|
const auto document = std::get_if<RecentEmojiDocument>(
|
||||||
&_searchResults[index].id.data);
|
&_searchResults[index].id.data);
|
||||||
if (document) {
|
if (document) {
|
||||||
return session().data().document(document->id);
|
return { session().data().document(document->id) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return {};
|
||||||
} else if (section == int(Section::Recent) && index < _recent.size()) {
|
} else if (section == int(Section::Recent) && index < _recent.size()) {
|
||||||
const auto document = std::get_if<RecentEmojiDocument>(
|
const auto document = std::get_if<RecentEmojiDocument>(
|
||||||
&_recent[index].id.data);
|
&_recent[index].id.data);
|
||||||
if (document) {
|
if (document) {
|
||||||
return session().data().document(document->id);
|
return { session().data().document(document->id) };
|
||||||
}
|
}
|
||||||
} else if (section >= _staticCount
|
} else if (section >= _staticCount
|
||||||
&& index < _custom[section - _staticCount].list.size()) {
|
&& index < _custom[section - _staticCount].list.size()) {
|
||||||
auto &set = _custom[section - _staticCount];
|
auto &set = _custom[section - _staticCount];
|
||||||
return set.list[index].document;
|
auto &entry = set.list[index];
|
||||||
|
return { entry.document, entry.collectible };
|
||||||
}
|
}
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const {
|
EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const {
|
||||||
|
@ -1643,9 +1696,11 @@ EmojiChosen EmojiListWidget::lookupChosen(
|
||||||
}
|
}
|
||||||
|
|
||||||
FileChosen EmojiListWidget::lookupChosen(
|
FileChosen EmojiListWidget::lookupChosen(
|
||||||
not_null<DocumentData*> custom,
|
ResolvedCustom custom,
|
||||||
const OverEmoji *over,
|
const OverEmoji *over,
|
||||||
Api::SendOptions options) {
|
Api::SendOptions options) {
|
||||||
|
Expects(custom.document != nullptr);
|
||||||
|
|
||||||
_grabbingChosen = true;
|
_grabbingChosen = true;
|
||||||
const auto guard = gsl::finally([&] { _grabbingChosen = false; });
|
const auto guard = gsl::finally([&] { _grabbingChosen = false; });
|
||||||
const auto rect = over ? emojiRect(over->section, over->index) : QRect();
|
const auto rect = over ? emojiRect(over->section, over->index) : QRect();
|
||||||
|
@ -1655,13 +1710,14 @@ FileChosen EmojiListWidget::lookupChosen(
|
||||||
) : QRect();
|
) : QRect();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.document = custom,
|
.document = custom.document,
|
||||||
.options = options,
|
.options = options,
|
||||||
.messageSendingFrom = {
|
.messageSendingFrom = {
|
||||||
.type = Ui::MessageSendingAnimationFrom::Type::Emoji,
|
.type = Ui::MessageSendingAnimationFrom::Type::Emoji,
|
||||||
.globalStartGeometry = over ? mapToGlobal(emoji) : QRect(),
|
.globalStartGeometry = over ? mapToGlobal(emoji) : QRect(),
|
||||||
.frame = over ? Ui::GrabWidgetToImage(this, emoji) : QImage(),
|
.frame = over ? Ui::GrabWidgetToImage(this, emoji) : QImage(),
|
||||||
},
|
},
|
||||||
|
.collectible = custom.collectible,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1791,6 +1847,8 @@ void EmojiListWidget::displaySet(uint64 setId) {
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (setId == Data::Stickers::CollectibleSetId) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const auto &sets = session().data().stickers().sets();
|
const auto &sets = session().data().stickers().sets();
|
||||||
auto it = sets.find(setId);
|
auto it = sets.find(setId);
|
||||||
|
@ -1829,6 +1887,7 @@ void EmojiListWidget::removeSet(uint64 setId) {
|
||||||
Assert(i != end(_custom));
|
Assert(i != end(_custom));
|
||||||
const auto removeLocally = !_megagroupSet->canEditEmoji();
|
const auto removeLocally = !_megagroupSet->canEditEmoji();
|
||||||
removeMegagroupSet(removeLocally);
|
removeMegagroupSet(removeLocally);
|
||||||
|
} else if (setId == Data::Stickers::CollectibleSetId) {
|
||||||
} else if (auto box = MakeConfirmRemoveSetBox(&session(), labelSt, setId)) {
|
} else if (auto box = MakeConfirmRemoveSetBox(&session(), labelSt, setId)) {
|
||||||
checkHideWithBox(std::move(box));
|
checkHideWithBox(std::move(box));
|
||||||
}
|
}
|
||||||
|
@ -1932,6 +1991,8 @@ bool EmojiListWidget::hasRemoveButton(int index) const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return !set.list.empty() && _megagroupSet->canEditEmoji();
|
return !set.list.empty() && _megagroupSet->canEditEmoji();
|
||||||
|
} else if (set.id == Data::Stickers::CollectibleSetId) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return set.canRemove && !set.premiumRequired;
|
return set.canRemove && !set.premiumRequired;
|
||||||
}
|
}
|
||||||
|
@ -1961,7 +2022,8 @@ bool EmojiListWidget::hasAddButton(int index) const {
|
||||||
const auto &set = _custom[index - _staticCount];
|
const auto &set = _custom[index - _staticCount];
|
||||||
return !set.canRemove
|
return !set.canRemove
|
||||||
&& !set.premiumRequired
|
&& !set.premiumRequired
|
||||||
&& set.id != Data::Stickers::MegagroupSetId;
|
&& set.id != Data::Stickers::MegagroupSetId
|
||||||
|
&& set.id != Data::Stickers::CollectibleSetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect EmojiListWidget::addButtonRect(int index) const {
|
QRect EmojiListWidget::addButtonRect(int index) const {
|
||||||
|
@ -1990,8 +2052,9 @@ bool EmojiListWidget::hasButton(int index) const {
|
||||||
} else if (index >= _staticCount
|
} else if (index >= _staticCount
|
||||||
&& index < _staticCount + _custom.size()) {
|
&& index < _staticCount + _custom.size()) {
|
||||||
const auto &custom = _custom[index - _staticCount];
|
const auto &custom = _custom[index - _staticCount];
|
||||||
return (custom.id != Data::Stickers::MegagroupSetId)
|
return (custom.id != Data::Stickers::CollectibleSetId)
|
||||||
|| custom.canRemove;
|
&& ((custom.id != Data::Stickers::MegagroupSetId)
|
||||||
|
|| custom.canRemove);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2019,6 +2082,7 @@ QRect EmojiListWidget::buttonRect(
|
||||||
auto EmojiListWidget::rightButton(int index) const -> const RightButton & {
|
auto EmojiListWidget::rightButton(int index) const -> const RightButton & {
|
||||||
Expects(index >= _staticCount
|
Expects(index >= _staticCount
|
||||||
&& index < _staticCount + _custom.size());
|
&& index < _staticCount + _custom.size());
|
||||||
|
|
||||||
return hasAddButton(index)
|
return hasAddButton(index)
|
||||||
? _add
|
? _add
|
||||||
: _custom[index - _staticCount].canRemove
|
: _custom[index - _staticCount].canRemove
|
||||||
|
@ -2304,6 +2368,7 @@ void EmojiListWidget::refreshCustom() {
|
||||||
.premiumRequired = premium && premiumMayBeBought,
|
.premiumRequired = premium && premiumMayBeBought,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
refreshEmojiStatusCollectibles();
|
||||||
refreshMegagroupStickers(push, GroupStickersPlace::Visible);
|
refreshMegagroupStickers(push, GroupStickersPlace::Visible);
|
||||||
for (const auto setId : owner->stickers().emojiSetsOrder()) {
|
for (const auto setId : owner->stickers().emojiSetsOrder()) {
|
||||||
push(setId, true);
|
push(setId, true);
|
||||||
|
@ -2404,6 +2469,44 @@ not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomRecent(
|
||||||
).first->second.emoji.get();
|
).first->second.emoji.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiListWidget::refreshEmojiStatusCollectibles() {
|
||||||
|
if (_mode != Mode::EmojiStatus || !_features.collectibleStatus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto type = Data::EmojiStatuses::Type::Collectibles;
|
||||||
|
const auto &list = session().data().emojiStatuses().list(type);
|
||||||
|
if (list.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto setId = Data::Stickers::CollectibleSetId;
|
||||||
|
auto set = std::vector<CustomOne>();
|
||||||
|
set.reserve(list.size());
|
||||||
|
for (const auto &status : list) {
|
||||||
|
const auto documentId = status.collectible
|
||||||
|
? status.collectible->documentId
|
||||||
|
: status.documentId;
|
||||||
|
const auto document = session().data().document(documentId);
|
||||||
|
if (const auto sticker = document->sticker()) {
|
||||||
|
set.push_back({
|
||||||
|
.collectible = status.collectible,
|
||||||
|
.custom = resolveCustomEmoji(document, setId),
|
||||||
|
.document = document,
|
||||||
|
.emoji = Ui::Emoji::Find(sticker->alt),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto collectibles = session().data().stickers().collectibleSet();
|
||||||
|
_custom.push_back({
|
||||||
|
.id = setId,
|
||||||
|
.set = collectibles,
|
||||||
|
.thumbnailDocument = nullptr,
|
||||||
|
.title = collectibles->title,
|
||||||
|
.list = std::move(set),
|
||||||
|
.canRemove = false,
|
||||||
|
.premiumRequired = !session().premium(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiListWidget::refreshMegagroupStickers(
|
void EmojiListWidget::refreshMegagroupStickers(
|
||||||
Fn<void(uint64 setId, bool installed)> push,
|
Fn<void(uint64 setId, bool installed)> push,
|
||||||
GroupStickersPlace place) {
|
GroupStickersPlace place) {
|
||||||
|
@ -2638,8 +2741,11 @@ void EmojiListWidget::setSelected(OverState newSelected) {
|
||||||
} else if (_previewShown && _pressed != _selected) {
|
} else if (_previewShown && _pressed != _selected) {
|
||||||
if (const auto over = std::get_if<OverEmoji>(&_selected)) {
|
if (const auto over = std::get_if<OverEmoji>(&_selected)) {
|
||||||
if (const auto custom = lookupCustomEmoji(over)) {
|
if (const auto custom = lookupCustomEmoji(over)) {
|
||||||
|
const auto document = custom.document;
|
||||||
_pressed = _selected;
|
_pressed = _selected;
|
||||||
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
|
_show->showMediaPreview(
|
||||||
|
document->stickerSetOrigin(),
|
||||||
|
document);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,12 +83,15 @@ enum class EmojiListMode {
|
||||||
MessageEffects,
|
MessageEffects,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<EmojiStatusId> DocumentListToRecent(
|
||||||
|
const std::vector<DocumentId> &documents);
|
||||||
|
|
||||||
struct EmojiListDescriptor {
|
struct EmojiListDescriptor {
|
||||||
std::shared_ptr<Show> show;
|
std::shared_ptr<Show> show;
|
||||||
EmojiListMode mode = EmojiListMode::Full;
|
EmojiListMode mode = EmojiListMode::Full;
|
||||||
Fn<QColor()> customTextColor;
|
Fn<QColor()> customTextColor;
|
||||||
Fn<bool()> paused;
|
Fn<bool()> paused;
|
||||||
std::vector<DocumentId> customRecentList;
|
std::vector<EmojiStatusId> customRecentList;
|
||||||
Fn<std::unique_ptr<Ui::Text::CustomEmoji>(
|
Fn<std::unique_ptr<Ui::Text::CustomEmoji>(
|
||||||
DocumentId,
|
DocumentId,
|
||||||
Fn<void()>)> customRecentFactory;
|
Fn<void()>)> customRecentFactory;
|
||||||
|
@ -137,7 +140,7 @@ public:
|
||||||
[[nodiscard]] rpl::producer<> jumpedToPremium() const;
|
[[nodiscard]] rpl::producer<> jumpedToPremium() const;
|
||||||
[[nodiscard]] rpl::producer<> escapes() const;
|
[[nodiscard]] rpl::producer<> escapes() const;
|
||||||
|
|
||||||
void provideRecent(const std::vector<DocumentId> &customRecentList);
|
void provideRecent(const std::vector<EmojiStatusId> &customRecentList);
|
||||||
|
|
||||||
void prepareExpanding();
|
void prepareExpanding();
|
||||||
void paintExpanding(
|
void paintExpanding(
|
||||||
|
@ -186,6 +189,7 @@ private:
|
||||||
bool collapsed = false;
|
bool collapsed = false;
|
||||||
};
|
};
|
||||||
struct CustomOne {
|
struct CustomOne {
|
||||||
|
std::shared_ptr<Data::EmojiStatusCollectible> collectible;
|
||||||
not_null<Ui::Text::CustomEmoji*> custom;
|
not_null<Ui::Text::CustomEmoji*> custom;
|
||||||
not_null<DocumentData*> document;
|
not_null<DocumentData*> document;
|
||||||
EmojiPtr emoji = nullptr;
|
EmojiPtr emoji = nullptr;
|
||||||
|
@ -253,6 +257,14 @@ private:
|
||||||
int finalHeight = 0;
|
int finalHeight = 0;
|
||||||
bool expanding = false;
|
bool expanding = false;
|
||||||
};
|
};
|
||||||
|
struct ResolvedCustom {
|
||||||
|
DocumentData *document = nullptr;
|
||||||
|
std::shared_ptr<Data::EmojiStatusCollectible> collectible;
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return document != nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
bool enumerateSections(Callback callback) const;
|
bool enumerateSections(Callback callback) const;
|
||||||
|
@ -271,6 +283,7 @@ private:
|
||||||
Visible,
|
Visible,
|
||||||
Hidden,
|
Hidden,
|
||||||
};
|
};
|
||||||
|
void refreshEmojiStatusCollectibles();
|
||||||
void refreshMegagroupStickers(
|
void refreshMegagroupStickers(
|
||||||
Fn<void(uint64 setId, bool installed)> push,
|
Fn<void(uint64 setId, bool installed)> push,
|
||||||
GroupStickersPlace place);
|
GroupStickersPlace place);
|
||||||
|
@ -296,22 +309,26 @@ private:
|
||||||
int index);
|
int index);
|
||||||
|
|
||||||
[[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const;
|
[[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const;
|
||||||
[[nodiscard]] DocumentData *lookupCustomEmoji(
|
[[nodiscard]] ResolvedCustom lookupCustomEmoji(
|
||||||
const OverEmoji *over) const;
|
const OverEmoji *over) const;
|
||||||
[[nodiscard]] DocumentData *lookupCustomEmoji(
|
[[nodiscard]] ResolvedCustom lookupCustomEmoji(
|
||||||
int index,
|
int index,
|
||||||
int section) const;
|
int section) const;
|
||||||
[[nodiscard]] EmojiChosen lookupChosen(
|
[[nodiscard]] EmojiChosen lookupChosen(
|
||||||
EmojiPtr emoji,
|
EmojiPtr emoji,
|
||||||
not_null<const OverEmoji*> over);
|
not_null<const OverEmoji*> over);
|
||||||
[[nodiscard]] FileChosen lookupChosen(
|
[[nodiscard]] FileChosen lookupChosen(
|
||||||
not_null<DocumentData*> custom,
|
ResolvedCustom custom,
|
||||||
const OverEmoji *over,
|
const OverEmoji *over,
|
||||||
Api::SendOptions options = Api::SendOptions());
|
Api::SendOptions options = Api::SendOptions());
|
||||||
void selectEmoji(EmojiChosen data);
|
void selectEmoji(EmojiChosen data);
|
||||||
void selectCustom(FileChosen data);
|
void selectCustom(FileChosen data);
|
||||||
void paint(Painter &p, ExpandingContext context, QRect clip);
|
void paint(Painter &p, ExpandingContext context, QRect clip);
|
||||||
void drawCollapsedBadge(QPainter &p, QPoint position, int count);
|
void drawCollapsedBadge(QPainter &p, QPoint position, int count);
|
||||||
|
void drawCollectible(
|
||||||
|
QPainter &p,
|
||||||
|
QPoint position,
|
||||||
|
Data::EmojiStatusCollectible *collectible);
|
||||||
void drawRecent(
|
void drawRecent(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
const ExpandingContext &context,
|
const ExpandingContext &context,
|
||||||
|
@ -370,7 +387,7 @@ private:
|
||||||
void repaintCustom(uint64 setId);
|
void repaintCustom(uint64 setId);
|
||||||
|
|
||||||
void fillRecent();
|
void fillRecent();
|
||||||
void fillRecentFrom(const std::vector<DocumentId> &list);
|
void fillRecentFrom(const std::vector<EmojiStatusId> &list);
|
||||||
[[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomEmoji(
|
[[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomEmoji(
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
uint64 setId);
|
uint64 setId);
|
||||||
|
|
|
@ -1467,6 +1467,8 @@ void StickersListFooter::paintSetIconToCache(
|
||||||
return &st().icons.people;
|
return &st().icons.people;
|
||||||
} else if (const auto section = SetIdEmojiSection(icon.setId)) {
|
} else if (const auto section = SetIdEmojiSection(icon.setId)) {
|
||||||
return sectionIcon(*section, selected);
|
return sectionIcon(*section, selected);
|
||||||
|
} else if (icon.setId == Data::Stickers::CollectibleSetId) {
|
||||||
|
return &st().icons.collectibles;
|
||||||
}
|
}
|
||||||
return sectionIcon(Section::Recent, selected);
|
return sectionIcon(Section::Recent, selected);
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -1017,7 +1017,7 @@ void TabbedSelector::setCurrentPeer(PeerData *peer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabbedSelector::provideRecentEmoji(
|
void TabbedSelector::provideRecentEmoji(
|
||||||
const std::vector<DocumentId> &customRecentList) {
|
const std::vector<EmojiStatusId> &customRecentList) {
|
||||||
for (const auto &tab : _tabs) {
|
for (const auto &tab : _tabs) {
|
||||||
if (tab.type() == SelectorTab::Emoji) {
|
if (tab.type() == SelectorTab::Emoji) {
|
||||||
const auto emoji = static_cast<EmojiListWidget*>(tab.widget());
|
const auto emoji = static_cast<EmojiListWidget*>(tab.widget());
|
||||||
|
|
|
@ -62,6 +62,7 @@ struct FileChosen {
|
||||||
not_null<DocumentData*> document;
|
not_null<DocumentData*> document;
|
||||||
Api::SendOptions options;
|
Api::SendOptions options;
|
||||||
Ui::MessageSendingAnimationFrom messageSendingFrom;
|
Ui::MessageSendingAnimationFrom messageSendingFrom;
|
||||||
|
std::shared_ptr<Data::EmojiStatusCollectible> collectible;
|
||||||
TextWithTags caption;
|
TextWithTags caption;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,7 +155,7 @@ public:
|
||||||
void refreshStickers();
|
void refreshStickers();
|
||||||
void setCurrentPeer(PeerData *peer);
|
void setCurrentPeer(PeerData *peer);
|
||||||
void provideRecentEmoji(
|
void provideRecentEmoji(
|
||||||
const std::vector<DocumentId> &customRecentList);
|
const std::vector<EmojiStatusId> &customRecentList);
|
||||||
|
|
||||||
void hideFinished();
|
void hideFinished();
|
||||||
void showStarted();
|
void showStarted();
|
||||||
|
|
|
@ -84,6 +84,10 @@ void EmojiStatuses::refreshChannelColored() {
|
||||||
requestChannelColored();
|
requestChannelColored();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiStatuses::refreshCollectibles() {
|
||||||
|
requestCollectibles();
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiStatuses::refreshRecentDelayed() {
|
void EmojiStatuses::refreshRecentDelayed() {
|
||||||
if (_recentRequestId || _recentRequestScheduled) {
|
if (_recentRequestId || _recentRequestScheduled) {
|
||||||
return;
|
return;
|
||||||
|
@ -103,6 +107,7 @@ const std::vector<EmojiStatusId> &EmojiStatuses::list(Type type) const {
|
||||||
case Type::Colored: return _colored;
|
case Type::Colored: return _colored;
|
||||||
case Type::ChannelDefault: return _channelDefault;
|
case Type::ChannelDefault: return _channelDefault;
|
||||||
case Type::ChannelColored: return _channelColored;
|
case Type::ChannelColored: return _channelColored;
|
||||||
|
case Type::Collectibles: return _collectibles;
|
||||||
}
|
}
|
||||||
Unexpected("Type in EmojiStatuses::list.");
|
Unexpected("Type in EmojiStatuses::list.");
|
||||||
}
|
}
|
||||||
|
@ -371,6 +376,7 @@ void EmojiStatuses::requestColored() {
|
||||||
_coloredRequestId = 0;
|
_coloredRequestId = 0;
|
||||||
result.match([&](const MTPDmessages_stickerSet &data) {
|
result.match([&](const MTPDmessages_stickerSet &data) {
|
||||||
updateColored(data);
|
updateColored(data);
|
||||||
|
refreshCollectibles();
|
||||||
}, [](const MTPDmessages_stickerSetNotModified &) {
|
}, [](const MTPDmessages_stickerSetNotModified &) {
|
||||||
LOG(("API Error: Unexpected messages.stickerSetNotModified."));
|
LOG(("API Error: Unexpected messages.stickerSetNotModified."));
|
||||||
});
|
});
|
||||||
|
@ -418,6 +424,25 @@ void EmojiStatuses::requestChannelColored() {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiStatuses::requestCollectibles() {
|
||||||
|
if (_collectiblesRequestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &api = _owner->session().api();
|
||||||
|
_collectiblesRequestId = api.request(
|
||||||
|
MTPaccount_GetCollectibleEmojiStatuses(MTP_long(_collectiblesHash))
|
||||||
|
).done([=](const MTPaccount_EmojiStatuses &result) {
|
||||||
|
_collectiblesRequestId = 0;
|
||||||
|
result.match([&](const MTPDaccount_emojiStatuses &data) {
|
||||||
|
updateCollectibles(data);
|
||||||
|
}, [&](const MTPDaccount_emojiStatusesNotModified &) {
|
||||||
|
});
|
||||||
|
}).fail([=] {
|
||||||
|
_collectiblesRequestId = 0;
|
||||||
|
_collectiblesHash = 0;
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiStatuses::updateRecent(const MTPDaccount_emojiStatuses &data) {
|
void EmojiStatuses::updateRecent(const MTPDaccount_emojiStatuses &data) {
|
||||||
_recentHash = data.vhash().v;
|
_recentHash = data.vhash().v;
|
||||||
_recent = parse(data);
|
_recent = parse(data);
|
||||||
|
@ -462,6 +487,13 @@ void EmojiStatuses::updateChannelColored(
|
||||||
_channelColoredUpdated.fire({});
|
_channelColoredUpdated.fire({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiStatuses::updateCollectibles(
|
||||||
|
const MTPDaccount_emojiStatuses &data) {
|
||||||
|
_collectiblesHash = data.vhash().v;
|
||||||
|
_collectibles = parse(data);
|
||||||
|
_collectiblesUpdated.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiStatuses::set(EmojiStatusId id, TimeId until) {
|
void EmojiStatuses::set(EmojiStatusId id, TimeId until) {
|
||||||
set(_owner->session().user(), id, until);
|
set(_owner->session().user(), id, until);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
void refreshColored();
|
void refreshColored();
|
||||||
void refreshChannelDefault();
|
void refreshChannelDefault();
|
||||||
void refreshChannelColored();
|
void refreshChannelColored();
|
||||||
|
void refreshCollectibles();
|
||||||
|
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Recent,
|
Recent,
|
||||||
|
@ -103,12 +104,14 @@ private:
|
||||||
void requestColored();
|
void requestColored();
|
||||||
void requestChannelDefault();
|
void requestChannelDefault();
|
||||||
void requestChannelColored();
|
void requestChannelColored();
|
||||||
|
void requestCollectibles();
|
||||||
|
|
||||||
void updateRecent(const MTPDaccount_emojiStatuses &data);
|
void updateRecent(const MTPDaccount_emojiStatuses &data);
|
||||||
void updateDefault(const MTPDaccount_emojiStatuses &data);
|
void updateDefault(const MTPDaccount_emojiStatuses &data);
|
||||||
void updateColored(const MTPDmessages_stickerSet &data);
|
void updateColored(const MTPDmessages_stickerSet &data);
|
||||||
void updateChannelDefault(const MTPDaccount_emojiStatuses &data);
|
void updateChannelDefault(const MTPDaccount_emojiStatuses &data);
|
||||||
void updateChannelColored(const MTPDmessages_stickerSet &data);
|
void updateChannelColored(const MTPDmessages_stickerSet &data);
|
||||||
|
void updateCollectibles(const MTPDaccount_emojiStatuses &data);
|
||||||
|
|
||||||
void processClearingIn(TimeId wait);
|
void processClearingIn(TimeId wait);
|
||||||
void processClearing();
|
void processClearing();
|
||||||
|
@ -153,6 +156,7 @@ private:
|
||||||
mtpRequestId _channelColoredRequestId = 0;
|
mtpRequestId _channelColoredRequestId = 0;
|
||||||
|
|
||||||
mtpRequestId _collectiblesRequestId = 0;
|
mtpRequestId _collectiblesRequestId = 0;
|
||||||
|
uint64 _collectiblesHash = 0;
|
||||||
|
|
||||||
base::flat_map<not_null<PeerData*>, mtpRequestId> _sentRequests;
|
base::flat_map<not_null<PeerData*>, mtpRequestId> _sentRequests;
|
||||||
|
|
||||||
|
|
|
@ -814,6 +814,25 @@ void Stickers::setPackAndEmoji(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<StickersSet*> Stickers::collectibleSet() {
|
||||||
|
const auto setId = CollectibleSetId;
|
||||||
|
auto &sets = setsRef();
|
||||||
|
auto it = sets.find(setId);
|
||||||
|
if (it == sets.cend()) {
|
||||||
|
it = sets.emplace(setId, std::make_unique<StickersSet>(
|
||||||
|
&owner(),
|
||||||
|
setId,
|
||||||
|
uint64(0), // accessHash
|
||||||
|
uint64(0), // hash
|
||||||
|
tr::lng_collectible_emoji(tr::now),
|
||||||
|
QString(),
|
||||||
|
0, // count
|
||||||
|
SetFlag::Special,
|
||||||
|
TimeId(0))).first;
|
||||||
|
}
|
||||||
|
return it->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
void Stickers::specialSetReceived(
|
void Stickers::specialSetReceived(
|
||||||
uint64 setId,
|
uint64 setId,
|
||||||
const QString &setTitle,
|
const QString &setTitle,
|
||||||
|
|
|
@ -65,6 +65,9 @@ public:
|
||||||
// For setting up megagroup sticker set.
|
// For setting up megagroup sticker set.
|
||||||
static constexpr auto MegagroupSetId = 0xFFFFFFFFFFFFFFEFULL;
|
static constexpr auto MegagroupSetId = 0xFFFFFFFFFFFFFFEFULL;
|
||||||
|
|
||||||
|
// For collectible emoji statuses.
|
||||||
|
static constexpr auto CollectibleSetId = 0xFFFFFFFFFFFFFFF8ULL;
|
||||||
|
|
||||||
void notifyUpdated(StickersType type);
|
void notifyUpdated(StickersType type);
|
||||||
[[nodiscard]] rpl::producer<StickersType> updated() const;
|
[[nodiscard]] rpl::producer<StickersType> updated() const;
|
||||||
[[nodiscard]] rpl::producer<> updated(StickersType type) const;
|
[[nodiscard]] rpl::producer<> updated(StickersType type) const;
|
||||||
|
@ -244,6 +247,8 @@ public:
|
||||||
[[nodiscard]] auto getEmojiListFromSet(not_null<DocumentData*> document)
|
[[nodiscard]] auto getEmojiListFromSet(not_null<DocumentData*> document)
|
||||||
-> std::optional<std::vector<not_null<EmojiPtr>>>;
|
-> std::optional<std::vector<not_null<EmojiPtr>>>;
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<StickersSet*> collectibleSet();
|
||||||
|
|
||||||
not_null<StickersSet*> feedSet(const MTPStickerSet &data);
|
not_null<StickersSet*> feedSet(const MTPStickerSet &data);
|
||||||
not_null<StickersSet*> feedSet(const MTPStickerSetCovered &data);
|
not_null<StickersSet*> feedSet(const MTPStickerSetCovered &data);
|
||||||
not_null<StickersSet*> feedSetFull(const MTPDmessages_stickerSet &data);
|
not_null<StickersSet*> feedSetFull(const MTPDmessages_stickerSet &data);
|
||||||
|
|
|
@ -33,6 +33,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_chat_filters.h"
|
#include "data/data_chat_filters.h"
|
||||||
#include "data/data_send_action.h"
|
#include "data/data_send_action.h"
|
||||||
|
#include "data/data_star_gift.h"
|
||||||
|
#include "data/data_emoji_statuses.h"
|
||||||
#include "data/data_folder.h"
|
#include "data/data_folder.h"
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
@ -1300,6 +1302,15 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
|
||||||
if (const auto topic = item->topic()) {
|
if (const auto topic = item->topic()) {
|
||||||
topic->applyItemAdded(item);
|
topic->applyItemAdded(item);
|
||||||
}
|
}
|
||||||
|
if (const auto media = item->media()) {
|
||||||
|
if (const auto gift = media->gift()) {
|
||||||
|
if (const auto unique = gift->unique.get()) {
|
||||||
|
if (unique->ownerId == session().userPeerId()) {
|
||||||
|
owner().emojiStatuses().refreshCollectibles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::registerClientSideMessage(not_null<HistoryItem*> item) {
|
void History::registerClientSideMessage(not_null<HistoryItem*> item) {
|
||||||
|
|
|
@ -522,9 +522,7 @@ auto UniqueGiftBg(
|
||||||
p.setBrush(Qt::transparent);
|
p.setBrush(Qt::transparent);
|
||||||
p.drawRoundedRect(inner, radius, radius);
|
p.drawRoundedRect(inner, radius, radius);
|
||||||
}
|
}
|
||||||
auto gradient = QRadialGradient(
|
auto gradient = QRadialGradient(inner.center(), inner.height() / 2);
|
||||||
inner.center(),
|
|
||||||
inner.height() / 2);
|
|
||||||
gradient.setStops({
|
gradient.setStops({
|
||||||
{ 0., gift->backdrop.centerColor },
|
{ 0., gift->backdrop.centerColor },
|
||||||
{ 1., gift->backdrop.edgeColor },
|
{ 1., gift->backdrop.edgeColor },
|
||||||
|
|
|
@ -1047,7 +1047,7 @@ void Selector::createList() {
|
||||||
.show = _show,
|
.show = _show,
|
||||||
.mode = _listMode,
|
.mode = _listMode,
|
||||||
.paused = _paused ? _paused : [] { return false; },
|
.paused = _paused ? _paused : [] { return false; },
|
||||||
.customRecentList = std::move(recentList),
|
.customRecentList = DocumentListToRecent(recentList),
|
||||||
.customRecentFactory = _unifiedFactoryOwner->factory(),
|
.customRecentFactory = _unifiedFactoryOwner->factory(),
|
||||||
.freeEffects = std::move(freeEffects),
|
.freeEffects = std::move(freeEffects),
|
||||||
.st = st,
|
.st = st,
|
||||||
|
|
|
@ -70,7 +70,7 @@ EmojiStatusPanel::~EmojiStatusPanel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiStatusPanel::setChooseFilter(Fn<bool(DocumentId)> filter) {
|
void EmojiStatusPanel::setChooseFilter(Fn<bool(EmojiStatusId)> filter) {
|
||||||
_chooseFilter = std::move(filter);
|
_chooseFilter = std::move(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ void EmojiStatusPanel::show(
|
||||||
.button = button,
|
.button = button,
|
||||||
.animationSizeTag = animationSizeTag,
|
.animationSizeTag = animationSizeTag,
|
||||||
.ensureAddedEmojiId = controller->session().user()->emojiStatusId(),
|
.ensureAddedEmojiId = controller->session().user()->emojiStatusId(),
|
||||||
|
.withCollectibles = true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,20 +117,14 @@ void EmojiStatusPanel::show(Descriptor &&descriptor) {
|
||||||
if (now && !ranges::contains(list, now)) {
|
if (now && !ranges::contains(list, now)) {
|
||||||
list.push_back(now);
|
list.push_back(now);
|
||||||
}
|
}
|
||||||
auto tmp = std::vector<DocumentId>();
|
_panel->selector()->provideRecentEmoji(list);
|
||||||
for (const auto &id : list) {
|
|
||||||
if (id.documentId) { // todo collectibles
|
|
||||||
tmp.push_back(id.documentId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_panel->selector()->provideRecentEmoji(tmp);
|
|
||||||
};
|
};
|
||||||
if (descriptor.backgroundEmojiMode) {
|
if (descriptor.backgroundEmojiMode) {
|
||||||
controller->session().api().peerPhoto().emojiListValue(
|
controller->session().api().peerPhoto().emojiListValue(
|
||||||
Api::PeerPhoto::EmojiListType::Background
|
Api::PeerPhoto::EmojiListType::Background
|
||||||
) | rpl::start_with_next([=](std::vector<DocumentId> &&list) {
|
) | rpl::start_with_next([=](std::vector<DocumentId> &&list) {
|
||||||
auto tmp = std::vector<EmojiStatusId>();
|
auto tmp = std::vector<EmojiStatusId>();
|
||||||
for (const auto &id : list) { // todo collectibles
|
for (const auto &id : list) {
|
||||||
tmp.push_back(EmojiStatusId{ .documentId = id });
|
tmp.push_back(EmojiStatusId{ .documentId = id });
|
||||||
}
|
}
|
||||||
feed(std::move(tmp));
|
feed(std::move(tmp));
|
||||||
|
@ -203,6 +198,8 @@ void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
using Mode = ChatHelpers::TabbedSelector::Mode;
|
using Mode = ChatHelpers::TabbedSelector::Mode;
|
||||||
const auto controller = descriptor.controller;
|
const auto controller = descriptor.controller;
|
||||||
const auto body = controller->window().widget()->bodyWidget();
|
const auto body = controller->window().widget()->bodyWidget();
|
||||||
|
auto features = ChatHelpers::ComposeFeatures();
|
||||||
|
features.collectibleStatus = descriptor.withCollectibles;
|
||||||
_panel = base::make_unique_q<ChatHelpers::TabbedPanel>(
|
_panel = base::make_unique_q<ChatHelpers::TabbedPanel>(
|
||||||
body,
|
body,
|
||||||
controller,
|
controller,
|
||||||
|
@ -221,6 +218,7 @@ void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
? Mode::ChannelStatus
|
? Mode::ChannelStatus
|
||||||
: Mode::EmojiStatus),
|
: Mode::EmojiStatus),
|
||||||
.customTextColor = descriptor.customTextColor,
|
.customTextColor = descriptor.customTextColor,
|
||||||
|
.features = features,
|
||||||
}));
|
}));
|
||||||
_customTextColor = descriptor.customTextColor;
|
_customTextColor = descriptor.customTextColor;
|
||||||
_backgroundEmojiMode = descriptor.backgroundEmojiMode;
|
_backgroundEmojiMode = descriptor.backgroundEmojiMode;
|
||||||
|
@ -233,7 +231,7 @@ void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
_panel->hide();
|
_panel->hide();
|
||||||
|
|
||||||
struct Chosen {
|
struct Chosen {
|
||||||
DocumentId id = 0;
|
EmojiStatusId id;
|
||||||
TimeId until = 0;
|
TimeId until = 0;
|
||||||
Ui::MessageSendingAnimationFrom animation;
|
Ui::MessageSendingAnimationFrom animation;
|
||||||
};
|
};
|
||||||
|
@ -246,7 +244,10 @@ void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
auto statusChosen = _panel->selector()->customEmojiChosen(
|
auto statusChosen = _panel->selector()->customEmojiChosen(
|
||||||
) | rpl::map([=](ChatHelpers::FileChosen data) {
|
) | rpl::map([=](ChatHelpers::FileChosen data) {
|
||||||
return Chosen{
|
return Chosen{
|
||||||
.id = data.document->id,
|
.id = {
|
||||||
|
data.collectible ? DocumentId() : data.document->id,
|
||||||
|
data.collectible,
|
||||||
|
},
|
||||||
.until = data.options.scheduled,
|
.until = data.options.scheduled,
|
||||||
.animation = data.messageSendingFrom,
|
.animation = data.messageSendingFrom,
|
||||||
};
|
};
|
||||||
|
@ -264,8 +265,8 @@ void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
) | rpl::start_with_next([=](const Chosen &chosen) {
|
) | rpl::start_with_next([=](const Chosen &chosen) {
|
||||||
const auto owner = &controller->session().data();
|
const auto owner = &controller->session().data();
|
||||||
startAnimation(owner, body, chosen.id, chosen.animation);
|
startAnimation(owner, body, chosen.id, chosen.animation);
|
||||||
_someCustomChosen.fire({ { chosen.id }, chosen.until });
|
_someCustomChosen.fire({ chosen.id, chosen.until });
|
||||||
_panel->hideAnimated(); // todo collectibles
|
_panel->hideAnimated();
|
||||||
}, _panel->lifetime());
|
}, _panel->lifetime());
|
||||||
} else {
|
} else {
|
||||||
const auto weak = Ui::MakeWeak(_panel.get());
|
const auto weak = Ui::MakeWeak(_panel.get());
|
||||||
|
@ -276,8 +277,8 @@ void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
const auto owner = &controller->session().data();
|
const auto owner = &controller->session().data();
|
||||||
if (weak) {
|
if (weak) {
|
||||||
startAnimation(owner, body, chosen.id, chosen.animation);
|
startAnimation(owner, body, chosen.id, chosen.animation);
|
||||||
} // todo collectibles
|
}
|
||||||
owner->emojiStatuses().set({ chosen.id }, chosen.until);
|
owner->emojiStatuses().set(chosen.id, chosen.until);
|
||||||
};
|
};
|
||||||
|
|
||||||
rpl::merge(
|
rpl::merge(
|
||||||
|
@ -301,7 +302,7 @@ void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
|
|
||||||
bool EmojiStatusPanel::filter(
|
bool EmojiStatusPanel::filter(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
DocumentId chosenId) const {
|
EmojiStatusId chosenId) const {
|
||||||
if (_chooseFilter) {
|
if (_chooseFilter) {
|
||||||
return _chooseFilter(chosenId);
|
return _chooseFilter(chosenId);
|
||||||
} else if (chosenId && !controller->session().premium()) {
|
} else if (chosenId && !controller->session().premium()) {
|
||||||
|
@ -314,13 +315,16 @@ bool EmojiStatusPanel::filter(
|
||||||
void EmojiStatusPanel::startAnimation(
|
void EmojiStatusPanel::startAnimation(
|
||||||
not_null<Data::Session*> owner,
|
not_null<Data::Session*> owner,
|
||||||
not_null<Ui::RpWidget*> body,
|
not_null<Ui::RpWidget*> body,
|
||||||
DocumentId statusId,
|
EmojiStatusId statusId,
|
||||||
Ui::MessageSendingAnimationFrom from) {
|
Ui::MessageSendingAnimationFrom from) {
|
||||||
if (!_panelButton || !statusId) {
|
if (!_panelButton || !statusId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto documentId = statusId.collectible
|
||||||
|
? statusId.collectible->documentId
|
||||||
|
: statusId.documentId;
|
||||||
auto args = Ui::ReactionFlyAnimationArgs{
|
auto args = Ui::ReactionFlyAnimationArgs{
|
||||||
.id = { { statusId } },
|
.id = { { documentId } },
|
||||||
.flyIcon = from.frame,
|
.flyIcon = from.frame,
|
||||||
.flyFrom = body->mapFromGlobal(from.globalStartGeometry),
|
.flyFrom = body->mapFromGlobal(from.globalStartGeometry),
|
||||||
.forceFirstFrame = _backgroundEmojiMode,
|
.forceFirstFrame = _backgroundEmojiMode,
|
||||||
|
|
|
@ -40,7 +40,7 @@ public:
|
||||||
EmojiStatusPanel();
|
EmojiStatusPanel();
|
||||||
~EmojiStatusPanel();
|
~EmojiStatusPanel();
|
||||||
|
|
||||||
void setChooseFilter(Fn<bool(DocumentId)> filter);
|
void setChooseFilter(Fn<bool(EmojiStatusId)> filter);
|
||||||
|
|
||||||
void show(
|
void show(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
|
@ -56,6 +56,7 @@ public:
|
||||||
Fn<QColor()> customTextColor;
|
Fn<QColor()> customTextColor;
|
||||||
bool backgroundEmojiMode = false;
|
bool backgroundEmojiMode = false;
|
||||||
bool channelStatusMode = false;
|
bool channelStatusMode = false;
|
||||||
|
bool withCollectibles = false;
|
||||||
};
|
};
|
||||||
void show(Descriptor &&descriptor);
|
void show(Descriptor &&descriptor);
|
||||||
void repaint();
|
void repaint();
|
||||||
|
@ -74,17 +75,17 @@ private:
|
||||||
void create(const Descriptor &descriptor);
|
void create(const Descriptor &descriptor);
|
||||||
[[nodiscard]] bool filter(
|
[[nodiscard]] bool filter(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
DocumentId chosenId) const;
|
EmojiStatusId chosenId) const;
|
||||||
|
|
||||||
void startAnimation(
|
void startAnimation(
|
||||||
not_null<Data::Session*> owner,
|
not_null<Data::Session*> owner,
|
||||||
not_null<Ui::RpWidget*> body,
|
not_null<Ui::RpWidget*> body,
|
||||||
DocumentId statusId,
|
EmojiStatusId statusId,
|
||||||
Ui::MessageSendingAnimationFrom from);
|
Ui::MessageSendingAnimationFrom from);
|
||||||
|
|
||||||
base::unique_qptr<ChatHelpers::TabbedPanel> _panel;
|
base::unique_qptr<ChatHelpers::TabbedPanel> _panel;
|
||||||
Fn<QColor()> _customTextColor;
|
Fn<QColor()> _customTextColor;
|
||||||
Fn<bool(DocumentId)> _chooseFilter;
|
Fn<bool(EmojiStatusId)> _chooseFilter;
|
||||||
QPointer<QWidget> _panelButton;
|
QPointer<QWidget> _panelButton;
|
||||||
std::unique_ptr<Ui::EmojiFlyAnimation> _animation;
|
std::unique_ptr<Ui::EmojiFlyAnimation> _animation;
|
||||||
rpl::event_stream<CustomChosen> _someCustomChosen;
|
rpl::event_stream<CustomChosen> _someCustomChosen;
|
||||||
|
|
|
@ -243,7 +243,7 @@ EmojiSelector::Selector EmojiSelector::createEmojiList(
|
||||||
.show = _controller->uiShow(),
|
.show = _controller->uiShow(),
|
||||||
.mode = ChatHelpers::EmojiListMode::UserpicBuilder,
|
.mode = ChatHelpers::EmojiListMode::UserpicBuilder,
|
||||||
.paused = [=] { return true; },
|
.paused = [=] { return true; },
|
||||||
.customRecentList = _lastRecent,
|
.customRecentList = ChatHelpers::DocumentListToRecent(_lastRecent),
|
||||||
.customRecentFactory = [=](DocumentId id, Fn<void()> repaint) {
|
.customRecentFactory = [=](DocumentId id, Fn<void()> repaint) {
|
||||||
return manager->create(id, std::move(repaint), tag);
|
return manager->create(id, std::move(repaint), tag);
|
||||||
},
|
},
|
||||||
|
|
|
@ -618,6 +618,7 @@ storiesEmojiPan: EmojiPan(defaultEmojiPan) {
|
||||||
}
|
}
|
||||||
icons: ComposeIcons {
|
icons: ComposeIcons {
|
||||||
settings: icon {{ "emoji/emoji_settings", storiesComposeGrayIcon }};
|
settings: icon {{ "emoji/emoji_settings", storiesComposeGrayIcon }};
|
||||||
|
collectibles: icon {{ "menu/unique", storiesComposeGrayIcon }};
|
||||||
|
|
||||||
recent: icon {{ "emoji/emoji_recent", storiesComposeGrayIcon }};
|
recent: icon {{ "emoji/emoji_recent", storiesComposeGrayIcon }};
|
||||||
recentActive: icon {{ "emoji/emoji_recent", storiesComposeWhiteText }};
|
recentActive: icon {{ "emoji/emoji_recent", storiesComposeWhiteText }};
|
||||||
|
|
|
@ -137,7 +137,8 @@ DocumentData *Document::readFromStreamHelper(
|
||||||
|| info->setId == Data::Stickers::CloudRecentSetId
|
|| info->setId == Data::Stickers::CloudRecentSetId
|
||||||
|| info->setId == Data::Stickers::CloudRecentAttachedSetId
|
|| info->setId == Data::Stickers::CloudRecentAttachedSetId
|
||||||
|| info->setId == Data::Stickers::FavedSetId
|
|| info->setId == Data::Stickers::FavedSetId
|
||||||
|| info->setId == Data::Stickers::CustomSetId) {
|
|| info->setId == Data::Stickers::CustomSetId
|
||||||
|
|| info->setId == Data::Stickers::CollectibleSetId) {
|
||||||
typeOfSet = StickerSetTypeEmpty;
|
typeOfSet = StickerSetTypeEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2193,7 +2193,8 @@ void Account::writeInstalledStickers() {
|
||||||
writeStickerSets(_installedStickersKey, [](const Data::StickersSet &set) {
|
writeStickerSets(_installedStickersKey, [](const Data::StickersSet &set) {
|
||||||
if (set.id == Data::Stickers::CloudRecentSetId
|
if (set.id == Data::Stickers::CloudRecentSetId
|
||||||
|| set.id == Data::Stickers::FavedSetId
|
|| set.id == Data::Stickers::FavedSetId
|
||||||
|| set.id == Data::Stickers::CloudRecentAttachedSetId) {
|
|| set.id == Data::Stickers::CloudRecentAttachedSetId
|
||||||
|
|| set.id == Data::Stickers::CollectibleSetId) {
|
||||||
// separate files for them
|
// separate files for them
|
||||||
return StickerSetCheckResult::Skip;
|
return StickerSetCheckResult::Skip;
|
||||||
} else if (set.flags & SetFlag::Special) {
|
} else if (set.flags & SetFlag::Special) {
|
||||||
|
@ -2220,7 +2221,8 @@ void Account::writeFeaturedStickers() {
|
||||||
writeStickerSets(_featuredStickersKey, [](const Data::StickersSet &set) {
|
writeStickerSets(_featuredStickersKey, [](const Data::StickersSet &set) {
|
||||||
if (set.id == Data::Stickers::CloudRecentSetId
|
if (set.id == Data::Stickers::CloudRecentSetId
|
||||||
|| set.id == Data::Stickers::FavedSetId
|
|| set.id == Data::Stickers::FavedSetId
|
||||||
|| set.id == Data::Stickers::CloudRecentAttachedSetId) {
|
|| set.id == Data::Stickers::CloudRecentAttachedSetId
|
||||||
|
|| set.id == Data::Stickers::CollectibleSetId) {
|
||||||
// separate files for them
|
// separate files for them
|
||||||
return StickerSetCheckResult::Skip;
|
return StickerSetCheckResult::Skip;
|
||||||
} else if ((set.flags & SetFlag::Special)
|
} else if ((set.flags & SetFlag::Special)
|
||||||
|
|
Loading…
Add table
Reference in a new issue