Show featured custom sets in the panel.

This commit is contained in:
John Preston 2022-07-22 19:00:06 +03:00
parent ff55918da0
commit b0fab8c987
11 changed files with 478 additions and 193 deletions

View File

@ -1061,11 +1061,11 @@ bool StickersBox::Inner::Row::isRecentSet() const {
}
bool StickersBox::Inner::Row::isMasksSet() const {
return (set->flags & SetFlag::Masks);
return (set->type() == Data::StickersType::Masks);
}
bool StickersBox::Inner::Row::isEmojiSet() const {
return (set->flags & SetFlag::Emoji);
return (set->type() == Data::StickersType::Emoji);
}
bool StickersBox::Inner::Row::isWebm() const {

View File

@ -31,6 +31,14 @@ stickersTrendingSubheaderFont: normalFont;
stickersTrendingSubheaderFg: windowSubTextFg;
stickersTrendingSubheaderTop: 31px;
emojiPanButtonRight: 7px;
emojiPanButtonTop: 10px;
emojiPanButton: RoundButton(defaultActiveButton) {
width: -24px;
height: 23px;
textTop: 2px;
}
stickersTrendingAddTop: 14px;
stickersTrendingAdd: RoundButton(defaultActiveButton) {
width: -16px;

View File

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/shadow.h"
#include "ui/text/custom_emoji_instance.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/premium_graphics.h"
#include "ui/emoji_config.h"
#include "ui/ui_utility.h"
#include "ui/cached_round_corners.h"
@ -31,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "core/core_settings.h"
#include "core/application.h"
#include "settings/settings_premium.h"
#include "window/window_session_controller.h"
#include "facades.h"
#include "styles/style_chat_helpers.h"
@ -363,6 +365,8 @@ EmojiListWidget::EmojiListWidget(
QWidget *parent,
not_null<Window::SessionController*> controller)
: Inner(parent, controller)
, _localSetsManager(
std::make_unique<LocalStickersManager>(&controller->session()))
, _picker(this)
, _showPickerTimer([=] { showPicker(); })
, _repaintTimer([=] { invokeRepaints(); }) {
@ -399,6 +403,16 @@ EmojiListWidget::EmojiListWidget(
refreshCustom();
resizeToWidth(width());
}, lifetime());
rpl::single(
rpl::empty
) | rpl::then(
style::PaletteChanged()
) | rpl::start_with_next([=] {
initButton(_add, tr::lng_stickers_featured_add(tr::now), false);
initButton(_unlock, tr::lng_emoji_featured_unlock(tr::now), true);
initButton(_restore, tr::lng_emoji_premium_restore(tr::now), true);
}, lifetime());
}
EmojiListWidget::~EmojiListWidget() {
@ -581,8 +595,6 @@ bool EmojiListWidget::enumerateSections(Callback callback) const {
auto i = 0;
auto info = SectionInfo();
const auto session = &controller()->session();
const auto premiumMayBeBought = !session->premium()
&& session->premiumPossible();
const auto next = [&] {
info.rowsCount = (info.count + _columnCount - 1) / _columnCount;
info.rowsTop = info.top
@ -605,7 +617,7 @@ bool EmojiListWidget::enumerateSections(Callback callback) const {
}
for (auto &section : _custom) {
info.section = i++;
info.premiumRequired = section.premium && premiumMayBeBought;
info.premiumRequired = section.premiumRequired;
info.count = int(section.list.size());
if (!next()) {
return false;
@ -763,23 +775,13 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
} else if (r.top() + r.height() <= info.top) {
return false;
}
auto widthForTitle = emojiRight() - (st::emojiPanHeaderLeft - st::roundRadiusSmall);
const auto buttonSelected = selectedButton
? (selectedButton->section == info.section)
: false;
const auto widthForTitle = emojiRight()
- (st::emojiPanHeaderLeft - st::roundRadiusSmall)
- paintButtonGetWidth(p, info, buttonSelected, r);
const auto skip = st::roundRadiusSmall;
if (hasRemoveButton(info.section)) {
auto &custom = _custom[info.section - kEmojiSectionCount];
auto remove = removeButtonRect(info.section);
if (remove.intersects(r)) {
auto selected = selectedButton ? (selectedButton->section == info.section) : false;
if (custom.ripple) {
custom.ripple->paint(p, remove.x() + st::stickerPanRemoveSet.rippleAreaPosition.x(), remove.y() + st::stickerPanRemoveSet.rippleAreaPosition.y(), width());
if (custom.ripple->empty()) {
custom.ripple.reset();
}
}
(selected ? st::stickerPanRemoveSet.iconOver : st::stickerPanRemoveSet.icon).paint(p, remove.topLeft() + st::stickerPanRemoveSet.iconPosition, width());
}
widthForTitle -= remove.width();
}
if (info.section > 0 && r.top() < info.rowsTop) {
p.setFont(st::emojiPanHeaderFont);
p.setPen(st::emojiPanHeaderFg);
@ -1010,7 +1012,14 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
} else if (auto button = std::get_if<OverButton>(&pressed)) {
Assert(button->section >= kEmojiSectionCount
&& button->section < kEmojiSectionCount + _custom.size());
removeSet(_custom[button->section - kEmojiSectionCount].id);
const auto id = _custom[button->section - kEmojiSectionCount].id;
if (hasRemoveButton(button->section)) {
removeSet(id);
} else if (hasAddButton(button->section)) {
_localSetsManager->install(id);
} else {
Settings::ShowPremium(controller(), u"animated_emoji"_q);
}
}
}
@ -1089,18 +1098,88 @@ bool EmojiListWidget::hasRemoveButton(int index) const {
|| index >= kEmojiSectionCount + _custom.size()) {
return false;
}
return true;
const auto &set = _custom[index - kEmojiSectionCount];
return set.canRemove && !set.premiumRequired;
}
QRect EmojiListWidget::removeButtonRect(int index) const {
auto buttonw = st::stickerPanRemoveSet.rippleAreaPosition.x()
return removeButtonRect(sectionInfo(index));
}
QRect EmojiListWidget::removeButtonRect(const SectionInfo &info) const {
const auto buttonw = st::stickerPanRemoveSet.rippleAreaPosition.x()
+ st::stickerPanRemoveSet.rippleAreaSize;
auto buttonh = st::stickerPanRemoveSet.height;
auto buttonx = emojiRight() - st::emojiPanRemoveSkip - buttonw;
auto buttony = sectionInfo(index).top + (st::emojiPanHeader - buttonh) / 2;
const auto buttonh = st::stickerPanRemoveSet.height;
const auto buttonx = emojiRight() - st::emojiPanRemoveSkip - buttonw;
const auto buttony = info.top + (st::emojiPanHeader - buttonh) / 2;
return QRect(buttonx, buttony, buttonw, buttonh);
}
bool EmojiListWidget::hasAddButton(int index) const {
if (index < kEmojiSectionCount
|| index >= kEmojiSectionCount + _custom.size()) {
return false;
}
const auto &set = _custom[index - kEmojiSectionCount];
return !set.canRemove && !set.premiumRequired;
}
QRect EmojiListWidget::addButtonRect(int index) const {
return buttonRect(sectionInfo(index), _add);
}
bool EmojiListWidget::hasUnlockButton(int index) const {
if (index < kEmojiSectionCount
|| index >= kEmojiSectionCount + _custom.size()) {
return false;
}
const auto &set = _custom[index - kEmojiSectionCount];
return set.premiumRequired;
}
QRect EmojiListWidget::unlockButtonRect(int index) const {
Expects(index >= kEmojiSectionCount
&& index < kEmojiSectionCount + _custom.size());
return buttonRect(sectionInfo(index), rightButton(index));
}
bool EmojiListWidget::hasButton(int index) const {
if (index < kEmojiSectionCount
|| index >= kEmojiSectionCount + _custom.size()) {
return false;
}
return true;
}
QRect EmojiListWidget::buttonRect(int index) const {
return hasRemoveButton(index)
? removeButtonRect(index)
: hasAddButton(index)
? addButtonRect(index)
: unlockButtonRect(index);
}
QRect EmojiListWidget::buttonRect(
const SectionInfo &info,
const RightButton &button) const {
const auto buttonw = button.textWidth - st::emojiPanButton.width;
const auto buttonh = st::emojiPanButton.height;
const auto buttonx = emojiRight() - buttonw - st::emojiPanButtonRight;
const auto buttony = info.top + st::emojiPanButtonTop;
return QRect(buttonx, buttony, buttonw, buttonh);
}
auto EmojiListWidget::rightButton(int index) const -> const RightButton & {
Expects(index >= kEmojiSectionCount
&& index < kEmojiSectionCount + _custom.size());
return hasAddButton(index)
? _add
: _custom[index - kEmojiSectionCount].canRemove
? _restore
: _unlock;
}
int EmojiListWidget::emojiRight() const {
return emojiLeft() + (_columnCount * _singleSize.width());
}
@ -1221,15 +1300,22 @@ void EmojiListWidget::refreshRecent() {
void EmojiListWidget::refreshCustom() {
auto old = base::take(_custom);
const auto owner = &controller()->session().data();
const auto &order = owner->stickers().emojiSetsOrder();
const auto &featured = owner->stickers().featuredEmojiSetsOrder();
const auto session = &controller()->session();
const auto premiumPossible = session->premiumPossible();
const auto premiumMayBeBought = premiumPossible && !session->premium();
const auto owner = &session->data();
const auto &sets = owner->stickers().sets();
for (const auto setId : ranges::views::concat(order, featured)) {
const auto push = [&](uint64 setId, bool installed) {
auto it = sets.find(setId);
if (it == sets.cend() || it->second->stickers.isEmpty()) {
continue;
return;
}
const auto canRemove = !!(it->second->flags
& Data::StickersSetFlag::Installed);
if (canRemove != installed) {
return;
}
auto premium = false;
const auto &list = it->second->stickers;
const auto i = ranges::find(old, setId, &CustomSet::id);
if (i != end(old)) {
@ -1239,19 +1325,26 @@ void EmojiListWidget::refreshCustom() {
return false;
}
for (auto k = 0; k != count; ++k) {
if (!premium && list[k]->isPremiumEmoji()) {
premium = true;
}
if (i->list[k].document != list[k]) {
return false;
}
}
return true;
}();
if (valid) {
if (premium && !premiumPossible) {
return;
} else if (valid) {
i->thumbnailDocument = it->second->lookupThumbnailDocument();
i->canRemove = canRemove;
i->premiumRequired = premium && premiumMayBeBought;
i->ripple.reset();
_custom.push_back(std::move(*i));
continue;
return;
}
}
auto premium = false;
auto set = std::vector<CustomOne>();
set.reserve(list.size());
for (const auto document : list) {
@ -1260,13 +1353,13 @@ void EmojiListWidget::refreshCustom() {
.instance = resolveCustomInstance(document, setId),
.document = document,
});
if (document->isPremiumEmoji()) {
if (!premium && document->isPremiumEmoji()) {
premium = true;
}
}
}
if (premium && !controller()->session().premiumPossible()) {
continue;
if (premium && !premiumPossible) {
return;
}
_custom.push_back({
.id = setId,
@ -1274,13 +1367,22 @@ void EmojiListWidget::refreshCustom() {
.thumbnailDocument = it->second->lookupThumbnailDocument(),
.title = it->second->title,
.list = std::move(set),
.premium = premium,
.canRemove = canRemove,
.premiumRequired = premium && premiumMayBeBought,
});
};
for (const auto setId : owner->stickers().emojiSetsOrder()) {
push(setId, true);
}
for (const auto setId : owner->stickers().featuredEmojiSetsOrder()) {
push(setId, false);
}
_footer->refreshIcons(
fillIcons(),
nullptr,
ValidateIconAnimations::None);
update();
}
auto EmojiListWidget::customInstanceWithLoader(
@ -1419,6 +1521,64 @@ std::vector<StickerIcon> EmojiListWidget::fillIcons() {
return result;
}
int EmojiListWidget::paintButtonGetWidth(
QPainter &p,
const SectionInfo &info,
bool selected,
QRect clip) const {
if (info.section < kEmojiSectionCount
|| info.section >= kEmojiSectionCount + _custom.size()) {
return 0;
}
auto &custom = _custom[info.section - kEmojiSectionCount];
if (hasRemoveButton(info.section)) {
const auto remove = removeButtonRect(info);
if (remove.intersects(clip)) {
if (custom.ripple) {
custom.ripple->paint(
p,
remove.x() + st::stickerPanRemoveSet.rippleAreaPosition.x(),
remove.y() + st::stickerPanRemoveSet.rippleAreaPosition.y(),
width());
if (custom.ripple->empty()) {
custom.ripple.reset();
}
}
(selected
? st::stickerPanRemoveSet.iconOver
: st::stickerPanRemoveSet.icon).paint(
p,
remove.topLeft() + st::stickerPanRemoveSet.iconPosition,
width());
}
return remove.width();
}
const auto canAdd = hasAddButton(info.section);
const auto &button = rightButton(info.section);
const auto rect = buttonRect(info, button);
p.drawImage(rect.topLeft(), selected ? button.backOver : button.back);
if (custom.ripple) {
const auto ripple = QColor(0, 0, 0, 36);
custom.ripple->paint(p, rect.x(), rect.y(), width(), &ripple);
if (custom.ripple->empty()) {
custom.ripple.reset();
}
}
p.setPen(!canAdd
? st::premiumButtonFg
: selected
? st::emojiPanButton.textFgOver
: st::emojiPanButton.textFg);
p.setFont(st::emojiPanButton.font);
p.drawText(
rect.x() - (st::emojiPanButton.width / 2),
(rect.y()
+ st::emojiPanButton.textTop
+ st::emojiPanButton.font->ascent),
button.text);
return rect.width();
}
bool EmojiListWidget::eventHook(QEvent *e) {
if (e->type() == QEvent::ParentChange) {
if (_picker->parentWidget() != parentWidget()) {
@ -1439,9 +1599,8 @@ void EmojiListWidget::updateSelected() {
auto info = sectionInfoByOffset(p.y());
auto section = info.section;
if (p.y() >= info.top && p.y() < info.rowsTop) {
if (hasRemoveButton(section)
&& myrtlrect(
removeButtonRect(section)).contains(p.x(), p.y())) {
if (hasButton(section)
&& myrtlrect(buttonRect(section)).contains(p.x(), p.y())) {
newSelected = OverButton{ section };
} else if (section >= kEmojiSectionCount) {
newSelected = OverSet{ section };
@ -1470,7 +1629,7 @@ void EmojiListWidget::setSelected(OverState newSelected) {
if (const auto sticker = std::get_if<OverEmoji>(&_selected)) {
rtlupdate(emojiRect(sticker->section, sticker->index));
} else if (const auto button = std::get_if<OverButton>(&_selected)) {
rtlupdate(removeButtonRect(button->section));
rtlupdate(buttonRect(button->section));
}
};
updateSelected();
@ -1513,27 +1672,72 @@ void EmojiListWidget::setPressed(OverState newPressed) {
}
}
void EmojiListWidget::initButton(
RightButton &button,
const QString &text,
bool gradient) {
button.text = text;
button.textWidth = st::emojiPanButton.font->width(text);
const auto width = button.textWidth - st::emojiPanButton.width;
const auto height = st::emojiPanButton.height;
const auto factor = style::DevicePixelRatio();
auto prepare = [&](QColor bg, QBrush fg) {
auto image = QImage(
QSize(width, height) * factor,
QImage::Format_ARGB32_Premultiplied);
image.setDevicePixelRatio(factor);
image.fill(Qt::transparent);
auto p = QPainter(&image);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(fg);
const auto radius = height / 2.;
p.drawRoundedRect(QRect(0, 0, width, height), radius, radius);
p.end();
return image;
};
button.back = prepare(Qt::transparent, [&]() -> QBrush {
if (gradient) {
auto result = QLinearGradient(QPointF(0, 0), QPointF(width, 0));
result.setStops(Ui::Premium::ButtonGradientStops());
return result;
}
return st::emojiPanButton.textBg;
}());
button.backOver = gradient
? button.back
: prepare(Qt::transparent, st::emojiPanButton.textBgOver);
button.rippleMask = prepare(Qt::black, Qt::white);
}
std::unique_ptr<Ui::RippleAnimation> EmojiListWidget::createButtonRipple(
int section) {
Expects(section >= kEmojiSectionCount
&& section < kEmojiSectionCount + _custom.size());
auto maskSize = QSize(
st::stickerPanRemoveSet.rippleAreaSize,
st::stickerPanRemoveSet.rippleAreaSize);
auto mask = Ui::RippleAnimation::ellipseMask(maskSize);
const auto remove = hasRemoveButton(section);
const auto &st = remove
? st::stickerPanRemoveSet.ripple
: st::emojiPanButton.ripple;
auto mask = remove
? Ui::RippleAnimation::ellipseMask(QSize(
st::stickerPanRemoveSet.rippleAreaSize,
st::stickerPanRemoveSet.rippleAreaSize))
: rightButton(section).rippleMask;
return std::make_unique<Ui::RippleAnimation>(
st::stickerPanRemoveSet.ripple,
st,
std::move(mask),
[this, section] { rtlupdate(removeButtonRect(section)); });
[this, section] { rtlupdate(buttonRect(section)); });
}
QPoint EmojiListWidget::buttonRippleTopLeft(int section) const {
Expects(section >= kEmojiSectionCount
&& section < kEmojiSectionCount + _custom.size());
return myrtlrect(removeButtonRect(section)).topLeft()
+ st::stickerPanRemoveSet.rippleAreaPosition;
return myrtlrect(buttonRect(section)).topLeft()
+ (hasRemoveButton(section)
? st::stickerPanRemoveSet.rippleAreaPosition
: QPoint());
}
void EmojiListWidget::showEmojiSection(Section section) {

View File

@ -49,6 +49,7 @@ inline constexpr auto kEmojiSectionCount = 8;
struct StickerIcon;
class EmojiColorPicker;
class StickersListFooter;
class LocalStickersManager;
class EmojiListWidget
: public TabbedSelector::Inner
@ -120,9 +121,17 @@ private:
DocumentData *thumbnailDocument = nullptr;
QString title;
std::vector<CustomOne> list;
std::unique_ptr<Ui::RippleAnimation> ripple;
mutable std::unique_ptr<Ui::RippleAnimation> ripple;
bool painted = false;
bool premium = false;
bool canRemove = false;
bool premiumRequired = false;
};
struct RightButton {
QImage back;
QImage backOver;
QImage rippleMask;
QString text;
int textWidth = 0;
};
struct RecentOne;
struct RepaintSet {
@ -210,15 +219,32 @@ private:
int index);
[[nodiscard]] bool hasRemoveButton(int index) const;
[[nodiscard]] QRect removeButtonRect(int index) const;
[[nodiscard]] QRect removeButtonRect(const SectionInfo &info) const;
[[nodiscard]] bool hasAddButton(int index) const;
[[nodiscard]] QRect addButtonRect(int index) const;
[[nodiscard]] bool hasUnlockButton(int index) const;
[[nodiscard]] QRect unlockButtonRect(int index) const;
[[nodiscard]] bool hasButton(int index) const;
[[nodiscard]] QRect buttonRect(int index) const;
[[nodiscard]] QRect buttonRect(
const SectionInfo &info,
const RightButton &button) const;
[[nodiscard]] const RightButton &rightButton(int index) const;
[[nodiscard]] QRect emojiRect(int section, int index) const;
[[nodiscard]] int emojiRight() const;
[[nodiscard]] int emojiLeft() const;
[[nodiscard]] uint64 sectionSetId(int section) const;
[[nodiscard]] std::vector<StickerIcon> fillIcons();
int paintButtonGetWidth(
QPainter &p,
const SectionInfo &info,
bool selected,
QRect clip) const;
void displaySet(uint64 setId);
void removeSet(uint64 setId);
void initButton(RightButton &button, const QString &text, bool gradient);
[[nodiscard]] std::unique_ptr<Ui::RippleAnimation> createButtonRipple(
int section);
[[nodiscard]] QPoint buttonRippleTopLeft(int section) const;
@ -246,6 +272,7 @@ private:
uint64 setId);
StickersListFooter *_footer = nullptr;
std::unique_ptr<LocalStickersManager> _localSetsManager;
int _counts[kEmojiSectionCount];
std::vector<RecentOne> _recent;
@ -261,6 +288,10 @@ private:
QPoint _areaPosition;
QPoint _innerPosition;
RightButton _add;
RightButton _unlock;
RightButton _restore;
OverState _selected;
OverState _pressed;
OverState _pickerSelected;

View File

@ -1275,4 +1275,79 @@ void StickersListFooter::paintSetIcon(
}
}
LocalStickersManager::LocalStickersManager(not_null<Main::Session*> session)
: _session(session)
, _api(&session->mtp()) {
}
void LocalStickersManager::install(uint64 setId) {
const auto &sets = _session->data().stickers().sets();
const auto it = sets.find(setId);
if (it == sets.cend()) {
return;
}
const auto set = it->second.get();
const auto input = set->mtpInput();
if (!(set->flags & Data::StickersSetFlag::NotLoaded)
&& !set->stickers.empty()) {
sendInstallRequest(setId, input);
return;
}
_api.request(MTPmessages_GetStickerSet(
input,
MTP_int(0) // hash
)).done([=](const MTPmessages_StickerSet &result) {
result.match([&](const MTPDmessages_stickerSet &data) {
_session->data().stickers().feedSetFull(data);
}, [](const MTPDmessages_stickerSetNotModified &) {
LOG(("API Error: Unexpected messages.stickerSetNotModified."));
});
sendInstallRequest(setId, input);
}).send();
}
bool LocalStickersManager::isInstalledLocally(uint64 setId) const {
return _installedLocallySets.contains(setId);
}
void LocalStickersManager::sendInstallRequest(
uint64 setId,
const MTPInputStickerSet &input) {
_api.request(MTPmessages_InstallStickerSet(
input,
MTP_bool(false)
)).done([=](const MTPmessages_StickerSetInstallResult &result) {
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
_session->data().stickers().applyArchivedResult(
result.c_messages_stickerSetInstallResultArchive());
}
}).fail([=] {
notInstalledLocally(setId);
_session->data().stickers().undoInstallLocally(setId);
}).send();
installedLocally(setId);
_session->data().stickers().installLocally(setId);
}
void LocalStickersManager::installedLocally(uint64 setId) {
_installedLocallySets.insert(setId);
}
void LocalStickersManager::notInstalledLocally(uint64 setId) {
_installedLocallySets.remove(setId);
}
void LocalStickersManager::removeInstalledLocally(uint64 setId) {
_installedLocallySets.remove(setId);
}
bool LocalStickersManager::clearInstalledLocally() {
if (_installedLocallySets.empty()) {
return false;
}
_installedLocallySets.clear();
return true;
}
} // namespace ChatHelpers

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/clip/media_clip_reader.h"
#include "chat_helpers/tabbed_selector.h"
#include "mtproto/sender.h"
#include "ui/round_rect.h"
namespace Ui {
@ -251,4 +252,27 @@ private:
};
class LocalStickersManager final {
public:
explicit LocalStickersManager(not_null<Main::Session*> session);
void install(uint64 setId);
[[nodiscard]] bool isInstalledLocally(uint64 setId) const;
void removeInstalledLocally(uint64 setId);
bool clearInstalledLocally();
private:
void sendInstallRequest(
uint64 setId,
const MTPInputStickerSet &input);
void installedLocally(uint64 setId);
void notInstalledLocally(uint64 setId);
const not_null<Main::Session*> _session;
MTP::Sender _api;
base::flat_set<uint64> _installedLocallySets;
};
} // namespace ChatHelpers

View File

@ -165,6 +165,8 @@ StickersListWidget::StickersListWidget(
bool masks)
: Inner(parent, controller)
, _api(&controller->session().mtp())
, _localSetsManager(
std::make_unique<LocalStickersManager>(&controller->session()))
, _section(Section::Stickers)
, _isMasks(masks)
, _updateItemsTimer([=] { updateItems(); })
@ -495,21 +497,6 @@ int StickersListWidget::countDesiredHeight(int newWidth) {
+ st::stickerPanPadding;
}
void StickersListWidget::installedLocally(uint64 setId) {
_installedLocallySets.insert(setId);
}
void StickersListWidget::notInstalledLocally(uint64 setId) {
_installedLocallySets.remove(setId);
}
void StickersListWidget::clearInstalledLocally() {
if (!_installedLocallySets.empty()) {
_installedLocallySets.clear();
refreshStickers();
}
}
void StickersListWidget::sendSearchRequest() {
if (_searchRequestId || _searchNextQuery.isEmpty()) {
return;
@ -851,7 +838,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
auto widthForTitle = stickersRight() - (st::emojiPanHeaderLeft - st::roundRadiusSmall);
if (featuredHasAddButton(info.section)) {
auto add = featuredAddRect(info.section);
auto add = featuredAddRect(info);
auto selected = selectedButton ? (selectedButton->section == info.section) : false;
auto &textBg = selected ? st::stickersTrendingAdd.textBgOver : st::stickersTrendingAdd.textBg;
@ -868,7 +855,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
widthForTitle -= add.width() - (st::stickersTrendingAdd.width / 2);
} else {
auto add = featuredAddRect(info.section);
auto add = featuredAddRect(info);
int checkx = add.left() + (add.width() - st::stickersFeaturedInstalled.width()) / 2;
int checky = add.top() + (add.height() - st::stickersFeaturedInstalled.height()) / 2;
st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width());
@ -924,7 +911,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
auto titleWidth = st::stickersTrendingHeaderFont->width(titleText);
auto widthForTitle = stickersRight() - (st::emojiPanHeaderLeft - st::roundRadiusSmall);
if (hasRemoveButton(info.section)) {
auto remove = removeButtonRect(info.section);
auto remove = removeButtonRect(info);
auto selected = selectedButton ? (selectedButton->section == info.section) : false;
if (set.ripple) {
set.ripple->paint(p, remove.x() + st::stickerPanRemoveSet.rippleAreaPosition.x(), remove.y() + st::stickerPanRemoveSet.rippleAreaPosition.y(), width());
@ -1430,10 +1417,14 @@ bool StickersListWidget::featuredHasAddButton(int index) const {
}
QRect StickersListWidget::featuredAddRect(int index) const {
return featuredAddRect(sectionInfo(index));
}
QRect StickersListWidget::featuredAddRect(const SectionInfo &info) const {
auto addw = _addWidth - st::stickersTrendingAdd.width;
auto addh = st::stickersTrendingAdd.height;
auto addx = stickersRight() - addw;
auto addy = sectionInfo(index).top + st::stickersTrendingAddTop;
auto addy = info.top + st::stickersTrendingAddTop;
return QRect(addx, addy, addw, addh);
}
@ -1460,10 +1451,14 @@ bool StickersListWidget::hasRemoveButton(int index) const {
}
QRect StickersListWidget::removeButtonRect(int index) const {
return removeButtonRect(sectionInfo(index));
}
QRect StickersListWidget::removeButtonRect(const SectionInfo &info) const {
auto buttonw = st::stickerPanRemoveSet.width;
auto buttonh = st::stickerPanRemoveSet.height;
auto buttonx = stickersRight() - buttonw;
auto buttony = sectionInfo(index).top + (st::emojiPanHeader - buttonh) / 2;
auto buttony = info.top + (st::emojiPanHeader - buttonh) / 2;
return QRect(buttonx, buttony, buttonw, buttonh);
}
@ -1692,7 +1687,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
} else if (auto button = std::get_if<OverButton>(&pressed)) {
Assert(button->section >= 0 && button->section < sets.size());
if (sets[button->section].externalLayout) {
installSet(sets[button->section].id);
_localSetsManager->install(sets[button->section].id);
} else if (sets[button->section].id == Data::Stickers::MegagroupSetId) {
auto removeLocally = sets[button->section].stickers.empty()
|| !_megagroupSet->canEditStickers();
@ -1823,7 +1818,9 @@ void StickersListWidget::processHideFinished() {
}
void StickersListWidget::processPanelHideFinished() {
clearInstalledLocally();
if (_localSetsManager->clearInstalledLocally()) {
refreshStickers();
}
clearHeavyData();
if (_footer) {
_footer->clearHeavyData();
@ -1925,7 +1922,7 @@ void StickersListWidget::refreshFeaturedSets() {
if (it == sets.cend()
|| ((it->second->flags & SetFlag::Installed)
&& !(it->second->flags & SetFlag::Archived)
&& !_installedLocallySets.contains(set.id))) {
&& !_localSetsManager->isInstalledLocally(set.id))) {
continue;
}
set.flags = it->second->flags;
@ -1949,7 +1946,7 @@ void StickersListWidget::refreshSearchSets() {
entry.stickers = std::move(elements);
}
if (!SetInMyList(entry.flags)) {
_installedLocallySets.remove(entry.id);
_localSetsManager->removeInstalledLocally(entry.id);
entry.externalLayout = true;
}
}
@ -2017,7 +2014,7 @@ bool StickersListWidget::appendSet(
if ((skip == AppendSkip::Installed)
&& (set->flags & SetFlag::Installed)
&& !(set->flags & SetFlag::Archived)) {
if (!_installedLocallySets.contains(setId)) {
if (!_localSetsManager->isInstalledLocally(setId)) {
return false;
}
}
@ -2379,22 +2376,22 @@ void StickersListWidget::updateSelected() {
auto info = sectionInfoByOffset(p.y());
auto section = info.section;
if (p.y() >= info.top && p.y() < info.rowsTop) {
if (hasRemoveButton(section) && myrtlrect(removeButtonRect(section)).contains(p.x(), p.y())) {
newSelected = OverButton { section };
} else if (featuredHasAddButton(section) && myrtlrect(featuredAddRect(section)).contains(p.x(), p.y())) {
if (hasRemoveButton(section) && myrtlrect(removeButtonRect(info)).contains(p.x(), p.y())) {
newSelected = OverButton{ section };
} else if (featuredHasAddButton(section) && myrtlrect(featuredAddRect(info)).contains(p.x(), p.y())) {
newSelected = OverButton{ section };
} else if (!(sets[section].flags & SetFlag::Special)) {
newSelected = OverSet { section };
newSelected = OverSet{ section };
} else if (sets[section].id == Data::Stickers::MegagroupSetId
&& (_megagroupSet->canEditStickers() || !sets[section].stickers.empty())) {
newSelected = OverSet { section };
newSelected = OverSet{ section };
}
} else if (p.y() >= info.rowsTop && p.y() < info.rowsBottom && sx >= 0) {
auto yOffset = p.y() - info.rowsTop;
auto &set = sets[section];
if (set.id == Data::Stickers::MegagroupSetId && set.stickers.empty()) {
if (_megagroupSetButtonRect.contains(stickersLeft() + sx, yOffset)) {
newSelected = OverGroupAdd {};
newSelected = OverGroupAdd{};
}
} else {
auto rowIndex = qFloor(yOffset / _singleSize.height());
@ -2616,50 +2613,6 @@ void StickersListWidget::displaySet(uint64 setId) {
}
}
void StickersListWidget::installSet(uint64 setId) {
const auto &sets = session().data().stickers().sets();
const auto it = sets.find(setId);
if (it != sets.cend()) {
const auto set = it->second.get();
const auto input = set->mtpInput();
if ((set->flags & SetFlag::NotLoaded) || set->stickers.empty()) {
_api.request(MTPmessages_GetStickerSet(
input,
MTP_int(0) // hash
)).done([=](const MTPmessages_StickerSet &result) {
result.match([&](const MTPDmessages_stickerSet &data) {
session().data().stickers().feedSetFull(data);
}, [](const MTPDmessages_stickerSetNotModified &) {
LOG(("API Error: Unexpected messages.stickerSetNotModified."));
});
sendInstallRequest(setId, input);
}).send();
} else {
sendInstallRequest(setId, input);
}
}
}
void StickersListWidget::sendInstallRequest(
uint64 setId,
const MTPInputStickerSet &input) {
_api.request(MTPmessages_InstallStickerSet(
input,
MTP_bool(false)
)).done([=](const MTPmessages_StickerSetInstallResult &result) {
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
session().data().stickers().applyArchivedResult(
result.c_messages_stickerSetInstallResultArchive());
}
}).fail([=] {
notInstalledLocally(setId);
session().data().stickers().undoInstallLocally(setId);
}).send();
installedLocally(setId);
session().data().stickers().installLocally(setId);
}
void StickersListWidget::removeMegagroupSet(bool locally) {
if (locally) {
session().settings().setGroupStickersSectionHidden(_megagroupSet->id);
@ -2693,15 +2646,15 @@ void StickersListWidget::removeSet(uint64 setId) {
}
const Data::StickersSetsOrder &StickersListWidget::defaultSetsOrder() const {
return !_isMasks
? session().data().stickers().setsOrder()
: session().data().stickers().maskSetsOrder();
return _isMasks
? session().data().stickers().maskSetsOrder()
: session().data().stickers().setsOrder();
}
Data::StickersSetsOrder &StickersListWidget::defaultSetsOrderRef() {
return !_isMasks
? session().data().stickers().setsOrderRef()
: session().data().stickers().maskSetsOrderRef();
return _isMasks
? session().data().stickers().maskSetsOrderRef()
: session().data().stickers().setsOrderRef();
}
bool StickersListWidget::mySetsEmpty() const {
@ -2762,9 +2715,9 @@ object_ptr<Ui::BoxContent> MakeConfirmRemoveSetBox(
// && !(set->flags & SetFlag::Special)) {
// sets.erase(it);
//}
auto &orderRef = (set->flags & SetFlag::Emoji)
auto &orderRef = (set->type() == Data::StickersType::Emoji)
? session->data().stickers().emojiSetsOrderRef()
: (set->flags & SetFlag::Masks)
: (set->type() == Data::StickersType::Masks)
? session->data().stickers().maskSetsOrderRef()
: session->data().stickers().setsOrderRef();
const auto removeIndex = orderRef.indexOf(setId);

View File

@ -49,6 +49,7 @@ namespace ChatHelpers {
struct StickerIcon;
enum class ValidateIconAnimations;
class StickersListFooter;
class LocalStickersManager;
class StickersListWidget final : public TabbedSelector::Inner {
public:
@ -78,10 +79,6 @@ public:
uint64 currentSet(int yOffset) const;
void installedLocally(uint64 setId);
void notInstalledLocally(uint64 setId);
void clearInstalledLocally();
void sendSearchRequest();
void searchForSets(const QString &query);
@ -202,12 +199,8 @@ private:
void setSection(Section section);
void displaySet(uint64 setId);
void installSet(uint64 setId);
void removeMegagroupSet(bool locally);
void removeSet(uint64 setId);
void sendInstallRequest(
uint64 setId,
const MTPInputStickerSet &input);
void refreshMySets();
void refreshFeaturedSets();
void refreshSearchSets();
@ -277,14 +270,16 @@ private:
const SectionInfo &info,
crl::time now);
int stickersRight() const;
bool featuredHasAddButton(int index) const;
QRect featuredAddRect(int index) const;
bool hasRemoveButton(int index) const;
QRect removeButtonRect(int index) const;
int megagroupSetInfoLeft() const;
[[nodiscard]] int stickersRight() const;
[[nodiscard]] bool featuredHasAddButton(int index) const;
[[nodiscard]] QRect featuredAddRect(int index) const;
[[nodiscard]] QRect featuredAddRect(const SectionInfo &info) const;
[[nodiscard]] bool hasRemoveButton(int index) const;
[[nodiscard]] QRect removeButtonRect(int index) const;
[[nodiscard]] QRect removeButtonRect(const SectionInfo &info) const;
[[nodiscard]] int megagroupSetInfoLeft() const;
void refreshMegagroupSetGeometry();
QRect megagroupSetButtonRectFinal() const;
[[nodiscard]] QRect megagroupSetButtonRectFinal() const;
[[nodiscard]] const Data::StickersSetsOrder &defaultSetsOrder() const;
[[nodiscard]] Data::StickersSetsOrder &defaultSetsOrderRef();
@ -332,6 +327,7 @@ private:
not_null<DocumentData*> document);
MTP::Sender _api;
std::unique_ptr<LocalStickersManager> _localSetsManager;
ChannelData *_megagroupSet = nullptr;
uint64 _megagroupSetIdRequested = 0;
std::vector<Set> _mySets;
@ -339,7 +335,6 @@ private:
std::vector<Set> _searchSets;
int _premiumsIndex = -1;
int _featuredSetsCount = 0;
base::flat_set<uint64> _installedLocallySets;
std::vector<bool> _custom;
base::flat_set<not_null<DocumentData*>> _favedStickersMap;
std::weak_ptr<Lottie::FrameRenderer> _lottieRenderer;

View File

@ -356,9 +356,12 @@ void Stickers::applyArchivedResult(
if (set->flags & SetFlag::NotLoaded) {
setsToRequest.insert(set->id, set->accessHash);
}
const auto masks = !!(set->flags & SetFlag::Masks);
(masks ? masksCount : stickersCount)++;
auto &order = masks ? maskSetsOrderRef() : setsOrderRef();
if (set->type() == StickersType::Emoji) {
continue;
}
const auto isMasks = (set->type() == StickersType::Masks);
(isMasks ? masksCount : stickersCount)++;
auto &order = isMasks ? maskSetsOrderRef() : setsOrderRef();
const auto index = order.indexOf(set->id);
if (index >= 0) {
order.removeAt(index);
@ -692,12 +695,7 @@ void Stickers::somethingReceived(
QMap<uint64, uint64> setsToRequest;
for (auto &[id, set] : sets) {
const auto archived = !!(set->flags & SetFlag::Archived);
const auto setType = !!(set->flags & SetFlag::Emoji)
? StickersType::Emoji
: !!(set->flags & SetFlag::Masks)
? StickersType::Masks
: StickersType::Stickers;
if (!archived && (type == setType)) {
if (!archived && (type == set->type())) {
// Mark for removing.
set->flags &= ~SetFlag::Installed;
set->installDate = 0;
@ -964,10 +962,10 @@ void Stickers::featuredReceived(
};
const auto isEmoji = (type == StickersType::Emoji);
auto &setsOrder = isEmoji
auto &featuredOrder = isEmoji
? featuredEmojiSetsOrderRef()
: featuredSetsOrderRef();
setsOrder.clear();
featuredOrder.clear();
auto &sets = setsRef();
auto setsToRequest = base::flat_map<uint64, uint64>();
@ -1000,15 +998,14 @@ void Stickers::featuredReceived(
}
return ImageWithLocation();
}();
const auto setId = data->vid().v;
const auto flags = SetFlag::Featured
| (unreadMap.contains(data->vid().v)
? SetFlag::Unread
: SetFlag())
| (unreadMap.contains(setId) ? SetFlag::Unread : SetFlag())
| ParseStickersSetFlags(*data);
if (it == sets.cend()) {
it = sets.emplace(data->vid().v, std::make_unique<StickersSet>(
&owner(),
data->vid().v,
setId,
data->vaccess_hash().v,
data->vhash().v,
title,
@ -1032,7 +1029,7 @@ void Stickers::featuredReceived(
}
it->second->setThumbnail(thumbnail);
it->second->thumbnailDocumentId = data->vthumb_document_id().value_or_empty();
setsOrder.push_back(data->vid().v);
featuredOrder.push_back(data->vid().v);
if (it->second->stickers.isEmpty()
|| (it->second->flags & SetFlag::NotLoaded)) {
setsToRequest.emplace(data->vid().v, data->vaccess_hash().v);
@ -1361,8 +1358,8 @@ not_null<StickersSet*> Stickers::feedSet(const MTPStickerSet &info) {
set->thumbnailDocumentId = data.vthumb_document_id().value_or_empty();
auto changedFlags = (oldFlags ^ set->flags);
if (changedFlags & SetFlag::Archived) {
const auto masks = !!(set->flags & SetFlag::Masks);
auto &archivedOrder = masks
const auto isMasks = (set->type() == StickersType::Masks);
auto &archivedOrder = isMasks
? archivedMaskSetsOrderRef()
: archivedSetsOrderRef();
const auto index = archivedOrder.indexOf(set->id);

View File

@ -403,7 +403,7 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) {
_invoice.cover.title = qs(data.vtitle());
_invoice.cover.description = TextUtilities::ParseEntities(
qs(data.vdescription()),
TextParseLinks | TextParseMultiline)
TextParseLinks | TextParseMultiline);
if (_invoice.cover.thumbnail.isNull() && !_thumbnailLoadProcess) {
if (const auto photo = data.vphoto()) {
loadThumbnail(
@ -435,7 +435,7 @@ void Form::processDetails(const MTPDpayments_paymentReceipt &data) {
.providerId = data.vprovider_id().v,
};
if (_invoice.cover.title.isEmpty()
&& _invoice.cover.description.isEmpty()
&& _invoice.cover.description.empty()
&& _invoice.cover.thumbnail.isNull()
&& !_thumbnailLoadProcess) {
_invoice.cover = Ui::Cover{

View File

@ -2019,9 +2019,8 @@ void Account::writeInstalledStickers() {
return StickerSetCheckResult::Skip;
}
} else if (!(set.flags & SetFlag::Installed)
|| (set.flags & SetFlag::Archived)) {
return StickerSetCheckResult::Skip;
} else if (set.flags & (SetFlag::Masks | SetFlag::Emoji)) {
|| (set.flags & SetFlag::Archived)
|| (set.type() != Data::StickersType::Stickers)) {
return StickerSetCheckResult::Skip;
} else if (set.flags & SetFlag::NotLoaded) {
// waiting to receive
@ -2042,9 +2041,9 @@ void Account::writeFeaturedStickers() {
|| set.id == Data::Stickers::CloudRecentAttachedSetId) {
// separate files for them
return StickerSetCheckResult::Skip;
} else if (set.flags & (SetFlag::Special | SetFlag::Emoji)) {
return StickerSetCheckResult::Skip;
} else if (!(set.flags & SetFlag::Featured)) {
} else if ((set.flags & SetFlag::Special)
|| !(set.flags & SetFlag::Featured)
|| (set.type() != Data::StickersType::Stickers)) {
return StickerSetCheckResult::Skip;
} else if (set.flags & SetFlag::NotLoaded) { // waiting to receive
return StickerSetCheckResult::Abort;
@ -2059,9 +2058,8 @@ void Account::writeFeaturedCustomEmoji() {
using SetFlag = Data::StickersSetFlag;
writeStickerSets(_featuredCustomEmojiKey, [](const Data::StickersSet &set) {
if (!(set.flags & SetFlag::Emoji)) {
return StickerSetCheckResult::Skip;
} else if (!(set.flags & SetFlag::Featured)) {
if (!(set.flags & SetFlag::Featured)
|| (set.type() != Data::StickersType::Emoji)) {
return StickerSetCheckResult::Skip;
} else if (set.flags & SetFlag::NotLoaded) { // waiting to receive
return StickerSetCheckResult::Abort;
@ -2095,10 +2093,8 @@ void Account::writeArchivedStickers() {
using SetFlag = Data::StickersSetFlag;
writeStickerSets(_archivedStickersKey, [](const Data::StickersSet &set) {
if (set.flags & (SetFlag::Masks | SetFlag::Emoji)) {
return StickerSetCheckResult::Skip;
}
if (!(set.flags & SetFlag::Archived)
|| (set.type() != Data::StickersType::Stickers)
|| set.stickers.isEmpty()) {
return StickerSetCheckResult::Skip;
}
@ -2110,10 +2106,9 @@ void Account::writeArchivedMasks() {
using SetFlag = Data::StickersSetFlag;
writeStickerSets(_archivedStickersKey, [](const Data::StickersSet &set) {
if (!(set.flags & SetFlag::Masks)) {
return StickerSetCheckResult::Skip;
}
if (!(set.flags & SetFlag::Archived) || set.stickers.isEmpty()) {
if (!(set.flags & SetFlag::Archived)
|| (set.type() != Data::StickersType::Masks)
|| set.stickers.isEmpty()) {
return StickerSetCheckResult::Skip;
}
return StickerSetCheckResult::Write;
@ -2124,7 +2119,10 @@ void Account::writeInstalledMasks() {
using SetFlag = Data::StickersSetFlag;
writeStickerSets(_installedMasksKey, [](const Data::StickersSet &set) {
if (!(set.flags & SetFlag::Masks) || set.stickers.isEmpty()) {
if (!(set.flags & SetFlag::Installed)
|| (set.flags & SetFlag::Archived)
|| (set.type() != Data::StickersType::Masks)
|| set.stickers.isEmpty()) {
return StickerSetCheckResult::Skip;
}
return StickerSetCheckResult::Write;
@ -2145,14 +2143,14 @@ void Account::writeInstalledCustomEmoji() {
using SetFlag = Data::StickersSetFlag;
writeStickerSets(_installedCustomEmojiKey, [](const Data::StickersSet &set) {
if (!(set.flags & SetFlag::Emoji)) {
if (!(set.flags & SetFlag::Installed)
|| (set.flags & SetFlag::Archived)
|| (set.type() != Data::StickersType::Emoji)) {
return StickerSetCheckResult::Skip;
} else if (set.flags & SetFlag::NotLoaded) {
// waiting to receive
return StickerSetCheckResult::Abort;
} else if (!(set.flags & SetFlag::Installed)
|| (set.flags & SetFlag::Archived)
|| set.stickers.isEmpty()) {
} else if (set.stickers.isEmpty()) {
return StickerSetCheckResult::Skip;
}
return StickerSetCheckResult::Write;