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 "base/event_filter.h"
#include "boxes/premium_limits_box.h"
#include "boxes/premium_preview_box.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.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_user.h"
#include "data/data_premium_limits.h"
#include "data/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h"
#include "editor/photo_editor_layer_widget.h"
#include "history/history_drag_area.h"
@ -502,14 +504,22 @@ void EditCaptionBox::setupEmojiPanel() {
_emojiPanel->hide();
_emojiPanel->selector()->setCurrentPeer(_historyItem->history()->peer);
_emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](Selector::EmojiChosen data) {
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji);
}, lifetime());
_emojiPanel->selector()->customEmojiChosen(
) | rpl::start_with_next([=](Selector::FileChosen data) {
Data::InsertCustomEmoji(_field.get(), data.document);
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
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());
_emojiPanel->selector()->showPromoForPremiumEmoji();
const auto filterCallback = [=](not_null<QEvent*> event) {
emojiFilterForGeometry(event);

View file

@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/mime_type.h"
#include "base/event_filter.h"
#include "boxes/premium_limits_box.h"
#include "boxes/premium_preview_box.h"
#include "ui/boxes/confirm_box.h"
#include "ui/effects/animations.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_user.h"
#include "data/data_premium_limits.h"
#include "data/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h"
#include "media/clip/media_clip_reader.h"
#include "api/api_common.h"
@ -744,14 +746,23 @@ void SendFilesBox::setupEmojiPanel() {
_emojiPanel->selector()->setAllowEmojiWithoutPremium(
_allowEmojiWithoutPremium);
_emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](Selector::EmojiChosen data) {
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(_caption->textCursor(), data.emoji);
}, lifetime());
_emojiPanel->selector()->customEmojiChosen(
) | rpl::start_with_next([=](Selector::FileChosen data) {
Data::InsertCustomEmoji(_caption.data(), data.document);
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
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());
_emojiPanel->selector()->showPromoForPremiumEmoji();
const auto filterCallback = [=](not_null<QEvent*> event) {
emojiFilterForGeometry(event);

View file

@ -154,6 +154,25 @@ void ValidatePremiumStarFg(QImage &image) {
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
StickerPremiumMark::StickerPremiumMark(not_null<Main::Session*> session) {
@ -264,6 +283,9 @@ private:
void visibleTopBottomUpdated(int visibleTop, int visibleBottom) override;
[[nodiscard]] Ui::MessageSendingAnimationFrom messageSentAnimationInfo(
int index,
not_null<DocumentData*> document) const;
[[nodiscard]] QSize boundingBoxSize() const;
void paintSticker(
@ -291,7 +313,10 @@ private:
void gotSet(const MTPmessages_StickerSet &set);
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();
@ -920,74 +945,113 @@ void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
}
_previewTimer.cancel();
const auto index = stickerFromGlobalPos(e->globalPos());
if (index < 0
|| index >= _pack.size()
|| setType() != Data::StickersType::Stickers) {
if (index < 0 || index >= _pack.size()) {
return;
}
send(_pack[index], {});
chosen(index, _pack[index], {});
}
void StickerSetBox::Inner::send(
void StickerSetBox::Inner::chosen(
int index,
not_null<DocumentData*> sticker,
Api::SendOptions options) {
const auto controller = _controller;
const auto animation = options.scheduled
? Ui::MessageSendingAnimationFrom()
: messageSentAnimationInfo(index, sticker);
Ui::PostponeCall(controller, [=] {
if (controller->content()->sendExistingDocument(sticker, options)) {
controller->window().hideSettingsAndLayer();
}
controller->stickerOrEmojiChosen({
.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) {
const auto index = stickerFromGlobalPos(e->globalPos());
if (index < 0
|| index >= _pack.size()
|| setType() != Data::StickersType::Stickers) {
return;
}
const auto type = _controller->content()->sendMenuType();
if (type == SendMenu::Type::Disabled) {
|| setType() == Data::StickersType::Masks) {
return;
}
_previewTimer.cancel();
_menu = base::make_unique_q<Ui::PopupMenu>(
this,
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 sendSelected = [=](Api::SendOptions options) {
send(document, options);
};
SendMenu::FillSendMenu(
_menu.get(),
type,
SendMenu::DefaultSilentCallback(sendSelected),
SendMenu::DefaultScheduleCallback(this, type, sendSelected));
const auto controller = _controller;
const auto toggleFavedSticker = [=] {
Api::ToggleFavedSticker(
controller,
document,
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0));
};
const auto isFaved = document->owner().stickers().isFaved(document);
_menu->addAction(
(isFaved
? tr::lng_faved_stickers_remove
: tr::lng_faved_stickers_add)(tr::now),
toggleFavedSticker,
(isFaved
? &st::menuIconUnfave
: &st::menuIconFave));
_menu->popup(QCursor::pos());
const auto controller = _controller;
const auto toggleFavedSticker = [=] {
Api::ToggleFavedSticker(
controller,
document,
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0));
};
const auto isFaved = document->owner().stickers().isFaved(document);
_menu->addAction(
(isFaved
? tr::lng_faved_stickers_remove
: tr::lng_faved_stickers_add)(tr::now),
toggleFavedSticker,
(isFaved
? &st::menuIconUnfave
: &st::menuIconFave));
}
if (_menu->empty()) {
_menu = nullptr;
} else {
_menu->popup(QCursor::pos());
}
}
void StickerSetBox::Inner::updateSelected() {
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) {
@ -1092,9 +1156,10 @@ uint64 StickerSetBox::Inner::setId() const {
QSize StickerSetBox::Inner::boundingBoxSize() const {
if (isEmojiSet()) {
const auto factor = style::DevicePixelRatio();
const auto large = Ui::Emoji::GetSizeLarge() / factor;
return QSize(large, large);
using namespace Data;
const auto size = FrameSizeFromTag(CustomEmojiSizeTag::Large)
/ style::DevicePixelRatio();
return { size, size };
}
return QSize(
_singleSize.width() - st::roundRadiusSmall * 2,

View file

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

View file

@ -85,8 +85,6 @@ class EmojiListWidget
, public Ui::AbstractTooltipShower {
public:
using Mode = EmojiListMode;
using EmojiChosen = TabbedSelector::EmojiChosen;
using FileChosen = TabbedSelector::FileChosen;
EmojiListWidget(
QWidget *parent,
@ -115,7 +113,6 @@ public:
[[nodiscard]] rpl::producer<EmojiChosen> chosen() const;
[[nodiscard]] rpl::producer<FileChosen> customChosen() const;
[[nodiscard]] rpl::producer<FileChosen> premiumChosen() const;
[[nodiscard]] rpl::producer<> jumpedToPremium() const;
void provideRecent(const std::vector<DocumentId> &customRecentList);
@ -382,7 +379,6 @@ private:
rpl::event_stream<EmojiChosen> _chosen;
rpl::event_stream<FileChosen> _customChosen;
rpl::event_stream<FileChosen> _premiumChosen;
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 "chat_helpers/stickers_lottie.h"
#include "chat_helpers/message_field.h" // PrepareMentionTag.
#include "chat_helpers/tabbed_selector.h" // ChatHelpers::FileChosen.
#include "mainwindow.h"
#include "apiwrap.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;
}
} else if (!_mrows->empty()) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -47,6 +47,10 @@ struct EmojiPan;
namespace ChatHelpers {
class EmojiListWidget;
class StickersListWidget;
class GifsListWidget;
enum class SelectorTab {
Emoji,
Stickers,
@ -54,27 +58,27 @@ enum class SelectorTab {
Masks,
};
class EmojiListWidget;
class StickersListWidget;
class GifsListWidget;
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;
class TabbedSelector : public Ui::RpWidget {
public:
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 {
Full,
EmojiOnly,
@ -98,7 +102,6 @@ public:
rpl::producer<EmojiChosen> emojiChosen() const;
rpl::producer<FileChosen> customEmojiChosen() const;
rpl::producer<FileChosen> premiumEmojiChosen() const;
rpl::producer<FileChosen> fileChosen() const;
rpl::producer<PhotoChosen> photoChosen() const;
rpl::producer<InlineChosen> inlineResultChosen() const;
@ -113,7 +116,6 @@ public:
void setRoundRadius(int radius);
void refreshStickers();
void setCurrentPeer(PeerData *peer);
void showPromoForPremiumEmoji();
void provideRecentEmoji(const std::vector<DocumentId> &customRecentList);
void hideFinished();

View file

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

View file

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

View file

@ -1072,7 +1072,7 @@ rpl::producer<PhotoChosen> ComposeControls::photoChosen() const {
}
auto ComposeControls::inlineResultChosen() const
->rpl::producer<ChatHelpers::TabbedSelector::InlineChosen> {
-> rpl::producer<InlineChosen> {
return _inlineResultChosen.events();
}
@ -1525,11 +1525,7 @@ void ComposeControls::initAutocomplete() {
//_saveDraftStart = crl::now();
//saveDraft();
//saveCloudDraft(); // won't be needed if SendInlineBotResult will clear the cloud draft
_fileChosen.fire(FileChosen{
.document = data.sticker,
.options = data.options,
.messageSendingFrom = base::take(data.messageSendingFrom),
});
_fileChosen.fire(std::move(data));
}, _autocomplete->lifetime());
_autocomplete->choosingProcesses(
@ -1857,28 +1853,33 @@ void ComposeControls::initTabbedSelector() {
return base::EventFilterResult::Continue;
});
using EmojiChosen = ChatHelpers::TabbedSelector::EmojiChosen;
selector->emojiChosen(
) | rpl::start_with_next([=](EmojiChosen data) {
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji);
}, wrap->lifetime());
using FileChosen = ChatHelpers::TabbedSelector::FileChosen;
selector->customEmojiChosen(
) | rpl::start_with_next([=](FileChosen data) {
Data::InsertCustomEmoji(_field, data.document);
}, wrap->lifetime());
selector->premiumEmojiChosen(
) | rpl::start_with_next([=](FileChosen data) {
if (_unavailableEmojiPasted) {
_unavailableEmojiPasted(data.document);
rpl::merge(
selector->fileChosen(),
selector->customEmojiChosen(),
_window->stickerOrEmojiChosen()
) | rpl::start_with_next([=](ChatHelpers::FileChosen &&data) {
if (const auto info = data.document->sticker()
; info && info->setType == Data::StickersType::Emoji) {
if (data.document->isPremiumEmoji()
&& !session().premium()
&& (!_history
|| !Data::AllowEmojiWithoutPremium(_history->peer))) {
if (_unavailableEmojiPasted) {
_unavailableEmojiPasted(data.document);
}
} else {
Data::InsertCustomEmoji(_field, data.document);
}
} else {
_fileChosen.fire(std::move(data));
}
}, wrap->lifetime());
selector->fileChosen(
) | rpl::start_to_stream(_fileChosen, wrap->lifetime());
selector->photoChosen(
) | 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/effects/animations.h"
#include "ui/widgets/input_fields.h"
#include "chat_helpers/tabbed_selector.h"
class History;
class FieldAutocomplete;
@ -28,6 +27,8 @@ enum class Type;
namespace ChatHelpers {
class TabbedPanel;
class TabbedSelector;
struct FileChosen;
struct PhotoChosen;
} // namespace ChatHelpers
namespace Data {
@ -43,6 +44,7 @@ class ItemBase;
class Widget;
} // namespace Layout
class Result;
struct ResultSelected;
} // namespace InlineBots
namespace Ui {
@ -78,9 +80,9 @@ class WebpageProcessor;
class ComposeControls final {
public:
using FileChosen = ChatHelpers::TabbedSelector::FileChosen;
using PhotoChosen = ChatHelpers::TabbedSelector::PhotoChosen;
using InlineChosen = ChatHelpers::TabbedSelector::InlineChosen;
using FileChosen = ChatHelpers::FileChosen;
using PhotoChosen = ChatHelpers::PhotoChosen;
using InlineChosen = InlineBots::ResultSelected;
using MessageToEdit = Controls::MessageToEdit;
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.h"
#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/chat_style.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/text/format_values.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/special_buttons.h"
#include "ui/ui_utility.h"
@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_sending.h"
#include "apiwrap.h"
#include "ui/boxes/confirm_box.h"
#include "chat_helpers/tabbed_selector.h"
#include "boxes/delete_messages_box.h"
#include "boxes/edit_caption_box.h"
#include "boxes/send_files_box.h"
@ -573,22 +574,21 @@ void RepliesWidget::setupComposeControls() {
using Selector = ChatHelpers::TabbedSelector;
_composeControls->fileChosen(
) | rpl::start_with_next([=](Selector::FileChosen chosen) {
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
controller()->hideLayer(anim::type::normal);
controller()->sendingAnimation().appendSending(
chosen.messageSendingFrom);
sendExistingDocument(
chosen.document,
chosen.options,
chosen.messageSendingFrom.localId);
data.messageSendingFrom);
const auto localId = data.messageSendingFrom.localId;
sendExistingDocument(data.document, data.options, localId);
}, lifetime());
_composeControls->photoChosen(
) | rpl::start_with_next([=](Selector::PhotoChosen chosen) {
) | rpl::start_with_next([=](ChatHelpers::PhotoChosen chosen) {
sendExistingPhoto(chosen.photo, chosen.options);
}, lifetime());
_composeControls->inlineResultChosen(
) | rpl::start_with_next([=](Selector::InlineChosen chosen) {
) | rpl::start_with_next([=](ChatHelpers::InlineChosen chosen) {
controller()->sendingAnimation().appendSending(
chosen.messageSendingFrom);
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/qt/qt_key_modifiers.h"
#include "core/file_utilities.h"
#include "chat_helpers/tabbed_selector.h"
#include "main/main_session.h"
#include "data/data_chat_participant_status.h"
#include "data/data_session.h"
@ -263,17 +264,18 @@ void ScheduledWidget::setupComposeControls() {
using Selector = ChatHelpers::TabbedSelector;
_composeControls->fileChosen(
) | rpl::start_with_next([=](Selector::FileChosen chosen) {
sendExistingDocument(chosen.document);
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
controller()->hideLayer(anim::type::normal);
sendExistingDocument(data.document);
}, lifetime());
_composeControls->photoChosen(
) | rpl::start_with_next([=](Selector::PhotoChosen chosen) {
) | rpl::start_with_next([=](ChatHelpers::PhotoChosen chosen) {
sendExistingPhoto(chosen.photo);
}, lifetime());
_composeControls->inlineResultChosen(
) | rpl::start_with_next([=](Selector::InlineChosen chosen) {
) | rpl::start_with_next([=](ChatHelpers::InlineChosen chosen) {
sendInlineResult(chosen.result, chosen.bot);
}, lifetime());

View file

@ -778,10 +778,8 @@ void Selector::createList(not_null<Window::SessionController*> controller) {
})
).data();
rpl::merge(
_list->customChosen(),
_list->premiumChosen()
) | rpl::start_with_next([=](TabbedSelector::FileChosen data) {
_list->customChosen(
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
const auto id = DocumentId{ data.document->id };
const auto i = defaultReactionIds.find(id);
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/text/format_values.h"
#include "base/unixtime.h"
#include "boxes/premium_preview_box.h"
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "main/main_session.h"
@ -246,7 +247,6 @@ void EmojiStatusPanel::create(
st::emojiPanMinHeight / 2,
st::emojiPanMinHeight);
_panel->hide();
_panel->selector()->setAllowEmojiWithoutPremium(false);
struct Chosen {
DocumentId id = 0;
@ -260,7 +260,7 @@ void EmojiStatusPanel::create(
}, _panel->lifetime());
auto statusChosen = _panel->selector()->customEmojiChosen(
) | rpl::map([=](Selector::FileChosen data) {
) | rpl::map([=](ChatHelpers::FileChosen data) {
return Chosen{
.id = data.document->id,
.until = data.options.scheduled,
@ -269,7 +269,7 @@ void EmojiStatusPanel::create(
});
auto emojiChosen = _panel->selector()->emojiChosen(
) | rpl::map([=](Selector::EmojiChosen data) {
) | rpl::map([=](ChatHelpers::EmojiChosen data) {
return Chosen{ .animation = data.messageSendingFrom };
});
@ -285,7 +285,9 @@ void EmojiStatusPanel::create(
std::move(statusChosen),
std::move(emojiChosen)
) | 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();
controller->show(Box(PickUntilBox, [=](TimeId seconds) {
set({ chosen.id, base::unixtime::now() + seconds });
@ -295,8 +297,6 @@ void EmojiStatusPanel::create(
_panel->hideAnimated();
}
}, _panel->lifetime());
_panel->selector()->showPromoForPremiumEmoji();
}
void EmojiStatusPanel::startAnimation(

View file

@ -1611,6 +1611,15 @@ rpl::producer<int> SessionController::connectingBottomSkipValue() const {
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(
object_ptr<Ui::BoxContent> content,
Ui::LayerOptions options,

View file

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