Insert / Copy emoji from pack preview.

This commit is contained in:
John Preston 2022-09-14 14:45:23 +04:00
parent cae18b3320
commit bc340d75c4
23 changed files with 282 additions and 215 deletions

View file

@ -12,6 +12,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 "boxes/premium_limits_box.h" #include "boxes/premium_limits_box.h"
#include "boxes/premium_preview_box.h"
#include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h" #include "chat_helpers/message_field.h"
#include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_panel.h"
@ -25,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_premium_limits.h" #include "data/data_premium_limits.h"
#include "data/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h" #include "data/stickers/data_custom_emoji.h"
#include "editor/photo_editor_layer_widget.h" #include "editor/photo_editor_layer_widget.h"
#include "history/history_drag_area.h" #include "history/history_drag_area.h"
@ -502,14 +504,22 @@ void EditCaptionBox::setupEmojiPanel() {
_emojiPanel->hide(); _emojiPanel->hide();
_emojiPanel->selector()->setCurrentPeer(_historyItem->history()->peer); _emojiPanel->selector()->setCurrentPeer(_historyItem->history()->peer);
_emojiPanel->selector()->emojiChosen( _emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](Selector::EmojiChosen data) { ) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji); Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji);
}, lifetime()); }, lifetime());
_emojiPanel->selector()->customEmojiChosen( _emojiPanel->selector()->customEmojiChosen(
) | rpl::start_with_next([=](Selector::FileChosen data) { ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
Data::InsertCustomEmoji(_field.get(), data.document); const auto info = data.document->sticker();
if (info
&& info->setType == Data::StickersType::Emoji
&& !_controller->session().premium()) {
ShowPremiumPreviewBox(
_controller,
PremiumPreview::AnimatedEmoji);
} else {
Data::InsertCustomEmoji(_field.get(), data.document);
}
}, lifetime()); }, lifetime());
_emojiPanel->selector()->showPromoForPremiumEmoji();
const auto filterCallback = [=](not_null<QEvent*> event) { const auto filterCallback = [=](not_null<QEvent*> event) {
emojiFilterForGeometry(event); emojiFilterForGeometry(event);

View file

@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/mime_type.h" #include "core/mime_type.h"
#include "base/event_filter.h" #include "base/event_filter.h"
#include "boxes/premium_limits_box.h" #include "boxes/premium_limits_box.h"
#include "boxes/premium_preview_box.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/effects/scroll_content_shadow.h" #include "ui/effects/scroll_content_shadow.h"
@ -48,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_premium_limits.h" #include "data/data_premium_limits.h"
#include "data/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h" #include "data/stickers/data_custom_emoji.h"
#include "media/clip/media_clip_reader.h" #include "media/clip/media_clip_reader.h"
#include "api/api_common.h" #include "api/api_common.h"
@ -744,14 +746,23 @@ void SendFilesBox::setupEmojiPanel() {
_emojiPanel->selector()->setAllowEmojiWithoutPremium( _emojiPanel->selector()->setAllowEmojiWithoutPremium(
_allowEmojiWithoutPremium); _allowEmojiWithoutPremium);
_emojiPanel->selector()->emojiChosen( _emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](Selector::EmojiChosen data) { ) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(_caption->textCursor(), data.emoji); Ui::InsertEmojiAtCursor(_caption->textCursor(), data.emoji);
}, lifetime()); }, lifetime());
_emojiPanel->selector()->customEmojiChosen( _emojiPanel->selector()->customEmojiChosen(
) | rpl::start_with_next([=](Selector::FileChosen data) { ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
Data::InsertCustomEmoji(_caption.data(), data.document); const auto info = data.document->sticker();
if (info
&& info->setType == Data::StickersType::Emoji
&& !_controller->session().premium()
&& !_allowEmojiWithoutPremium) {
ShowPremiumPreviewBox(
_controller,
PremiumPreview::AnimatedEmoji);
} else {
Data::InsertCustomEmoji(_caption.data(), data.document);
}
}, lifetime()); }, lifetime());
_emojiPanel->selector()->showPromoForPremiumEmoji();
const auto filterCallback = [=](not_null<QEvent*> event) { const auto filterCallback = [=](not_null<QEvent*> event) {
emojiFilterForGeometry(event); emojiFilterForGeometry(event);

View file

@ -154,6 +154,25 @@ void ValidatePremiumStarFg(QImage &image) {
star.render(&p, outer); star.render(&p, outer);
} }
[[nodiscard]] TextForMimeData PrepareTextFromEmoji(
not_null<DocumentData*> document) {
const auto info = document->sticker();
const auto text = info ? info->alt : QString();
return {
.expanded = text,
.rich = {
text,
{
EntityInText(
EntityType::CustomEmoji,
0,
text.size(),
Data::SerializeCustomEmojiId(document))
},
},
};
}
} // namespace } // namespace
StickerPremiumMark::StickerPremiumMark(not_null<Main::Session*> session) { StickerPremiumMark::StickerPremiumMark(not_null<Main::Session*> session) {
@ -264,6 +283,9 @@ private:
void visibleTopBottomUpdated(int visibleTop, int visibleBottom) override; void visibleTopBottomUpdated(int visibleTop, int visibleBottom) override;
[[nodiscard]] Ui::MessageSendingAnimationFrom messageSentAnimationInfo(
int index,
not_null<DocumentData*> document) const;
[[nodiscard]] QSize boundingBoxSize() const; [[nodiscard]] QSize boundingBoxSize() const;
void paintSticker( void paintSticker(
@ -291,7 +313,10 @@ private:
void gotSet(const MTPmessages_StickerSet &set); void gotSet(const MTPmessages_StickerSet &set);
void installDone(const MTPmessages_StickerSetInstallResult &result); void installDone(const MTPmessages_StickerSetInstallResult &result);
void send(not_null<DocumentData*> sticker, Api::SendOptions options); void chosen(
int index,
not_null<DocumentData*> sticker,
Api::SendOptions options);
not_null<Lottie::MultiPlayer*> getLottiePlayer(); not_null<Lottie::MultiPlayer*> getLottiePlayer();
@ -920,74 +945,113 @@ void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
} }
_previewTimer.cancel(); _previewTimer.cancel();
const auto index = stickerFromGlobalPos(e->globalPos()); const auto index = stickerFromGlobalPos(e->globalPos());
if (index < 0 if (index < 0 || index >= _pack.size()) {
|| index >= _pack.size()
|| setType() != Data::StickersType::Stickers) {
return; return;
} }
send(_pack[index], {}); chosen(index, _pack[index], {});
} }
void StickerSetBox::Inner::send( void StickerSetBox::Inner::chosen(
int index,
not_null<DocumentData*> sticker, not_null<DocumentData*> sticker,
Api::SendOptions options) { Api::SendOptions options) {
const auto controller = _controller; const auto controller = _controller;
const auto animation = options.scheduled
? Ui::MessageSendingAnimationFrom()
: messageSentAnimationInfo(index, sticker);
Ui::PostponeCall(controller, [=] { Ui::PostponeCall(controller, [=] {
if (controller->content()->sendExistingDocument(sticker, options)) { controller->stickerOrEmojiChosen({
controller->window().hideSettingsAndLayer(); .document = sticker,
} .options = options,
.messageSendingFrom = animation,
});
}); });
} }
auto StickerSetBox::Inner::messageSentAnimationInfo(
int index,
not_null<DocumentData*> document) const
-> Ui::MessageSendingAnimationFrom {
if (index < 0 || index >= _pack.size() || _pack[index] != document) {
return {};
}
const auto row = index / _perRow;
const auto column = index % _perRow;
const auto left = _padding.left() + column * _singleSize.width();
const auto top = _padding.top() + row * _singleSize.height();
const auto rect = QRect(QPoint(left, top), _singleSize);
const auto size = ChatHelpers::ComputeStickerSize(
document,
boundingBoxSize());
const auto innerPos = QPoint(
(rect.width() - size.width()) / 2,
(rect.height() - size.height()) / 2);
return {
.type = Ui::MessageSendingAnimationFrom::Type::Sticker,
.localId = _controller->session().data().nextLocalMessageId(),
.globalStartGeometry = mapToGlobal(
QRect(rect.topLeft() + innerPos, size)),
};
}
void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) { void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) {
const auto index = stickerFromGlobalPos(e->globalPos()); const auto index = stickerFromGlobalPos(e->globalPos());
if (index < 0 if (index < 0
|| index >= _pack.size() || index >= _pack.size()
|| setType() != Data::StickersType::Stickers) { || setType() == Data::StickersType::Masks) {
return;
}
const auto type = _controller->content()->sendMenuType();
if (type == SendMenu::Type::Disabled) {
return; return;
} }
_previewTimer.cancel(); _previewTimer.cancel();
_menu = base::make_unique_q<Ui::PopupMenu>( _menu = base::make_unique_q<Ui::PopupMenu>(
this, this,
st::popupMenuWithIcons); st::popupMenuWithIcons);
const auto type = _controller->content()->sendMenuType();
if (setType() == Data::StickersType::Emoji) {
if (const auto t = PrepareTextFromEmoji(_pack[index]); !t.empty()) {
_menu->addAction(tr::lng_mediaview_copy(tr::now), [=] {
if (auto data = TextUtilities::MimeDataFromText(t)) {
QGuiApplication::clipboard()->setMimeData(data.release());
}
}, &st::menuIconCopy);
}
} else if (type != SendMenu::Type::Disabled) {
const auto document = _pack[index];
const auto sendSelected = [=](Api::SendOptions options) {
chosen(index, document, options);
};
SendMenu::FillSendMenu(
_menu.get(),
type,
SendMenu::DefaultSilentCallback(sendSelected),
SendMenu::DefaultScheduleCallback(this, type, sendSelected));
const auto document = _pack[index]; const auto controller = _controller;
const auto sendSelected = [=](Api::SendOptions options) { const auto toggleFavedSticker = [=] {
send(document, options); Api::ToggleFavedSticker(
}; controller,
SendMenu::FillSendMenu( document,
_menu.get(), Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0));
type, };
SendMenu::DefaultSilentCallback(sendSelected), const auto isFaved = document->owner().stickers().isFaved(document);
SendMenu::DefaultScheduleCallback(this, type, sendSelected)); _menu->addAction(
(isFaved
const auto controller = _controller; ? tr::lng_faved_stickers_remove
const auto toggleFavedSticker = [=] { : tr::lng_faved_stickers_add)(tr::now),
Api::ToggleFavedSticker( toggleFavedSticker,
controller, (isFaved
document, ? &st::menuIconUnfave
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0)); : &st::menuIconFave));
}; }
const auto isFaved = document->owner().stickers().isFaved(document); if (_menu->empty()) {
_menu->addAction( _menu = nullptr;
(isFaved } else {
? tr::lng_faved_stickers_remove _menu->popup(QCursor::pos());
: tr::lng_faved_stickers_add)(tr::now), }
toggleFavedSticker,
(isFaved
? &st::menuIconUnfave
: &st::menuIconFave));
_menu->popup(QCursor::pos());
} }
void StickerSetBox::Inner::updateSelected() { void StickerSetBox::Inner::updateSelected() {
auto selected = stickerFromGlobalPos(QCursor::pos()); auto selected = stickerFromGlobalPos(QCursor::pos());
setSelected(setType() != Data::StickersType::Stickers ? -1 : selected); setSelected(setType() == Data::StickersType::Masks ? -1 : selected);
} }
void StickerSetBox::Inner::setSelected(int selected) { void StickerSetBox::Inner::setSelected(int selected) {
@ -1092,9 +1156,10 @@ uint64 StickerSetBox::Inner::setId() const {
QSize StickerSetBox::Inner::boundingBoxSize() const { QSize StickerSetBox::Inner::boundingBoxSize() const {
if (isEmojiSet()) { if (isEmojiSet()) {
const auto factor = style::DevicePixelRatio(); using namespace Data;
const auto large = Ui::Emoji::GetSizeLarge() / factor; const auto size = FrameSizeFromTag(CustomEmojiSizeTag::Large)
return QSize(large, large); / style::DevicePixelRatio();
return { size, size };
} }
return QSize( return QSize(
_singleSize.width() - st::roundRadiusSmall * 2, _singleSize.width() - st::roundRadiusSmall * 2,

View file

@ -46,8 +46,6 @@ constexpr auto kAppearDuration = 0.3;
using Core::RecentEmojiId; using Core::RecentEmojiId;
using Core::RecentEmojiDocument; using Core::RecentEmojiDocument;
using EmojiChosen = TabbedSelector::EmojiChosen;
using FileChosen = TabbedSelector::FileChosen;
} // namespace } // namespace
@ -504,10 +502,6 @@ rpl::producer<FileChosen> EmojiListWidget::customChosen() const {
return _customChosen.events(); return _customChosen.events();
} }
rpl::producer<FileChosen> EmojiListWidget::premiumChosen() const {
return _premiumChosen.events();
}
rpl::producer<> EmojiListWidget::jumpedToPremium() const { rpl::producer<> EmojiListWidget::jumpedToPremium() const {
return _jumpedToPremium.events(); return _jumpedToPremium.events();
} }
@ -1294,14 +1288,9 @@ void EmojiListWidget::selectEmoji(EmojiChosen data) {
void EmojiListWidget::selectCustom(FileChosen data) { void EmojiListWidget::selectCustom(FileChosen data) {
const auto document = data.document; const auto document = data.document;
if (document->isPremiumEmoji() const auto skip = (document->isPremiumEmoji() && !session().premium());
&& !document->session().premium() if (!skip && _mode == Mode::Full) {
&& !_allowWithoutPremium) { auto &settings = Core::App().settings();
_premiumChosen.fire(std::move(data));
return;
}
auto &settings = Core::App().settings();
if (_mode == Mode::Full) {
settings.incrementRecentEmoji({ RecentEmojiDocument{ settings.incrementRecentEmoji({ RecentEmojiDocument{
document->id, document->id,
document->session().isTestMode(), document->session().isTestMode(),

View file

@ -85,8 +85,6 @@ class EmojiListWidget
, public Ui::AbstractTooltipShower { , public Ui::AbstractTooltipShower {
public: public:
using Mode = EmojiListMode; using Mode = EmojiListMode;
using EmojiChosen = TabbedSelector::EmojiChosen;
using FileChosen = TabbedSelector::FileChosen;
EmojiListWidget( EmojiListWidget(
QWidget *parent, QWidget *parent,
@ -115,7 +113,6 @@ public:
[[nodiscard]] rpl::producer<EmojiChosen> chosen() const; [[nodiscard]] rpl::producer<EmojiChosen> chosen() const;
[[nodiscard]] rpl::producer<FileChosen> customChosen() const; [[nodiscard]] rpl::producer<FileChosen> customChosen() const;
[[nodiscard]] rpl::producer<FileChosen> premiumChosen() const;
[[nodiscard]] rpl::producer<> jumpedToPremium() const; [[nodiscard]] rpl::producer<> jumpedToPremium() const;
void provideRecent(const std::vector<DocumentId> &customRecentList); void provideRecent(const std::vector<DocumentId> &customRecentList);
@ -382,7 +379,6 @@ private:
rpl::event_stream<EmojiChosen> _chosen; rpl::event_stream<EmojiChosen> _chosen;
rpl::event_stream<FileChosen> _customChosen; rpl::event_stream<FileChosen> _customChosen;
rpl::event_stream<FileChosen> _premiumChosen;
rpl::event_stream<> _jumpedToPremium; rpl::event_stream<> _jumpedToPremium;
}; };

View file

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "menu/menu_send.h" // SendMenu::FillSendMenu #include "menu/menu_send.h" // SendMenu::FillSendMenu
#include "chat_helpers/stickers_lottie.h" #include "chat_helpers/stickers_lottie.h"
#include "chat_helpers/message_field.h" // PrepareMentionTag. #include "chat_helpers/message_field.h" // PrepareMentionTag.
#include "chat_helpers/tabbed_selector.h" // ChatHelpers::FileChosen.
#include "mainwindow.h" #include "mainwindow.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "api/api_chat_participants.h" #include "api/api_chat_participants.h"
@ -1127,7 +1128,7 @@ bool FieldAutocomplete::Inner::chooseAtIndex(
}; };
}; };
_stickerChosen.fire({ document, options, method, from() }); _stickerChosen.fire({ document, options, from() });
return true; return true;
} }
} else if (!_mrows->empty()) { } else if (!_mrows->empty()) {

View file

@ -38,6 +38,9 @@ namespace SendMenu {
enum class Type; enum class Type;
} // namespace SendMenu } // namespace SendMenu
namespace ChatHelpers {
struct FileChosen;
} // namespace ChatHelpers
class FieldAutocomplete final : public Ui::RpWidget { class FieldAutocomplete final : public Ui::RpWidget {
public: public:
@ -73,22 +76,17 @@ public:
}; };
struct MentionChosen { struct MentionChosen {
not_null<UserData*> user; not_null<UserData*> user;
ChooseMethod method; ChooseMethod method = ChooseMethod::ByEnter;
}; };
struct HashtagChosen { struct HashtagChosen {
QString hashtag; QString hashtag;
ChooseMethod method; ChooseMethod method = ChooseMethod::ByEnter;
}; };
struct BotCommandChosen { struct BotCommandChosen {
QString command; QString command;
ChooseMethod method; ChooseMethod method = ChooseMethod::ByEnter;
};
struct StickerChosen {
not_null<DocumentData*> sticker;
Api::SendOptions options;
ChooseMethod method;
Ui::MessageSendingAnimationFrom messageSendingFrom;
}; };
using StickerChosen = ChatHelpers::FileChosen;
enum class Type { enum class Type {
Mentions, Mentions,
Hashtags, Hashtags,

View file

@ -220,12 +220,11 @@ GifsListWidget::GifsListWidget(
_mosaic.setRightSkip(st::inlineResultsSkip); _mosaic.setRightSkip(st::inlineResultsSkip);
} }
rpl::producer<TabbedSelector::FileChosen> GifsListWidget::fileChosen() const { rpl::producer<FileChosen> GifsListWidget::fileChosen() const {
return _fileChosen.events(); return _fileChosen.events();
} }
auto GifsListWidget::photoChosen() const rpl::producer<PhotoChosen> GifsListWidget::photoChosen() const {
-> rpl::producer<TabbedSelector::PhotoChosen> {
return _photoChosen.events(); return _photoChosen.events();
} }

View file

@ -49,15 +49,13 @@ class GifsListWidget
: public TabbedSelector::Inner : public TabbedSelector::Inner
, public InlineBots::Layout::Context { , public InlineBots::Layout::Context {
public: public:
using InlineChosen = TabbedSelector::InlineChosen;
GifsListWidget( GifsListWidget(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
Window::GifPauseReason level); Window::GifPauseReason level);
rpl::producer<TabbedSelector::FileChosen> fileChosen() const; rpl::producer<FileChosen> fileChosen() const;
rpl::producer<TabbedSelector::PhotoChosen> photoChosen() const; rpl::producer<PhotoChosen> photoChosen() const;
rpl::producer<InlineChosen> inlineResultChosen() const; rpl::producer<InlineChosen> inlineResultChosen() const;
void refreshRecent() override; void refreshRecent() override;
@ -190,8 +188,8 @@ private:
QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; QString _inlineQuery, _inlineNextQuery, _inlineNextOffset;
mtpRequestId _inlineRequestId = 0; mtpRequestId _inlineRequestId = 0;
rpl::event_stream<TabbedSelector::FileChosen> _fileChosen; rpl::event_stream<FileChosen> _fileChosen;
rpl::event_stream<TabbedSelector::PhotoChosen> _photoChosen; rpl::event_stream<PhotoChosen> _photoChosen;
rpl::event_stream<InlineChosen> _inlineResultChosen; rpl::event_stream<InlineChosen> _inlineResultChosen;
rpl::event_stream<> _cancelled; rpl::event_stream<> _cancelled;

View file

@ -232,7 +232,7 @@ StickersListWidget::StickersListWidget(
}, lifetime()); }, lifetime());
} }
rpl::producer<TabbedSelector::FileChosen> StickersListWidget::chosen() const { rpl::producer<FileChosen> StickersListWidget::chosen() const {
return _chosen.events(); return _chosen.events();
} }

View file

@ -65,7 +65,7 @@ public:
Window::GifPauseReason level, Window::GifPauseReason level,
bool masks = false); bool masks = false);
rpl::producer<TabbedSelector::FileChosen> chosen() const; rpl::producer<FileChosen> chosen() const;
rpl::producer<> scrollUpdated() const; rpl::producer<> scrollUpdated() const;
rpl::producer<TabbedSelector::Action> choosingUpdated() const; rpl::producer<TabbedSelector::Action> choosingUpdated() const;
@ -389,7 +389,7 @@ private:
QString _searchQuery, _searchNextQuery; QString _searchQuery, _searchNextQuery;
mtpRequestId _searchRequestId = 0; mtpRequestId _searchRequestId = 0;
rpl::event_stream<TabbedSelector::FileChosen> _chosen; rpl::event_stream<FileChosen> _chosen;
rpl::event_stream<> _scrollUpdated; rpl::event_stream<> _scrollUpdated;
rpl::event_stream<TabbedSelector::Action> _choosingUpdated; rpl::event_stream<TabbedSelector::Action> _choosingUpdated;

View file

@ -492,21 +492,16 @@ bool TabbedSelector::hasMasksTab() const {
return _hasMasksTab; return _hasMasksTab;
} }
auto TabbedSelector::emojiChosen() const -> rpl::producer<EmojiChosen> { rpl::producer<EmojiChosen> TabbedSelector::emojiChosen() const {
return emoji()->chosen(); return emoji()->chosen();
} }
auto TabbedSelector::customEmojiChosen() const -> rpl::producer<FileChosen> { rpl::producer<FileChosen> TabbedSelector::customEmojiChosen() const {
return emoji()->customChosen(); return emoji()->customChosen();
} }
auto TabbedSelector::premiumEmojiChosen() const rpl::producer<FileChosen> TabbedSelector::fileChosen() const {
-> rpl::producer<FileChosen> { auto never = rpl::never<FileChosen>(
return emoji()->premiumChosen();
}
auto TabbedSelector::fileChosen() const -> rpl::producer<FileChosen> {
auto never = rpl::never<TabbedSelector::FileChosen>(
) | rpl::type_erased(); ) | rpl::type_erased();
return rpl::merge( return rpl::merge(
hasStickersTab() ? stickers()->chosen() : never, hasStickersTab() ? stickers()->chosen() : never,
@ -514,8 +509,7 @@ auto TabbedSelector::fileChosen() const -> rpl::producer<FileChosen> {
hasMasksTab() ? masks()->chosen() : never); hasMasksTab() ? masks()->chosen() : never);
} }
auto TabbedSelector::photoChosen() const rpl::producer<PhotoChosen> TabbedSelector::photoChosen() const {
-> rpl::producer<TabbedSelector::PhotoChosen>{
return hasGifsTab() ? gifs()->photoChosen() : nullptr; return hasGifsTab() ? gifs()->photoChosen() : nullptr;
} }
@ -865,13 +859,6 @@ void TabbedSelector::setCurrentPeer(PeerData *peer) {
peer && Data::AllowEmojiWithoutPremium(peer)); peer && Data::AllowEmojiWithoutPremium(peer));
} }
void TabbedSelector::showPromoForPremiumEmoji() {
premiumEmojiChosen(
) | rpl::start_with_next([=] {
ShowPremiumPreviewBox(_controller, PremiumPreview::AnimatedEmoji);
}, lifetime());
}
void TabbedSelector::provideRecentEmoji( void TabbedSelector::provideRecentEmoji(
const std::vector<DocumentId> &customRecentList) { const std::vector<DocumentId> &customRecentList) {
for (const auto &tab : _tabs) { for (const auto &tab : _tabs) {

View file

@ -47,6 +47,10 @@ struct EmojiPan;
namespace ChatHelpers { namespace ChatHelpers {
class EmojiListWidget;
class StickersListWidget;
class GifsListWidget;
enum class SelectorTab { enum class SelectorTab {
Emoji, Emoji,
Stickers, Stickers,
@ -54,27 +58,27 @@ enum class SelectorTab {
Masks, Masks,
}; };
class EmojiListWidget; struct FileChosen {
class StickersListWidget; not_null<DocumentData*> document;
class GifsListWidget; Api::SendOptions options;
Ui::MessageSendingAnimationFrom messageSendingFrom;
};
struct PhotoChosen {
not_null<PhotoData*> photo;
Api::SendOptions options;
};
struct EmojiChosen {
EmojiPtr emoji;
Ui::MessageSendingAnimationFrom messageSendingFrom;
};
using InlineChosen = InlineBots::ResultSelected;
class TabbedSelector : public Ui::RpWidget { class TabbedSelector : public Ui::RpWidget {
public: public:
static constexpr auto kPickCustomTimeId = -1; static constexpr auto kPickCustomTimeId = -1;
struct FileChosen {
not_null<DocumentData*> document;
Api::SendOptions options;
Ui::MessageSendingAnimationFrom messageSendingFrom;
};
struct PhotoChosen {
not_null<PhotoData*> photo;
Api::SendOptions options;
};
struct EmojiChosen {
EmojiPtr emoji;
Ui::MessageSendingAnimationFrom messageSendingFrom;
};
using InlineChosen = InlineBots::ResultSelected;
enum class Mode { enum class Mode {
Full, Full,
EmojiOnly, EmojiOnly,
@ -98,7 +102,6 @@ public:
rpl::producer<EmojiChosen> emojiChosen() const; rpl::producer<EmojiChosen> emojiChosen() const;
rpl::producer<FileChosen> customEmojiChosen() const; rpl::producer<FileChosen> customEmojiChosen() const;
rpl::producer<FileChosen> premiumEmojiChosen() const;
rpl::producer<FileChosen> fileChosen() const; rpl::producer<FileChosen> fileChosen() const;
rpl::producer<PhotoChosen> photoChosen() const; rpl::producer<PhotoChosen> photoChosen() const;
rpl::producer<InlineChosen> inlineResultChosen() const; rpl::producer<InlineChosen> inlineResultChosen() const;
@ -113,7 +116,6 @@ public:
void setRoundRadius(int radius); void setRoundRadius(int radius);
void refreshStickers(); void refreshStickers();
void setCurrentPeer(PeerData *peer); void setCurrentPeer(PeerData *peer);
void showPromoForPremiumEmoji();
void provideRecentEmoji(const std::vector<DocumentId> &customRecentList); void provideRecentEmoji(const std::vector<DocumentId> &customRecentList);
void hideFinished(); void hideFinished();

View file

@ -37,7 +37,7 @@ StickersPanelController::StickersPanelController(
auto StickersPanelController::stickerChosen() const auto StickersPanelController::stickerChosen() const
-> rpl::producer<not_null<DocumentData*>> { -> rpl::producer<not_null<DocumentData*>> {
return _stickersPanel->selector()->fileChosen( return _stickersPanel->selector()->fileChosen(
) | rpl::map([](const ChatHelpers::TabbedSelector::FileChosen &data) { ) | rpl::map([](const ChatHelpers::FileChosen &data) {
return data.document; return data.document;
}); });
} }

View file

@ -423,14 +423,6 @@ HistoryWidget::HistoryWidget(
insertHashtagOrBotCommand(data.command, data.method); insertHashtagOrBotCommand(data.command, data.method);
}, lifetime()); }, lifetime());
_fieldAutocomplete->stickerChosen(
) | rpl::start_with_next([=](FieldAutocomplete::StickerChosen data) {
controller->sendingAnimation().appendSending(
data.messageSendingFrom);
const auto localId = data.messageSendingFrom.localId;
sendExistingDocument(data.sticker, data.options, localId);
}, lifetime());
_fieldAutocomplete->setModerateKeyActivateCallback([=](int key) { _fieldAutocomplete->setModerateKeyActivateCallback([=](int key) {
const auto context = [=](FullMsgId itemId) { const auto context = [=](FullMsgId itemId) {
return _list->prepareClickContext(Qt::LeftButton, itemId); return _list->prepareClickContext(Qt::LeftButton, itemId);
@ -1067,48 +1059,48 @@ void HistoryWidget::initTabbedSelector() {
selector->emojiChosen( selector->emojiChosen(
) | rpl::filter([=] { ) | rpl::filter([=] {
return !isHidden() && !_field->isHidden(); return !isHidden() && !_field->isHidden();
}) | rpl::start_with_next([=](Selector::EmojiChosen data) { }) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji); Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji);
}, lifetime()); }, lifetime());
selector->customEmojiChosen( rpl::merge(
) | rpl::filter([=] { selector->fileChosen() | filter,
return !isHidden() && !_field->isHidden(); _fieldAutocomplete->stickerChosen(),
}) | rpl::start_with_next([=](Selector::FileChosen data) { selector->customEmojiChosen() | filter,
Data::InsertCustomEmoji(_field.data(), data.document); controller()->stickerOrEmojiChosen() | filter
}, lifetime()); ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
controller()->hideLayer(anim::type::normal);
selector->premiumEmojiChosen( if (const auto info = data.document->sticker()
) | rpl::filter([=] { ; info && info->setType == Data::StickersType::Emoji) {
return !isHidden() && !_field->isHidden(); if (data.document->isPremiumEmoji()
}) | rpl::start_with_next([=](Selector::FileChosen data) { && !session().premium()
showPremiumToast(data.document); && (!_peer || !Data::AllowEmojiWithoutPremium(_peer))) {
}, lifetime()); showPremiumToast(data.document);
} else if (!_field->isHidden()) {
selector->fileChosen( Data::InsertCustomEmoji(_field.data(), data.document);
) | filter | rpl::start_with_next([=](Selector::FileChosen data) { }
controller()->sendingAnimation().appendSending( } else {
data.messageSendingFrom); controller()->sendingAnimation().appendSending(
sendExistingDocument( data.messageSendingFrom);
data.document, const auto localId = data.messageSendingFrom.localId;
data.options, sendExistingDocument(data.document, data.options, localId);
data.messageSendingFrom.localId); }
}, lifetime()); }, lifetime());
selector->photoChosen( selector->photoChosen(
) | filter | rpl::start_with_next([=](Selector::PhotoChosen data) { ) | filter | rpl::start_with_next([=](ChatHelpers::PhotoChosen data) {
sendExistingPhoto(data.photo, data.options); sendExistingPhoto(data.photo, data.options);
}, lifetime()); }, lifetime());
selector->inlineResultChosen( selector->inlineResultChosen(
) | filter | rpl::filter([=](const Selector::InlineChosen &data) { ) | filter | rpl::filter([=](const ChatHelpers::InlineChosen &data) {
if (!data.recipientOverride) { if (!data.recipientOverride) {
return true; return true;
} else if (data.recipientOverride != _peer) { } else if (data.recipientOverride != _peer) {
showHistory(data.recipientOverride->id, ShowAtTheEndMsgId); showHistory(data.recipientOverride->id, ShowAtTheEndMsgId);
} }
return (data.recipientOverride == _peer); return (data.recipientOverride == _peer);
}) | rpl::start_with_next([=](Selector::InlineChosen data) { }) | rpl::start_with_next([=](ChatHelpers::InlineChosen data) {
sendInlineResult(data); sendInlineResult(data);
}, lifetime()); }, lifetime());

View file

@ -1072,7 +1072,7 @@ rpl::producer<PhotoChosen> ComposeControls::photoChosen() const {
} }
auto ComposeControls::inlineResultChosen() const auto ComposeControls::inlineResultChosen() const
->rpl::producer<ChatHelpers::TabbedSelector::InlineChosen> { -> rpl::producer<InlineChosen> {
return _inlineResultChosen.events(); return _inlineResultChosen.events();
} }
@ -1525,11 +1525,7 @@ void ComposeControls::initAutocomplete() {
//_saveDraftStart = crl::now(); //_saveDraftStart = crl::now();
//saveDraft(); //saveDraft();
//saveCloudDraft(); // won't be needed if SendInlineBotResult will clear the cloud draft //saveCloudDraft(); // won't be needed if SendInlineBotResult will clear the cloud draft
_fileChosen.fire(FileChosen{ _fileChosen.fire(std::move(data));
.document = data.sticker,
.options = data.options,
.messageSendingFrom = base::take(data.messageSendingFrom),
});
}, _autocomplete->lifetime()); }, _autocomplete->lifetime());
_autocomplete->choosingProcesses( _autocomplete->choosingProcesses(
@ -1857,28 +1853,33 @@ void ComposeControls::initTabbedSelector() {
return base::EventFilterResult::Continue; return base::EventFilterResult::Continue;
}); });
using EmojiChosen = ChatHelpers::TabbedSelector::EmojiChosen;
selector->emojiChosen( selector->emojiChosen(
) | rpl::start_with_next([=](EmojiChosen data) { ) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji); Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji);
}, wrap->lifetime()); }, wrap->lifetime());
using FileChosen = ChatHelpers::TabbedSelector::FileChosen; rpl::merge(
selector->customEmojiChosen( selector->fileChosen(),
) | rpl::start_with_next([=](FileChosen data) { selector->customEmojiChosen(),
Data::InsertCustomEmoji(_field, data.document); _window->stickerOrEmojiChosen()
}, wrap->lifetime()); ) | rpl::start_with_next([=](ChatHelpers::FileChosen &&data) {
if (const auto info = data.document->sticker()
selector->premiumEmojiChosen( ; info && info->setType == Data::StickersType::Emoji) {
) | rpl::start_with_next([=](FileChosen data) { if (data.document->isPremiumEmoji()
if (_unavailableEmojiPasted) { && !session().premium()
_unavailableEmojiPasted(data.document); && (!_history
|| !Data::AllowEmojiWithoutPremium(_history->peer))) {
if (_unavailableEmojiPasted) {
_unavailableEmojiPasted(data.document);
}
} else {
Data::InsertCustomEmoji(_field, data.document);
}
} else {
_fileChosen.fire(std::move(data));
} }
}, wrap->lifetime()); }, wrap->lifetime());
selector->fileChosen(
) | rpl::start_to_stream(_fileChosen, wrap->lifetime());
selector->photoChosen( selector->photoChosen(
) | rpl::start_to_stream(_photoChosen, wrap->lifetime()); ) | rpl::start_to_stream(_photoChosen, wrap->lifetime());

View file

@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
#include "chat_helpers/tabbed_selector.h"
class History; class History;
class FieldAutocomplete; class FieldAutocomplete;
@ -28,6 +27,8 @@ enum class Type;
namespace ChatHelpers { namespace ChatHelpers {
class TabbedPanel; class TabbedPanel;
class TabbedSelector; class TabbedSelector;
struct FileChosen;
struct PhotoChosen;
} // namespace ChatHelpers } // namespace ChatHelpers
namespace Data { namespace Data {
@ -43,6 +44,7 @@ class ItemBase;
class Widget; class Widget;
} // namespace Layout } // namespace Layout
class Result; class Result;
struct ResultSelected;
} // namespace InlineBots } // namespace InlineBots
namespace Ui { namespace Ui {
@ -78,9 +80,9 @@ class WebpageProcessor;
class ComposeControls final { class ComposeControls final {
public: public:
using FileChosen = ChatHelpers::TabbedSelector::FileChosen; using FileChosen = ChatHelpers::FileChosen;
using PhotoChosen = ChatHelpers::TabbedSelector::PhotoChosen; using PhotoChosen = ChatHelpers::PhotoChosen;
using InlineChosen = ChatHelpers::TabbedSelector::InlineChosen; using InlineChosen = InlineBots::ResultSelected;
using MessageToEdit = Controls::MessageToEdit; using MessageToEdit = Controls::MessageToEdit;
using VoiceToSend = Controls::VoiceToSend; using VoiceToSend = Controls::VoiceToSend;

View file

@ -18,6 +18,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_components.h" #include "history/history_item_components.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "menu/menu_send.h" // SendMenu::Type. #include "menu/menu_send.h" // SendMenu::Type.
#include "ui/chat/attach/attach_prepare.h"
#include "ui/chat/attach/attach_send_files_way.h"
#include "ui/chat/pinned_bar.h" #include "ui/chat/pinned_bar.h"
#include "ui/chat/chat_style.h" #include "ui/chat/chat_style.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
@ -28,8 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/text/format_values.h" #include "ui/text/format_values.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/chat/attach/attach_prepare.h"
#include "ui/chat/attach/attach_send_files_way.h"
#include "ui/effects/message_sending_animation_controller.h" #include "ui/effects/message_sending_animation_controller.h"
#include "ui/special_buttons.h" #include "ui/special_buttons.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_sending.h" #include "api/api_sending.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "chat_helpers/tabbed_selector.h"
#include "boxes/delete_messages_box.h" #include "boxes/delete_messages_box.h"
#include "boxes/edit_caption_box.h" #include "boxes/edit_caption_box.h"
#include "boxes/send_files_box.h" #include "boxes/send_files_box.h"
@ -573,22 +574,21 @@ void RepliesWidget::setupComposeControls() {
using Selector = ChatHelpers::TabbedSelector; using Selector = ChatHelpers::TabbedSelector;
_composeControls->fileChosen( _composeControls->fileChosen(
) | rpl::start_with_next([=](Selector::FileChosen chosen) { ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
controller()->hideLayer(anim::type::normal);
controller()->sendingAnimation().appendSending( controller()->sendingAnimation().appendSending(
chosen.messageSendingFrom); data.messageSendingFrom);
sendExistingDocument( const auto localId = data.messageSendingFrom.localId;
chosen.document, sendExistingDocument(data.document, data.options, localId);
chosen.options,
chosen.messageSendingFrom.localId);
}, lifetime()); }, lifetime());
_composeControls->photoChosen( _composeControls->photoChosen(
) | rpl::start_with_next([=](Selector::PhotoChosen chosen) { ) | rpl::start_with_next([=](ChatHelpers::PhotoChosen chosen) {
sendExistingPhoto(chosen.photo, chosen.options); sendExistingPhoto(chosen.photo, chosen.options);
}, lifetime()); }, lifetime());
_composeControls->inlineResultChosen( _composeControls->inlineResultChosen(
) | rpl::start_with_next([=](Selector::InlineChosen chosen) { ) | rpl::start_with_next([=](ChatHelpers::InlineChosen chosen) {
controller()->sendingAnimation().appendSending( controller()->sendingAnimation().appendSending(
chosen.messageSendingFrom); chosen.messageSendingFrom);
const auto localId = chosen.messageSendingFrom.localId; const auto localId = chosen.messageSendingFrom.localId;

View file

@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/call_delayed.h" #include "base/call_delayed.h"
#include "base/qt/qt_key_modifiers.h" #include "base/qt/qt_key_modifiers.h"
#include "core/file_utilities.h" #include "core/file_utilities.h"
#include "chat_helpers/tabbed_selector.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "data/data_chat_participant_status.h" #include "data/data_chat_participant_status.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -263,17 +264,18 @@ void ScheduledWidget::setupComposeControls() {
using Selector = ChatHelpers::TabbedSelector; using Selector = ChatHelpers::TabbedSelector;
_composeControls->fileChosen( _composeControls->fileChosen(
) | rpl::start_with_next([=](Selector::FileChosen chosen) { ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
sendExistingDocument(chosen.document); controller()->hideLayer(anim::type::normal);
sendExistingDocument(data.document);
}, lifetime()); }, lifetime());
_composeControls->photoChosen( _composeControls->photoChosen(
) | rpl::start_with_next([=](Selector::PhotoChosen chosen) { ) | rpl::start_with_next([=](ChatHelpers::PhotoChosen chosen) {
sendExistingPhoto(chosen.photo); sendExistingPhoto(chosen.photo);
}, lifetime()); }, lifetime());
_composeControls->inlineResultChosen( _composeControls->inlineResultChosen(
) | rpl::start_with_next([=](Selector::InlineChosen chosen) { ) | rpl::start_with_next([=](ChatHelpers::InlineChosen chosen) {
sendInlineResult(chosen.result, chosen.bot); sendInlineResult(chosen.result, chosen.bot);
}, lifetime()); }, lifetime());

View file

@ -778,10 +778,8 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
}) })
).data(); ).data();
rpl::merge( _list->customChosen(
_list->customChosen(), ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
_list->premiumChosen()
) | rpl::start_with_next([=](TabbedSelector::FileChosen data) {
const auto id = DocumentId{ data.document->id }; const auto id = DocumentId{ data.document->id };
const auto i = defaultReactionIds.find(id); const auto i = defaultReactionIds.find(id);
const auto reactionId = (i != end(defaultReactionIds)) const auto reactionId = (i != end(defaultReactionIds))

View file

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/time_picker_box.h" #include "ui/boxes/time_picker_box.h"
#include "ui/text/format_values.h" #include "ui/text/format_values.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "boxes/premium_preview_box.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "window/window_controller.h" #include "window/window_controller.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -246,7 +247,6 @@ void EmojiStatusPanel::create(
st::emojiPanMinHeight / 2, st::emojiPanMinHeight / 2,
st::emojiPanMinHeight); st::emojiPanMinHeight);
_panel->hide(); _panel->hide();
_panel->selector()->setAllowEmojiWithoutPremium(false);
struct Chosen { struct Chosen {
DocumentId id = 0; DocumentId id = 0;
@ -260,7 +260,7 @@ void EmojiStatusPanel::create(
}, _panel->lifetime()); }, _panel->lifetime());
auto statusChosen = _panel->selector()->customEmojiChosen( auto statusChosen = _panel->selector()->customEmojiChosen(
) | rpl::map([=](Selector::FileChosen data) { ) | rpl::map([=](ChatHelpers::FileChosen data) {
return Chosen{ return Chosen{
.id = data.document->id, .id = data.document->id,
.until = data.options.scheduled, .until = data.options.scheduled,
@ -269,7 +269,7 @@ void EmojiStatusPanel::create(
}); });
auto emojiChosen = _panel->selector()->emojiChosen( auto emojiChosen = _panel->selector()->emojiChosen(
) | rpl::map([=](Selector::EmojiChosen data) { ) | rpl::map([=](ChatHelpers::EmojiChosen data) {
return Chosen{ .animation = data.messageSendingFrom }; return Chosen{ .animation = data.messageSendingFrom };
}); });
@ -285,7 +285,9 @@ void EmojiStatusPanel::create(
std::move(statusChosen), std::move(statusChosen),
std::move(emojiChosen) std::move(emojiChosen)
) | rpl::start_with_next([=](const Chosen chosen) { ) | rpl::start_with_next([=](const Chosen chosen) {
if (chosen.until == Selector::kPickCustomTimeId) { if (chosen.id && !controller->session().premium()) {
ShowPremiumPreviewBox(controller, PremiumPreview::EmojiStatus);
} else if (chosen.until == Selector::kPickCustomTimeId) {
_panel->hideAnimated(); _panel->hideAnimated();
controller->show(Box(PickUntilBox, [=](TimeId seconds) { controller->show(Box(PickUntilBox, [=](TimeId seconds) {
set({ chosen.id, base::unixtime::now() + seconds }); set({ chosen.id, base::unixtime::now() + seconds });
@ -295,8 +297,6 @@ void EmojiStatusPanel::create(
_panel->hideAnimated(); _panel->hideAnimated();
} }
}, _panel->lifetime()); }, _panel->lifetime());
_panel->selector()->showPromoForPremiumEmoji();
} }
void EmojiStatusPanel::startAnimation( void EmojiStatusPanel::startAnimation(

View file

@ -1611,6 +1611,15 @@ rpl::producer<int> SessionController::connectingBottomSkipValue() const {
return _connectingBottomSkip.value(); return _connectingBottomSkip.value();
} }
void SessionController::stickerOrEmojiChosen(FileChosen chosen) {
_stickerOrEmojiChosen.fire(std::move(chosen));
}
auto SessionController::stickerOrEmojiChosen() const
-> rpl::producer<FileChosen> {
return _stickerOrEmojiChosen.events();
}
QPointer<Ui::BoxContent> SessionController::show( QPointer<Ui::BoxContent> SessionController::show(
object_ptr<Ui::BoxContent> content, object_ptr<Ui::BoxContent> content,
Ui::LayerOptions options, Ui::LayerOptions options,

View file

@ -32,6 +32,7 @@ enum class WindowLayout;
namespace ChatHelpers { namespace ChatHelpers {
class TabbedSelector; class TabbedSelector;
class EmojiInteractions; class EmojiInteractions;
struct FileChosen;
} // namespace ChatHelpers } // namespace ChatHelpers
namespace Main { namespace Main {
@ -317,6 +318,10 @@ public:
void setConnectingBottomSkip(int skip); void setConnectingBottomSkip(int skip);
rpl::producer<int> connectingBottomSkipValue() const; rpl::producer<int> connectingBottomSkipValue() const;
using FileChosen = ChatHelpers::FileChosen;
void stickerOrEmojiChosen(FileChosen chosen);
[[nodiscard]] rpl::producer<FileChosen> stickerOrEmojiChosen() const;
QPointer<Ui::BoxContent> show( QPointer<Ui::BoxContent> show(
object_ptr<Ui::BoxContent> content, object_ptr<Ui::BoxContent> content,
Ui::LayerOptions options = Ui::LayerOption::KeepOther, Ui::LayerOptions options = Ui::LayerOption::KeepOther,
@ -585,6 +590,8 @@ private:
rpl::variable<int> _connectingBottomSkip; rpl::variable<int> _connectingBottomSkip;
rpl::event_stream<ChatHelpers::FileChosen> _stickerOrEmojiChosen;
PeerData *_showEditPeer = nullptr; PeerData *_showEditPeer = nullptr;
rpl::variable<Data::Folder*> _openedFolder; rpl::variable<Data::Folder*> _openedFolder;