diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d6ea686ea2..843ec2acca 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4601,6 +4601,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_stickers_create_new" = "Create a New Sticker"; "lng_stickers_add_existing" = "Add an Existing Sticker"; +"lng_emoji_create_new" = "Create a New Emoji"; +"lng_emoji_add_existing" = "Add an Existing Emoji"; "lng_stickers_add_to_set" = "Add to Sticker Set"; "lng_stickers_already_in_set" = "This Sticker is already in the Set."; "lng_stickers_set_is_full" = "This Sticker Set is full."; diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 7200f6431b..23821d1689 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -402,6 +402,11 @@ private: void showAddMenu(QPoint globalPos); void startAddExistingStickerFlow(); void startCreateNewStickerFlow(); + void startAddExistingEmojiFlow(); + void startCreateNewEmojiFlow(); + [[nodiscard]] ChatHelpers::TabbedPanel *createPickerPanel( + ChatHelpers::TabbedSelector::Mode mode, + uint64 excludeSetId); void installDone(const MTPmessages_StickerSetInstallResult &result); @@ -2426,11 +2431,16 @@ void StickerSetBox::Inner::repaintItems(crl::time now) { } bool StickerSetBox::Inner::hasAddCell() const { - return _loaded - && _amSetCreator - && (setType() == Data::StickersType::Stickers) - && !_pack.isEmpty() - && (_pack.size() < Api::kStickersInOwnedSetMax); + if (!_loaded || !_amSetCreator || _pack.isEmpty()) { + return false; + } + const auto type = setType(); + if (type == Data::StickersType::Emoji) { + return (_pack.size() < Api::kEmojiInOwnedSetMax); + } else if (type == Data::StickersType::Stickers) { + return (_pack.size() < Api::kStickersInOwnedSetMax); + } + return false; } int StickerSetBox::Inner::totalCellsCount() const { @@ -2470,11 +2480,15 @@ void StickerSetBox::Inner::paintAddCell(QPainter &p) const { ltrRect.height()) : ltrRect; const auto center = rect::center(rect); + const auto bgRadius = isEmojiSet() + ? st::emojiSetAddCellBgRadius + : st::stickersAddCellBgRadius; + const auto plusSize = isEmojiSet() + ? st::emojiSetAddCellPlusSize + : st::stickersAddCellPlusSize; const auto inner = QRect( - center - QPoint( - st::stickersAddCellBgRadius, - st::stickersAddCellBgRadius), - Size(st::stickersAddCellBgRadius * 2)); + center - QPoint(bgRadius, bgRadius), + Size(bgRadius * 2)); auto hq = PainterHighQualityEnabler(p); const auto base = st::windowSubTextFg->c; @@ -2485,7 +2499,7 @@ void StickerSetBox::Inner::paintAddCell(QPainter &p) const { p.setBrush(anim::with_alpha(base, bgAlpha)); p.drawEllipse(inner); - const auto plusHalf = st::stickersAddCellPlusSize / 2; + const auto plusHalf = plusSize / 2; const auto thickness = st::stickersAddCellPlusThickness; const auto plusH = QRectF( center.x() - plusHalf, @@ -2510,14 +2524,25 @@ void StickerSetBox::Inner::showAddMenu(QPoint globalPos) { _menu = base::make_unique_q( this, st::popupMenuWithIcons); - _menu->addAction( - tr::lng_stickers_create_new(tr::now), - crl::guard(this, [=] { startCreateNewStickerFlow(); }), - &st::menuIconStickerCreate); - _menu->addAction( - tr::lng_stickers_add_existing(tr::now), - crl::guard(this, [=] { startAddExistingStickerFlow(); }), - &st::menuIconStickerAdd); + if (setType() == Data::StickersType::Emoji) { + _menu->addAction( + tr::lng_emoji_create_new(tr::now), + crl::guard(this, [=] { startCreateNewEmojiFlow(); }), + &st::menuIconStickerCreate); + _menu->addAction( + tr::lng_emoji_add_existing(tr::now), + crl::guard(this, [=] { startAddExistingEmojiFlow(); }), + &st::menuIconEmoji); + } else { + _menu->addAction( + tr::lng_stickers_create_new(tr::now), + crl::guard(this, [=] { startCreateNewStickerFlow(); }), + &st::menuIconStickerCreate); + _menu->addAction( + tr::lng_stickers_add_existing(tr::now), + crl::guard(this, [=] { startAddExistingStickerFlow(); }), + &st::menuIconStickerAdd); + } _menu->popup(globalPos); } @@ -2525,19 +2550,13 @@ void StickerSetBox::Inner::setOuterContainer(QPointer container) { _outerContainer = std::move(container); } -void StickerSetBox::Inner::startAddExistingStickerFlow() { - if (!hasAddCell() || !_outerContainer) { - return; +ChatHelpers::TabbedPanel *StickerSetBox::Inner::createPickerPanel( + ChatHelpers::TabbedSelector::Mode mode, + uint64 excludeSetId) { + if (!_outerContainer) { + return nullptr; } const auto container = _outerContainer.data(); - const auto identifier = StickerSetIdentifier{ - .id = _setId, - .accessHash = _setAccessHash, - .shortName = _setShortName, - }; - const auto session = _session; - const auto show = _show; - using Selector = ChatHelpers::TabbedSelector; _pickerPanel = base::make_unique_q( container, @@ -2548,8 +2567,8 @@ void StickerSetBox::Inner::startAddExistingStickerFlow() { .show = _show, .st = st::defaultComposeControls.tabbed, .level = Window::GifPauseReason::Layer, - .mode = Selector::Mode::StickersOnly, - .excludeStickerSetId = _setId, + .mode = mode, + .excludeStickerSetId = excludeSetId, }), }); const auto panel = _pickerPanel.get(); @@ -2561,30 +2580,6 @@ void StickerSetBox::Inner::startAddExistingStickerFlow() { panel->setShowAnimationOrigin(Ui::PanelAnimation::Origin::TopLeft); panel->hide(); - panel->selector()->fileChosen( - ) | rpl::on_next([=, this](const ChatHelpers::FileChosen &chosen) { - const auto document = chosen.document; - if (_pickerPanel) { - _pickerPanel->hideAnimated(); - } - const auto emoji = Api::StickerEmojiOrDefault(document); - Api::AddExistingStickerToSet( - session, - identifier, - document, - emoji, - crl::guard(this, [=, this](MTPmessages_StickerSet result) { - applySet(result); - show->showToast( - tr::lng_stickers_create_added(tr::now)); - }), - crl::guard(this, [=](QString err) { - show->showToast(err.isEmpty() - ? tr::lng_attach_failed(tr::now) - : err); - })); - }, panel->lifetime()); - const auto reposition = [=] { const auto size = container->size(); const auto margins = st::emojiPanMargins; @@ -2607,6 +2602,91 @@ void StickerSetBox::Inner::startAddExistingStickerFlow() { return base::EventFilterResult::Continue; }); reposition(); + return panel; +} + +void StickerSetBox::Inner::startAddExistingStickerFlow() { + if (!hasAddCell()) { + return; + } + const auto panel = createPickerPanel( + ChatHelpers::TabbedSelector::Mode::StickersOnly, + _setId); + if (!panel) { + return; + } + const auto identifier = StickerSetIdentifier{ + .id = _setId, + .accessHash = _setAccessHash, + .shortName = _setShortName, + }; + const auto session = _session; + const auto show = _show; + panel->selector()->fileChosen( + ) | rpl::on_next([=, this](const ChatHelpers::FileChosen &chosen) { + const auto document = chosen.document; + if (_pickerPanel) { + _pickerPanel->hideAnimated(); + } + const auto emoji = Api::StickerEmojiOrDefault(document); + Api::AddExistingStickerToSet( + session, + identifier, + document, + emoji, + crl::guard(this, [=, this](MTPmessages_StickerSet result) { + applySet(result); + show->showToast( + tr::lng_stickers_create_added(tr::now)); + }), + crl::guard(this, [=](QString err) { + show->showToast(err.isEmpty() + ? tr::lng_attach_failed(tr::now) + : err); + })); + }, panel->lifetime()); + panel->showAnimated(); +} + +void StickerSetBox::Inner::startAddExistingEmojiFlow() { + if (!hasAddCell()) { + return; + } + const auto panel = createPickerPanel( + ChatHelpers::TabbedSelector::Mode::CustomEmojiOnly, + 0); + if (!panel) { + return; + } + const auto identifier = StickerSetIdentifier{ + .id = _setId, + .accessHash = _setAccessHash, + .shortName = _setShortName, + }; + const auto session = _session; + const auto show = _show; + panel->selector()->customEmojiChosen( + ) | rpl::on_next([=, this](const ChatHelpers::FileChosen &chosen) { + const auto document = chosen.document; + if (_pickerPanel) { + _pickerPanel->hideAnimated(); + } + const auto emoji = Api::StickerEmojiOrDefault(document); + Api::AddExistingStickerToSet( + session, + identifier, + document, + emoji, + crl::guard(this, [=, this](MTPmessages_StickerSet result) { + applySet(result); + show->showToast(tr::lng_emoji_added(tr::now)); + }), + crl::guard(this, [=](QString err) { + show->showToast(err.isEmpty() + ? tr::lng_attach_failed(tr::now) + : err); + })); + }, panel->lifetime()); panel->showAnimated(); } @@ -2626,4 +2706,20 @@ void StickerSetBox::Inner::startCreateNewStickerFlow() { Api::OpenCreateStickerFlow(_show, identifier, onDone); } +void StickerSetBox::Inner::startCreateNewEmojiFlow() { + if (!hasAddCell()) { + return; + } + const auto identifier = StickerSetIdentifier{ + .id = _setId, + .accessHash = _setAccessHash, + .shortName = _setShortName, + }; + const auto onDone = crl::guard(this, [=, this]( + MTPmessages_StickerSet result) { + applySet(result); + }); + Api::OpenCreateEmojiFlow(_show, identifier, onDone); +} + StickerSetBox::Inner::~Inner() = default; diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 14fd2ca8ec..f45d815300 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -444,6 +444,8 @@ stickersRowDuration: 200; stickersAddCellPlusSize: 22px; stickersAddCellPlusThickness: 2px; stickersAddCellBgRadius: 28px; +emojiSetAddCellPlusSize: 13px; +emojiSetAddCellBgRadius: 16px; stickersEmojiPickerExpandedRadius: 20px; stickersEmojiPickerBg: emojiPanBg; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 31c45231ff..721a26f405 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -519,6 +519,7 @@ EmojiListWidget::EmojiListWidget( if (_mode != Mode::RecentReactions && _mode != Mode::BackgroundEmoji && _mode != Mode::ChannelStatus + && _mode != Mode::CustomOnly && !_onlyUnicodeEmoji) { setupSearch(); } @@ -2896,6 +2897,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { switch (_mode) { case Mode::Full: case Mode::UserpicBuilder: + case Mode::CustomOnly: Settings::ShowPremium(resolved, u"animated_emoji"_q); break; case Mode::FullReactions: diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index 4a4438d81e..d025f29885 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -85,6 +85,7 @@ enum class EmojiListMode { BackgroundEmoji, PeerTitle, MessageEffects, + CustomOnly, }; [[nodiscard]] std::vector DocumentListToRecent( diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 6d2d1637e3..fd046b776c 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -625,6 +625,8 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) { ? EmojiMode::FullReactions : _mode == Mode::RecentReactions ? EmojiMode::RecentReactions + : _mode == Mode::CustomEmojiOnly + ? EmojiMode::CustomOnly : _mode == Mode::PeerTitle ? EmojiMode::PeerTitle : EmojiMode::Full), diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index 8b77b15be8..53ed102d02 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -83,6 +83,7 @@ using InlineChosen = InlineBots::ResultSelected; enum class TabbedSelectorMode { Full, EmojiOnly, + CustomEmojiOnly, StickersOnly, MediaEditor, EmojiStatus,