mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Implement effects paywalls.
This commit is contained in:
parent
d102d256a9
commit
732b67ca04
13 changed files with 241 additions and 47 deletions
|
@ -564,6 +564,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_effect_add_title" = "Add an animated effect";
|
||||
"lng_effect_stickers_title" = "Effects from stickers";
|
||||
"lng_effect_send" = "Send with Effect";
|
||||
"lng_effect_none" = "No effects found.";
|
||||
"lng_effect_premium" = "Subscribe to {link} to add this animated effect.";
|
||||
"lng_effect_premium_link" = "Telegram Premium";
|
||||
|
||||
"lng_languages" = "Languages";
|
||||
"lng_languages_none" = "No languages found.";
|
||||
|
|
|
@ -73,7 +73,9 @@ using Data::StickersSet;
|
|||
using Data::StickersPack;
|
||||
using SetFlag = Data::StickersSetFlag;
|
||||
|
||||
[[nodiscard]] std::optional<QColor> ComputeImageColor(const QImage &frame) {
|
||||
[[nodiscard]] std::optional<QColor> ComputeImageColor(
|
||||
const style::icon &lockIcon,
|
||||
const QImage &frame) {
|
||||
if (frame.isNull()
|
||||
|| frame.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
return {};
|
||||
|
@ -83,7 +85,7 @@ using SetFlag = Data::StickersSetFlag;
|
|||
auto sb = int64();
|
||||
auto sa = int64();
|
||||
const auto factor = frame.devicePixelRatio();
|
||||
const auto size = st::stickersPremiumLock.size() * factor;
|
||||
const auto size = lockIcon.size() * factor;
|
||||
const auto width = std::min(frame.width(), size.width());
|
||||
const auto height = std::min(frame.height(), size.height());
|
||||
const auto skipx = (frame.width() - width) / 2;
|
||||
|
@ -110,22 +112,30 @@ using SetFlag = Data::StickersSetFlag;
|
|||
|
||||
}
|
||||
|
||||
[[nodiscard]] QColor ComputeLockColor(const QImage &frame) {
|
||||
return ComputeImageColor(frame).value_or(st::windowSubTextFg->c);
|
||||
[[nodiscard]] QColor ComputeLockColor(
|
||||
const style::icon &lockIcon,
|
||||
const QImage &frame) {
|
||||
return ComputeImageColor(
|
||||
lockIcon,
|
||||
frame
|
||||
).value_or(st::windowSubTextFg->c);
|
||||
}
|
||||
|
||||
void ValidatePremiumLockBg(QImage &image, const QImage &frame) {
|
||||
void ValidatePremiumLockBg(
|
||||
const style::icon &lockIcon,
|
||||
QImage &image,
|
||||
const QImage &frame) {
|
||||
if (!image.isNull()) {
|
||||
return;
|
||||
}
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
const auto size = st::stickersPremiumLock.size();
|
||||
const auto size = lockIcon.size();
|
||||
image = QImage(
|
||||
size * factor,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
image.setDevicePixelRatio(factor);
|
||||
auto p = QPainter(&image);
|
||||
const auto color = ComputeLockColor(frame);
|
||||
const auto color = ComputeLockColor(lockIcon, frame);
|
||||
p.fillRect(
|
||||
QRect(QPoint(), size),
|
||||
anim::color(color, st::windowSubTextFg, kGrayLockOpacity));
|
||||
|
@ -134,12 +144,12 @@ void ValidatePremiumLockBg(QImage &image, const QImage &frame) {
|
|||
image = Images::Circle(std::move(image));
|
||||
}
|
||||
|
||||
void ValidatePremiumStarFg(QImage &image) {
|
||||
void ValidatePremiumStarFg(const style::icon &lockIcon, QImage &image) {
|
||||
if (!image.isNull()) {
|
||||
return;
|
||||
}
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
const auto size = st::stickersPremiumLock.size();
|
||||
const auto size = lockIcon.size();
|
||||
image = QImage(
|
||||
size * factor,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
|
@ -176,7 +186,10 @@ void ValidatePremiumStarFg(QImage &image) {
|
|||
|
||||
} // namespace
|
||||
|
||||
StickerPremiumMark::StickerPremiumMark(not_null<Main::Session*> session) {
|
||||
StickerPremiumMark::StickerPremiumMark(
|
||||
not_null<Main::Session*> session,
|
||||
const style::icon &lockIcon)
|
||||
: _lockIcon(lockIcon) {
|
||||
style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
_lockGray = QImage();
|
||||
|
@ -202,16 +215,14 @@ void StickerPremiumMark::paint(
|
|||
const auto factor = style::DevicePixelRatio();
|
||||
const auto radius = st::roundRadiusSmall;
|
||||
const auto point = position + QPoint(
|
||||
(_premium
|
||||
? (singleSize.width() - (bg.width() / factor) - radius)
|
||||
: (singleSize.width() - (bg.width() / factor)) / 2),
|
||||
(singleSize.width() - (bg.width() / factor) - radius),
|
||||
singleSize.height() - (bg.height() / factor) - radius);
|
||||
p.drawImage(point, bg);
|
||||
if (_premium) {
|
||||
validateStar();
|
||||
p.drawImage(point, _star);
|
||||
} else {
|
||||
st::stickersPremiumLock.paint(p, point, outerWidth);
|
||||
_lockIcon.paint(p, point, outerWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,11 +230,11 @@ void StickerPremiumMark::validateLock(
|
|||
const QImage &frame,
|
||||
QImage &backCache) {
|
||||
auto &image = frame.isNull() ? _lockGray : backCache;
|
||||
ValidatePremiumLockBg(image, frame);
|
||||
ValidatePremiumLockBg(_lockIcon, image, frame);
|
||||
}
|
||||
|
||||
void StickerPremiumMark::validateStar() {
|
||||
ValidatePremiumStarFg(_star);
|
||||
ValidatePremiumStarFg(_lockIcon, _star);
|
||||
}
|
||||
|
||||
class StickerSetBox::Inner final : public Ui::RpWidget {
|
||||
|
@ -664,7 +675,7 @@ StickerSetBox::Inner::Inner(
|
|||
st::windowBgRipple,
|
||||
st::windowBgOver,
|
||||
[=] { repaintItems(); }))
|
||||
, _premiumMark(_session)
|
||||
, _premiumMark(_session, st::stickersPremiumLock)
|
||||
, _updateItemsTimer([=] { updateItems(); })
|
||||
, _input(set)
|
||||
, _padding((type == Data::StickersType::Emoji)
|
||||
|
|
|
@ -30,7 +30,9 @@ class Show;
|
|||
|
||||
class StickerPremiumMark final {
|
||||
public:
|
||||
explicit StickerPremiumMark(not_null<Main::Session*> session);
|
||||
StickerPremiumMark(
|
||||
not_null<Main::Session*> session,
|
||||
const style::icon &lockIcon);
|
||||
|
||||
void paint(
|
||||
QPainter &p,
|
||||
|
@ -44,6 +46,7 @@ private:
|
|||
void validateLock(const QImage &frame, QImage &backCache);
|
||||
void validateStar();
|
||||
|
||||
const style::icon &_lockIcon;
|
||||
QImage _lockGray;
|
||||
QImage _star;
|
||||
bool _premium = false;
|
||||
|
|
|
@ -754,6 +754,7 @@ inlineResultsMinWidth: 48px;
|
|||
inlineDurationMargin: 3px;
|
||||
|
||||
stickersPremiumLock: icon{{ "emoji/premium_lock", premiumButtonFg }};
|
||||
emojiPremiumLock: icon{{ "chat/mini_lock", premiumButtonFg }};
|
||||
|
||||
reactStripExtend: margins(21px, 49px, 39px, 0px);
|
||||
reactStripHeight: 40px;
|
||||
|
|
|
@ -136,6 +136,7 @@ struct EmojiListWidget::CustomEmojiInstance {
|
|||
struct EmojiListWidget::RecentOne {
|
||||
Ui::Text::CustomEmoji *custom = nullptr;
|
||||
RecentEmojiId id;
|
||||
mutable QImage premiumLock;
|
||||
};
|
||||
|
||||
EmojiColorPicker::EmojiColorPicker(
|
||||
|
@ -478,8 +479,12 @@ EmojiListWidget::EmojiListWidget(
|
|||
, _localSetsManager(
|
||||
std::make_unique<LocalStickersManager>(&session()))
|
||||
, _customRecentFactory(std::move(descriptor.customRecentFactory))
|
||||
, _freeEffects(std::move(descriptor.freeEffects))
|
||||
, _customTextColor(std::move(descriptor.customTextColor))
|
||||
, _overBg(st::emojiPanRadius, st().overBg)
|
||||
, _premiumMark(std::make_unique<StickerPremiumMark>(
|
||||
&session(),
|
||||
st::emojiPremiumLock))
|
||||
, _collapsedBg(st::emojiPanExpand.height / 2, st().headerFg)
|
||||
, _picker(this, st())
|
||||
, _showPickerTimer([=] { showPicker(); })
|
||||
|
@ -591,6 +596,10 @@ rpl::producer<std::vector<QString>> EmojiListWidget::searchQueries() const {
|
|||
return _searchQueries.events();
|
||||
}
|
||||
|
||||
rpl::producer<int> EmojiListWidget::recentShownCount() const {
|
||||
return _recentShownCount.value();
|
||||
}
|
||||
|
||||
void EmojiListWidget::applyNextSearchQuery() {
|
||||
if (_searchQuery == _nextSearchQuery) {
|
||||
return;
|
||||
|
@ -612,6 +621,9 @@ void EmojiListWidget::applyNextSearchQuery() {
|
|||
_searchCustomIds.clear();
|
||||
}
|
||||
resizeToWidth(width());
|
||||
_recentShownCount = searching
|
||||
? _searchResults.size()
|
||||
: _recent.size();
|
||||
update();
|
||||
if (modeChanged) {
|
||||
visibleTopBottomUpdated(getVisibleTop(), getVisibleBottom());
|
||||
|
@ -1024,9 +1036,10 @@ int EmojiListWidget::countDesiredHeight(int newWidth) {
|
|||
const auto minimalLastHeight = std::max(
|
||||
minimalHeight - padding.bottom(),
|
||||
0);
|
||||
return qMax(
|
||||
minimalHeight,
|
||||
countResult(minimalLastHeight) + padding.bottom());
|
||||
const auto result = countResult(minimalLastHeight);
|
||||
return result
|
||||
? qMax(minimalHeight, result + padding.bottom())
|
||||
: 0;
|
||||
}
|
||||
|
||||
int EmojiListWidget::defaultMinimalHeight() const {
|
||||
|
@ -1291,6 +1304,8 @@ void EmojiListWidget::paint(
|
|||
QRect clip) {
|
||||
validateEmojiPaintContext(context);
|
||||
|
||||
_paintAsPremium = session().premium();
|
||||
|
||||
auto fromColumn = floorclamp(
|
||||
clip.x() - _rowsLeft,
|
||||
_singleSize.width(),
|
||||
|
@ -1455,16 +1470,44 @@ void EmojiListWidget::drawRecent(
|
|||
QPoint position,
|
||||
const RecentOne &recent) {
|
||||
_recentPainted = true;
|
||||
const auto locked = (_mode == Mode::MessageEffects)
|
||||
&& !_paintAsPremium
|
||||
&& v::is<RecentEmojiDocument>(recent.id.data)
|
||||
&& !_freeEffects.contains(
|
||||
v::get<RecentEmojiDocument>(recent.id.data).id);
|
||||
auto lockedPainted = false;
|
||||
if (locked) {
|
||||
if (_premiumMarkFrameCache.isNull()) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
_premiumMarkFrameCache = QImage(
|
||||
QSize(_customSingleSize, _customSingleSize) * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_premiumMarkFrameCache.setDevicePixelRatio(ratio);
|
||||
}
|
||||
_premiumMarkFrameCache.fill(Qt::transparent);
|
||||
}
|
||||
if (const auto custom = recent.custom) {
|
||||
_emojiPaintContext->scale = context.progress;
|
||||
_emojiPaintContext->position = position
|
||||
const auto exactPosition = position
|
||||
+ _innerPosition
|
||||
+ _customPosition;
|
||||
_emojiPaintContext->scale = context.progress;
|
||||
if (_mode == Mode::ChannelStatus) {
|
||||
_emojiPaintContext->internal.forceFirstFrame
|
||||
= (recent.id == _recent.front().id);
|
||||
}
|
||||
custom->paint(p, *_emojiPaintContext);
|
||||
if (locked) {
|
||||
lockedPainted = custom->ready();
|
||||
|
||||
auto q = Painter(&_premiumMarkFrameCache);
|
||||
_emojiPaintContext->position = QPoint();
|
||||
custom->paint(q, *_emojiPaintContext);
|
||||
q.end();
|
||||
|
||||
p.drawImage(exactPosition, _premiumMarkFrameCache);
|
||||
} else {
|
||||
_emojiPaintContext->position = exactPosition;
|
||||
custom->paint(p, *_emojiPaintContext);
|
||||
}
|
||||
} else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) {
|
||||
if (_mode == Mode::EmojiStatus) {
|
||||
position += QPoint(
|
||||
|
@ -1478,6 +1521,16 @@ void EmojiListWidget::drawRecent(
|
|||
} else {
|
||||
Unexpected("Empty custom emoji in EmojiListWidget::drawRecent.");
|
||||
}
|
||||
|
||||
if (locked) {
|
||||
_premiumMark->paint(
|
||||
p,
|
||||
lockedPainted ? _premiumMarkFrameCache : QImage(),
|
||||
recent.premiumLock,
|
||||
position,
|
||||
_singleSize,
|
||||
width());
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiListWidget::drawEmoji(
|
||||
|
|
|
@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/round_rect.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
class StickerPremiumMark;
|
||||
|
||||
namespace style {
|
||||
struct EmojiPan;
|
||||
} // namespace style
|
||||
|
@ -89,6 +91,7 @@ struct EmojiListDescriptor {
|
|||
Fn<std::unique_ptr<Ui::Text::CustomEmoji>(
|
||||
DocumentId,
|
||||
Fn<void()>)> customRecentFactory;
|
||||
base::flat_set<DocumentId> freeEffects;
|
||||
const style::EmojiPan *st = nullptr;
|
||||
ComposeFeatures features;
|
||||
};
|
||||
|
@ -148,6 +151,7 @@ public:
|
|||
const SendMenu::Details &details) override;
|
||||
|
||||
[[nodiscard]] rpl::producer<std::vector<QString>> searchQueries() const;
|
||||
[[nodiscard]] rpl::producer<int> recentShownCount() const;
|
||||
|
||||
protected:
|
||||
void visibleTopBottomUpdated(
|
||||
|
@ -400,10 +404,13 @@ private:
|
|||
int _counts[kEmojiSectionCount];
|
||||
std::vector<RecentOne> _recent;
|
||||
base::flat_set<DocumentId> _recentCustomIds;
|
||||
base::flat_set<DocumentId> _freeEffects;
|
||||
base::flat_set<uint64> _repaintsScheduled;
|
||||
rpl::variable<int> _recentShownCount;
|
||||
std::unique_ptr<Ui::Text::CustomEmojiPaintContext> _emojiPaintContext;
|
||||
bool _recentPainted = false;
|
||||
bool _grabbingChosen = false;
|
||||
bool _paintAsPremium = false;
|
||||
QVector<EmojiPtr> _emoji[kEmojiSectionCount];
|
||||
std::vector<CustomSet> _custom;
|
||||
base::flat_set<DocumentId> _restrictedCustomList;
|
||||
|
@ -417,6 +424,8 @@ private:
|
|||
Ui::RoundRect _overBg;
|
||||
QImage _searchExpandCache;
|
||||
|
||||
std::unique_ptr<StickerPremiumMark> _premiumMark;
|
||||
QImage _premiumMarkFrameCache;
|
||||
mutable std::unique_ptr<Ui::RippleAnimation> _colorAllRipple;
|
||||
bool _colorAllRippleForced = false;
|
||||
rpl::lifetime _colorAllRippleForcedLifetime;
|
||||
|
|
|
@ -891,7 +891,7 @@ FieldAutocomplete::Inner::Inner(
|
|||
_st.pathBg,
|
||||
_st.pathFg,
|
||||
[=] { update(); }))
|
||||
, _premiumMark(_session)
|
||||
, _premiumMark(_session, st::stickersPremiumLock)
|
||||
, _previewTimer([=] { showPreview(); }) {
|
||||
_session->downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
|
|
@ -219,7 +219,9 @@ StickersListWidget::StickersListWidget(
|
|||
, _installedWidth(st::stickersTrendingInstalled.font->width(_installedText))
|
||||
, _settings(this, tr::lng_stickers_you_have(tr::now))
|
||||
, _previewTimer([=] { showPreview(); })
|
||||
, _premiumMark(std::make_unique<StickerPremiumMark>(&session()))
|
||||
, _premiumMark(std::make_unique<StickerPremiumMark>(
|
||||
&session(),
|
||||
st::stickersPremiumLock))
|
||||
, _searchRequestTimer([=] { sendSearchRequest(); }) {
|
||||
setMouseTracking(true);
|
||||
if (st().bg->c.alpha() > 0) {
|
||||
|
@ -542,8 +544,8 @@ int StickersListWidget::countDesiredHeight(int newWidth) {
|
|||
const auto minimalLastHeight = (_section == Section::Stickers)
|
||||
? minimalHeight
|
||||
: 0;
|
||||
return qMax(minimalHeight, countResult(minimalLastHeight))
|
||||
+ st::stickerPanPadding;
|
||||
const auto result = qMax(minimalHeight, countResult(minimalLastHeight));
|
||||
return result ? (result + st::stickerPanPadding) : 0;
|
||||
}
|
||||
|
||||
void StickersListWidget::sendSearchRequest() {
|
||||
|
@ -675,9 +677,14 @@ void StickersListWidget::refreshSearchRows(
|
|||
_lastMousePosition = QCursor::pos();
|
||||
|
||||
resizeToWidth(width());
|
||||
_recentShownCount = _filteredStickers.size();
|
||||
updateSelected();
|
||||
}
|
||||
|
||||
rpl::producer<int> StickersListWidget::recentShownCount() const {
|
||||
return _recentShownCount.value();
|
||||
}
|
||||
|
||||
void StickersListWidget::fillLocalSearchRows(const QString &query) {
|
||||
const auto searchWordsList = TextUtilities::PrepareSearchWords(query);
|
||||
if (searchWordsList.isEmpty()) {
|
||||
|
@ -910,6 +917,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
|
|||
toColumn = _columnCount - toColumn;
|
||||
}
|
||||
|
||||
_paintAsPremium = session().premium();
|
||||
_pathGradient->startFrame(0, width(), width() / 2);
|
||||
|
||||
auto &sets = shownSets();
|
||||
|
@ -1489,7 +1497,7 @@ void StickersListWidget::paintSticker(
|
|||
: (set.id == SearchEmojiSectionSetId())
|
||||
? &_filterStickersCornerEmoji
|
||||
: nullptr;
|
||||
if (corner && !corner->empty()) {
|
||||
if (corner && !corner->empty() && _paintAsPremium) {
|
||||
Assert(index < corner->size());
|
||||
if (const auto emoji = (*corner)[index]) {
|
||||
const auto size = Ui::Emoji::GetSizeNormal();
|
||||
|
@ -1960,6 +1968,11 @@ void StickersListWidget::setSection(Section section) {
|
|||
}
|
||||
clearHeavyData();
|
||||
_section = section;
|
||||
_recentShownCount = (section == Section::Search)
|
||||
? _filteredStickers.size()
|
||||
: _mySets.empty()
|
||||
? 0
|
||||
: _mySets.front().stickers.size();
|
||||
}
|
||||
|
||||
void StickersListWidget::clearHeavyData() {
|
||||
|
@ -2242,6 +2255,9 @@ void StickersListWidget::refreshRecentStickers(bool performResize) {
|
|||
clearSelection();
|
||||
|
||||
auto recentPack = collectRecentStickers();
|
||||
if (_section == Section::Stickers) {
|
||||
_recentShownCount = recentPack.size();
|
||||
}
|
||||
auto recentIt = std::find_if(_mySets.begin(), _mySets.end(), [](auto &set) {
|
||||
return set.id == Data::Stickers::RecentSetId;
|
||||
});
|
||||
|
|
|
@ -128,6 +128,7 @@ public:
|
|||
bool mySetsEmpty() const;
|
||||
|
||||
void applySearchQuery(std::vector<QString> &&query);
|
||||
[[nodiscard]] rpl::producer<int> recentShownCount() const;
|
||||
|
||||
~StickersListWidget();
|
||||
|
||||
|
@ -388,6 +389,7 @@ private:
|
|||
base::flat_set<not_null<DocumentData*>> _favedStickersMap;
|
||||
std::weak_ptr<Lottie::FrameRenderer> _lottieRenderer;
|
||||
|
||||
bool _paintAsPremium = false;
|
||||
bool _showingSetById = false;
|
||||
crl::time _lastScrolledAt = 0;
|
||||
crl::time _lastFullUpdatedAt = 0;
|
||||
|
@ -437,6 +439,7 @@ private:
|
|||
|
||||
std::vector<not_null<DocumentData*>> _filteredStickers;
|
||||
std::vector<EmojiPtr> _filterStickersCornerEmoji;
|
||||
rpl::variable<int> _recentShownCount;
|
||||
std::map<QString, std::vector<uint64>> _searchCache;
|
||||
std::vector<std::pair<uint64, QStringList>> _searchIndex;
|
||||
base::Timer _searchRequestTimer;
|
||||
|
|
|
@ -972,6 +972,7 @@ void Selector::createList() {
|
|||
: st::reactPanelScrollRounded);
|
||||
_scroll->hide();
|
||||
|
||||
const auto effects = !_reactions.stickers.empty();
|
||||
const auto st = lifetime().make_state<style::EmojiPan>(_st);
|
||||
st->padding.setTop(_skipy);
|
||||
if (!_reactions.customAllowed) {
|
||||
|
@ -979,15 +980,35 @@ void Selector::createList() {
|
|||
}
|
||||
auto lists = _scroll->setOwnedWidget(
|
||||
object_ptr<Ui::VerticalLayout>(_scroll));
|
||||
auto recentList = _strip
|
||||
? _unifiedFactoryOwner->unifiedIdsList()
|
||||
: _recent;
|
||||
auto freeEffects = base::flat_set<DocumentId>();
|
||||
if (effects) {
|
||||
auto free = base::flat_set<Data::ReactionId>();
|
||||
free.reserve(_reactions.recent.size());
|
||||
for (const auto &reaction : _reactions.recent) {
|
||||
if (!reaction.premium) {
|
||||
free.emplace(reaction.id);
|
||||
}
|
||||
}
|
||||
for (const auto &id : recentList) {
|
||||
const auto reactionId = _strip
|
||||
? _unifiedFactoryOwner->lookupReactionId(id)
|
||||
: Data::ReactionId{ id };
|
||||
if (free.contains(reactionId)) {
|
||||
freeEffects.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
_list = lists->add(
|
||||
object_ptr<EmojiListWidget>(lists, EmojiListDescriptor{
|
||||
.show = _show,
|
||||
.mode = _listMode,
|
||||
.paused = [] { return false; },
|
||||
.customRecentList = (_strip
|
||||
? _unifiedFactoryOwner->unifiedIdsList()
|
||||
: _recent),
|
||||
.customRecentList = std::move(recentList),
|
||||
.customRecentFactory = _unifiedFactoryOwner->factory(),
|
||||
.freeEffects = std::move(freeEffects),
|
||||
.st = st,
|
||||
}));
|
||||
if (!_reactions.stickers.empty()) {
|
||||
|
@ -1092,6 +1113,27 @@ void Selector::createList() {
|
|||
) | rpl::start_with_next([=](std::vector<QString> &&query) {
|
||||
_stickers->applySearchQuery(std::move(query));
|
||||
}, _stickers->lifetime());
|
||||
|
||||
|
||||
rpl::combine(
|
||||
_list->recentShownCount(),
|
||||
_stickers->recentShownCount()
|
||||
) | rpl::start_with_next([=](int emoji, int stickers) {
|
||||
_showEmptySearch = !emoji && !stickers;
|
||||
_scroll->update();
|
||||
}, _scroll->lifetime());
|
||||
|
||||
_scroll->paintRequest() | rpl::filter([=] {
|
||||
return _showEmptySearch;
|
||||
}) | rpl::start_with_next([=] {
|
||||
auto p = QPainter(_scroll);
|
||||
p.setPen(st::windowSubTextFg);
|
||||
p.setFont(st::normalFont);
|
||||
p.drawText(
|
||||
_scroll->rect(),
|
||||
tr::lng_effect_none(tr::now),
|
||||
style::al_center);
|
||||
}, _scroll->lifetime());
|
||||
} else {
|
||||
_list->setMinimalHeight(geometry.width(), _scroll->height());
|
||||
}
|
||||
|
|
|
@ -205,6 +205,7 @@ private:
|
|||
Ui::PlainShadow *_shadow = nullptr;
|
||||
rpl::variable<int> _shadowTop = 0;
|
||||
rpl::variable<int> _shadowSkip = 0;
|
||||
bool _showEmptySearch = false;
|
||||
|
||||
QImage _paintBuffer;
|
||||
Ui::Animations::Simple _expanding;
|
||||
|
|
|
@ -29,9 +29,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
|
@ -42,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
@ -89,6 +93,8 @@ private:
|
|||
void paintEvent(QPaintEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
|
||||
[[nodiscard]] bool canSend() const;
|
||||
|
||||
void setupGeometry(QPoint position);
|
||||
void setupBackground();
|
||||
void setupItem();
|
||||
|
@ -110,6 +116,9 @@ private:
|
|||
const AdminLog::OwnedItem _replyTo;
|
||||
const AdminLog::OwnedItem _item;
|
||||
const std::unique_ptr<Ui::FlatButton> _send;
|
||||
const std::unique_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _premiumPromoLabel;
|
||||
const not_null<Ui::RpWidget*> _bottom;
|
||||
const Fn<void()> _close;
|
||||
const Fn<void(Action, Details)> _actionWithEffect;
|
||||
|
||||
QImage _icon;
|
||||
|
@ -236,11 +245,26 @@ EffectPreview::EffectPreview(
|
|||
_replyTo->data()->fullId(),
|
||||
tr::lng_settings_chat_message_reply(tr::now),
|
||||
_effectId))
|
||||
, _send(
|
||||
std::make_unique<BottomRounded>(
|
||||
, _send(canSend()
|
||||
? std::make_unique<BottomRounded>(
|
||||
this,
|
||||
tr::lng_effect_send(tr::now),
|
||||
st::effectPreviewSend))
|
||||
st::effectPreviewSend)
|
||||
: nullptr)
|
||||
, _premiumPromoLabel(canSend()
|
||||
? nullptr
|
||||
: std::make_unique<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||
this,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
this,
|
||||
tr::lng_effect_premium(
|
||||
lt_link,
|
||||
tr::lng_effect_premium_link() | Ui::Text::ToLink(),
|
||||
Ui::Text::WithEntities),
|
||||
st::effectPreviewPromoLabel),
|
||||
st::effectPreviewPromoPadding))
|
||||
, _bottom(_send ? ((Ui::RpWidget*)_send.get()) : _premiumPromoLabel.get())
|
||||
, _close(done)
|
||||
, _actionWithEffect(ComposeActionWithEffect(action, _effectId, done)) {
|
||||
setupGeometry(position);
|
||||
setupBackground();
|
||||
|
@ -291,8 +315,9 @@ void EffectPreview::setupGeometry(QPoint position) {
|
|||
const auto shadow = st::previewMenu.shadow;
|
||||
const auto extend = shadow.extend;
|
||||
_inner = QRect(QPoint(extend.left(), extend.top()), innerSize);
|
||||
_bottom->resizeToWidth(_inner.width());
|
||||
const auto size = _inner.marginsAdded(extend).size()
|
||||
+ QSize(0, _send->height());
|
||||
+ QSize(0, _bottom->height());
|
||||
const auto left = std::max(
|
||||
std::min(
|
||||
position.x() - size.width() / 2,
|
||||
|
@ -305,11 +330,11 @@ void EffectPreview::setupGeometry(QPoint position) {
|
|||
parent->height() - size.height()),
|
||||
topMin);
|
||||
setGeometry(left, top, size.width(), size.height());
|
||||
_send->setGeometry(
|
||||
_bottom->setGeometry(
|
||||
_inner.x(),
|
||||
_inner.y() + _inner.height(),
|
||||
_inner.width(),
|
||||
_send->height());
|
||||
_bottom->height());
|
||||
}
|
||||
|
||||
void EffectPreview::setupBackground() {
|
||||
|
@ -343,7 +368,7 @@ void EffectPreview::setupItem() {
|
|||
|
||||
void EffectPreview::repaintBackground() {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto inner = _inner.size() + QSize(0, _send->height());
|
||||
const auto inner = _inner.size() + QSize(0, _bottom->height());
|
||||
auto bg = QImage(
|
||||
inner * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
|
@ -357,7 +382,7 @@ void EffectPreview::repaintBackground() {
|
|||
QSize(inner.width(), inner.height() * 5),
|
||||
QRect(QPoint(), inner));
|
||||
p.fillRect(
|
||||
QRect(0, _inner.height(), _inner.width(), _send->height()),
|
||||
QRect(0, _inner.height(), _inner.width(), _bottom->height()),
|
||||
st::previewMarkRead.bgColor);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||
|
@ -409,14 +434,32 @@ void EffectPreview::createLottie() {
|
|||
}, raw->lifetime());
|
||||
}
|
||||
|
||||
bool EffectPreview::canSend() const {
|
||||
return !_effect.premium || _show->session().premium();
|
||||
}
|
||||
|
||||
void EffectPreview::setupSend(Details details) {
|
||||
_send->setClickedCallback([=] {
|
||||
_actionWithEffect(Api::SendOptions(), details);
|
||||
});
|
||||
const auto type = details.type;
|
||||
SetupMenuAndShortcuts(_send.get(), _show, [=] {
|
||||
return Details{ .type = type };
|
||||
}, _actionWithEffect);
|
||||
if (_send) {
|
||||
_send->setClickedCallback([=] {
|
||||
_actionWithEffect(Api::SendOptions(), details);
|
||||
});
|
||||
const auto type = details.type;
|
||||
SetupMenuAndShortcuts(_send.get(), _show, [=] {
|
||||
return Details{ .type = type };
|
||||
}, _actionWithEffect);
|
||||
} else {
|
||||
_premiumPromoLabel->entity()->setClickHandlerFilter([=](auto&&...) {
|
||||
const auto window = _show->resolveWindow(
|
||||
ChatHelpers::WindowUsage::PremiumPromo);
|
||||
if (window) {
|
||||
if (const auto onstack = _close) {
|
||||
onstack();
|
||||
}
|
||||
Settings::ShowPremium(window, "message_effect");
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectPreview::checkReady() {
|
||||
|
|
|
@ -1120,3 +1120,12 @@ effectPreviewSend: FlatButton(previewMarkRead) {
|
|||
bgColor: transparent;
|
||||
overBgColor: transparent;
|
||||
}
|
||||
effectPreviewPromoLabel: FlatLabel(defaultFlatLabel) {
|
||||
minWidth: 64px;
|
||||
align: align(top);
|
||||
textFg: windowSubTextFg;
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: font(11px);
|
||||
}
|
||||
}
|
||||
effectPreviewPromoPadding: margins(4px, 6px, 4px, 6px);
|
||||
|
|
Loading…
Add table
Reference in a new issue