mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Insert / Copy emoji from pack preview.
This commit is contained in:
parent
cae18b3320
commit
bc340d75c4
23 changed files with 282 additions and 215 deletions
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue