Allow setting channel wallpaper.

This commit is contained in:
John Preston 2023-12-22 00:17:44 -04:00
parent 941126ad69
commit fd64718502
10 changed files with 420 additions and 164 deletions

View file

@ -591,6 +591,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_section_background" = "Chat background";
"lng_settings_bg_from_gallery" = "Choose from gallery";
"lng_settings_bg_from_file" = "Choose from file";
"lng_settings_bg_remove" = "Remove wallpaper";
"lng_settings_bg_theme_edit" = "Edit theme";
"lng_settings_bg_theme_create" = "Create new theme";
"lng_settings_bg_cloud_themes" = "Custom themes";
@ -838,6 +839,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_background_blur" = "Blurred";
"lng_background_sure_delete" = "Are you sure you want to delete this background?";
"lng_background_other_info" = "{user} will be able to apply this wallpaper";
"lng_background_other_channel" = "All subscribers will see this wallpaper";
"lng_background_apply1" = "Apply the wallpaper in this chat.";
"lng_background_apply2" = "Enjoy the view.";
"lng_background_apply_button" = "Apply For This Chat";
@ -846,6 +848,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_background_reset_default" = "Reset";
"lng_background_apply_me" = "Apply for me";
"lng_background_apply_both" = "Apply for me and {user}";
"lng_background_apply_channel" = "Apply For Channel";
"lng_download_path_ask" = "Ask download path for each file";
"lng_download_path" = "Download path";

View file

@ -129,6 +129,8 @@ private:
int row) const;
void validatePaperThumbnail(const Paper &paper) const;
[[nodiscard]] bool forChannel() const;
const not_null<Main::Session*> _session;
PeerData * const _forPeer = nullptr;
@ -176,6 +178,23 @@ void BackgroundBox::prepare() {
st::infoIconMediaPhoto,
st::infoSharedMediaButtonIconPosition);
if (forChannel() && _forPeer->wallPaper()) {
const auto remove = container->add(object_ptr<Ui::SettingsButton>(
container,
tr::lng_settings_bg_remove(),
st::infoBlockButton));
object_ptr<Info::Profile::FloatingIcon>(
remove,
st::infoIconDeleteRed,
st::infoSharedMediaButtonIconPosition);
remove->setClickedCallback([=] {
if (const auto resolved = _inner->resolveResetCustomPaper()) {
chosen(*resolved);
}
});
}
button->setClickedCallback([=] {
chooseFromFile();
});
@ -290,6 +309,23 @@ void BackgroundBox::chosen(const Data::WallPaper &paper) {
closeBox();
}
return;
} else if (forChannel()) {
if (_forPeer->wallPaper() && _forPeer->wallPaper()->equals(paper)) {
closeBox();
return;
}
const auto &themes = _forPeer->owner().cloudThemes();
for (const auto &theme : themes.chatThemes()) {
for (const auto &[type, themed] : theme.settings) {
if (themed.paper && themed.paper->equals(paper)) {
_controller->show(Box<BackgroundPreviewBox>(
_controller,
Data::WallPaper::FromEmojiId(theme.emoticon),
BackgroundPreviewArgs{ _forPeer }));
return;
}
}
}
}
_controller->show(Box<BackgroundPreviewBox>(
_controller,
@ -316,6 +352,10 @@ void BackgroundBox::resetForPeer() {
}
}
bool BackgroundBox::forChannel() const {
return _forPeer && _forPeer->isChannel();
}
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
const auto session = &_controller->session();
const auto remove = [=, weak = Ui::MakeWeak(this)](Fn<void()> &&close) {
@ -345,9 +385,16 @@ BackgroundBox::Inner::Inner(
, _session(session)
, _forPeer(forPeer)
, _api(&_session->mtp())
, _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [=] { update(); })) {
, _check(
std::make_unique<Ui::RoundCheckbox>(
st::overviewCheck,
[=] { update(); })) {
_check->setChecked(true, anim::type::instant);
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
resize(
st::boxWideWidth,
(2 * (st::backgroundSize.height() + st::backgroundPadding)
+ st::backgroundPadding));
Window::Theme::IsNightModeValue(
) | rpl::start_with_next([=] {
updatePapers();
@ -364,21 +411,31 @@ BackgroundBox::Inner::Inner(
_check->invalidateCache();
}, lifetime());
using Update = Window::Theme::BackgroundUpdate;
Window::Theme::Background()->updates(
) | rpl::start_with_next([=](const Update &update) {
if (update.type == Update::Type::New) {
sortPapers();
requestPapers();
this->update();
}
}, lifetime());
if (forChannel()) {
_session->data().cloudThemes().chatThemesUpdated(
) | rpl::start_with_next([=] {
updatePapers();
}, lifetime());
} else {
using Update = Window::Theme::BackgroundUpdate;
Window::Theme::Background()->updates(
) | rpl::start_with_next([=](const Update &update) {
if (update.type == Update::Type::New) {
sortPapers();
requestPapers();
this->update();
}
}, lifetime());
}
setMouseTracking(true);
}
void BackgroundBox::Inner::requestPapers() {
if (forChannel()) {
_session->data().cloudThemes().refreshChatThemes();
return;
}
_api.request(MTPaccount_GetWallPapers(
MTP_long(_session->data().wallpapersHash())
)).done([=](const MTPaccount_WallPapers &result) {
@ -395,7 +452,7 @@ auto BackgroundBox::Inner::resolveResetCustomPaper() const
}
const auto nonCustom = Window::Theme::Background()->paper();
const auto themeEmoji = _forPeer->themeEmoji();
if (themeEmoji.isEmpty()) {
if (forChannel() || themeEmoji.isEmpty()) {
return nonCustom;
}
const auto &themes = _forPeer->owner().cloudThemes();
@ -443,6 +500,8 @@ void BackgroundBox::Inner::pushCustomPapers() {
}
void BackgroundBox::Inner::sortPapers() {
Expects(!forChannel());
const auto currentCustom = _forPeer ? _forPeer->wallPaper() : nullptr;
_currentId = currentCustom
? currentCustom->id()
@ -472,23 +531,60 @@ void BackgroundBox::Inner::sortPapers() {
}
void BackgroundBox::Inner::updatePapers() {
if (_session->data().wallpapers().empty()) {
return;
if (forChannel()) {
if (_session->data().cloudThemes().chatThemes().empty()) {
return;
}
} else {
if (_session->data().wallpapers().empty()) {
return;
}
}
_over = _overDown = Selection();
_papers = _session->data().wallpapers(
) | ranges::views::filter([&](const Data::WallPaper &paper) {
return (!paper.isPattern() || !paper.backgroundColors().empty())
&& (!_forPeer
|| (!Data::IsDefaultWallPaper(paper)
&& (Data::IsCloudWallPaper(paper)
|| Data::IsCustomWallPaper(paper))));
}) | ranges::views::transform([](const Data::WallPaper &paper) {
return Paper{ paper };
}) | ranges::to_vector;
pushCustomPapers();
sortPapers();
const auto was = base::take(_papers);
if (forChannel()) {
const auto now = _forPeer->wallPaper();
const auto &list = _session->data().cloudThemes().chatThemes();
if (list.empty()) {
return;
}
using Type = Data::CloudThemeType;
const auto type = Window::Theme::IsNightMode()
? Type::Dark
: Type::Light;
_papers.reserve(list.size() + 1);
const auto nowEmojiId = now ? now->emojiId() : QString();
if (!now || !now->emojiId().isEmpty()) {
_papers.push_back({ Window::Theme::Background()->paper() });
_currentId = _papers.back().data.id();
} else {
_papers.push_back({ *now });
_currentId = now->id();
}
for (const auto &theme : list) {
const auto i = theme.settings.find(type);
if (i != end(theme.settings) && i->second.paper) {
_papers.push_back({ *i->second.paper });
if (nowEmojiId == theme.emoticon) {
_currentId = _papers.back().data.id();
}
}
}
} else {
_papers = _session->data().wallpapers(
) | ranges::views::filter([&](const Data::WallPaper &paper) {
return (!paper.isPattern() || !paper.backgroundColors().empty())
&& (!_forPeer
|| (!Data::IsDefaultWallPaper(paper)
&& (Data::IsCloudWallPaper(paper)
|| Data::IsCustomWallPaper(paper))));
}) | ranges::views::transform([](const Data::WallPaper &paper) {
return Paper{ paper };
}) | ranges::to_vector;
pushCustomPapers();
sortPapers();
}
resizeToContentAndPreload();
}
@ -587,6 +683,10 @@ void BackgroundBox::Inner::validatePaperThumbnail(
paper.thumbnail.setDevicePixelRatio(cRetinaFactor());
}
bool BackgroundBox::Inner::forChannel() const {
return _forPeer && _forPeer->isChannel();
}
void BackgroundBox::Inner::paintPaper(
QPainter &p,
const Paper &paper,
@ -604,7 +704,8 @@ void BackgroundBox::Inner::paintPaper(
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
_check->paint(p, checkLeft, checkTop, width());
} else if (Data::IsCloudWallPaper(paper.data)
} else if (!forChannel()
&& Data::IsCloudWallPaper(paper.data)
&& !Data::IsDefaultWallPaper(paper.data)
&& !Data::IsLegacy2DefaultWallPaper(paper.data)
&& !Data::IsLegacy3DefaultWallPaper(paper.data)
@ -642,7 +743,8 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
- st::stickerPanDeleteIconBg.width();
const auto deleteBottom = row * (height + skip) + skip
+ st::stickerPanDeleteIconBg.height();
const auto inDelete = (x >= deleteLeft)
const auto inDelete = !forChannel()
&& (x >= deleteLeft)
&& (y < deleteBottom)
&& Data::IsCloudWallPaper(data)
&& !Data::IsDefaultWallPaper(data)

View file

@ -38,6 +38,7 @@ private:
const Data::WallPaper &paper) const;
void removePaper(const Data::WallPaper &paper);
void resetForPeer();
[[nodiscard]] bool forChannel() const;
void chooseFromFile();

View file

@ -8,11 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/background_preview_box.h"
#include "base/unixtime.h"
#include "boxes/peers/edit_peer_color_box.h"
#include "boxes/premium_preview_box.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "window/themes/window_theme.h"
#include "ui/boxes/confirm_box.h"
#include "ui/boxes/boost_box.h"
#include "ui/controls/chat_service_checkbox.h"
#include "ui/chat/chat_theme.h"
#include "ui/chat/chat_style.h"
@ -29,6 +31,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "history/history_item_helpers.h"
#include "history/view/history_view_message.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "data/data_session.h"
@ -159,6 +163,25 @@ constexpr auto kDefaultDimming = 50;
return result;
}
[[nodiscard]] Data::WallPaper Resolve(
not_null<Main::Session*> session,
const Data::WallPaper &paper,
bool dark) {
if (paper.emojiId().isEmpty()) {
return paper;
}
const auto &themes = session->data().cloudThemes();
if (const auto theme = themes.themeForEmoji(paper.emojiId())) {
using Type = Data::CloudThemeType;
const auto type = dark ? Type::Dark : Type::Light;
const auto i = theme->settings.find(type);
if (i != end(theme->settings) && i->second.paper) {
return *i->second.paper;
}
}
return paper;
}
} // namespace
struct BackgroundPreviewBox::OverridenStyle {
@ -196,15 +219,17 @@ BackgroundPreviewBox::BackgroundPreviewBox(
? tr::lng_background_apply2(tr::now)
: tr::lng_background_text2(tr::now)),
true))
, _paper(paper)
, _paperEmojiId(paper.emojiId())
, _paper(
Resolve(&controller->session(), paper, Window::Theme::IsNightMode()))
, _media(_paper.document() ? _paper.document()->createMediaView() : nullptr)
, _radial([=](crl::time now) { radialAnimationCallback(now); })
, _appNightMode(Window::Theme::IsNightModeValue())
, _boxDarkMode(_appNightMode.current())
, _dimmingIntensity(std::clamp(paper.patternIntensity(), 0, 100))
, _dimmingIntensity(std::clamp(_paper.patternIntensity(), 0, 100))
, _dimmed(_forPeer
&& (paper.document() || paper.localThumbnail())
&& !paper.isPattern()) {
&& (_paper.document() || _paper.localThumbnail())
&& !_paper.isPattern()) {
if (_media) {
_media->thumbnailWanted(_paper.fileOrigin());
}
@ -244,7 +269,36 @@ BackgroundPreviewBox::BackgroundPreviewBox(
BackgroundPreviewBox::~BackgroundPreviewBox() = default;
void BackgroundPreviewBox::recreate(bool dark) {
_paper = Resolve(
&_controller->session(),
Data::WallPaper::FromEmojiId(_paperEmojiId),
dark);
_media = _paper.document()
? _paper.document()->createMediaView()
: nullptr;
if (_media) {
_media->thumbnailWanted(_paper.fileOrigin());
}
_full = QImage();
_generated = _scaled = _blurred = _fadeOutThumbnail = QPixmap();
_generating = {};
generateBackground();
_paper.loadDocument();
if (const auto document = _paper.document()) {
if (document->loading()) {
_radial.start(_media->progress());
}
}
checkLoadedDocument();
updateServiceBg(_paper.backgroundColors());
update();
}
void BackgroundPreviewBox::applyDarkMode(bool dark) {
if (!_paperEmojiId.isEmpty()) {
recreate(dark);
}
const auto equals = (dark == Window::Theme::IsNightMode());
const auto &palette = (dark ? _darkPalette : _lightPalette);
if (!equals && !palette) {
@ -410,6 +464,10 @@ auto BackgroundPreviewBox::prepareOverridenStyle(bool dark)
return result;
}
bool BackgroundPreviewBox::forChannel() const {
return _forPeer && _forPeer->isChannel();
}
void BackgroundPreviewBox::generateBackground() {
if (_paper.backgroundColors().empty()) {
return;
@ -435,7 +493,9 @@ void BackgroundPreviewBox::resetTitle() {
void BackgroundPreviewBox::rebuildButtons(bool dark) {
clearButtons();
addButton(_forPeer
addButton(forChannel()
? tr::lng_background_apply_channel()
: _forPeer
? tr::lng_background_apply_button()
: tr::lng_settings_apply(), [=] { apply(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
@ -624,6 +684,36 @@ void BackgroundPreviewBox::setExistingForPeer(
_controller->finishChatThemeEdit(_forPeer);
}
void BackgroundPreviewBox::checkLevelForChannel() {
Expects(forChannel());
const auto show = _controller->uiShow();
_forPeerLevelCheck = true;
const auto weak = Ui::MakeWeak(this);
CheckBoostLevel(show, _forPeer, [=](int level) {
if (!weak) {
return std::optional<Ui::AskBoostReason>();
}
const auto appConfig = &_forPeer->session().account().appConfig();
const auto defaultRequired = appConfig->get<int>(
"channel_wallpaper_level_min",
9);
const auto customRequired = appConfig->get<int>(
"channel_custom_wallpaper_level_min",
10);
const auto required = _paperEmojiId.isEmpty()
? customRequired
: defaultRequired;
if (level >= required) {
applyForPeer(false);
return std::optional<Ui::AskBoostReason>();
}
return std::make_optional(Ui::AskBoostReason{
Ui::AskBoostWallpaper{ required }
});
}, [=] { _forPeerLevelCheck = false; });
}
void BackgroundPreviewBox::applyForPeer() {
Expects(_forPeer != nullptr);
@ -636,105 +726,110 @@ void BackgroundPreviewBox::applyForPeer() {
}
}
if (!_fromMessageId && _forPeer->session().premiumPossible()) {
if (_forBothOverlay) {
return;
}
const auto size = this->size() * style::DevicePixelRatio();
const auto bg = Images::DitherImage(
Images::BlurLargeImage(
Ui::GrabWidgetToImage(this).scaled(
size / style::ConvertScale(4),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation),
24).scaled(
size,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation));
_forBothOverlay = std::make_unique<Ui::FadeWrap<>>(
this,
object_ptr<Ui::RpWidget>(this));
const auto overlay = _forBothOverlay->entity();
sizeValue() | rpl::start_with_next([=](QSize size) {
_forBothOverlay->setGeometry({ QPoint(), size });
overlay->setGeometry({ QPoint(), size });
}, _forBothOverlay->lifetime());
overlay->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
auto p = QPainter(overlay);
p.drawImage(0, 0, bg);
p.fillRect(clip, QColor(0, 0, 0, 64));
}, overlay->lifetime());
using namespace Ui;
const auto forMe = CreateChild<RoundButton>(
overlay,
tr::lng_background_apply_me(),
st::backgroundConfirm);
forMe->setClickedCallback([=] {
applyForPeer(false);
});
using namespace rpl::mappers;
const auto forBoth = ::Settings::CreateLockedButton(
overlay,
tr::lng_background_apply_both(
lt_user,
rpl::single(_forPeer->shortName())),
st::backgroundConfirm,
Data::AmPremiumValue(&_forPeer->session()) | rpl::map(!_1));
forBoth->setClickedCallback([=] {
if (_forPeer->session().premium()) {
applyForPeer(true);
} else {
ShowPremiumPreviewBox(
_controller->uiShow(),
PremiumPreview::Wallpapers);
}
});
const auto cancel = CreateChild<RoundButton>(
overlay,
tr::lng_cancel(),
st::backgroundConfirmCancel);
cancel->setClickedCallback([=] {
const auto raw = _forBothOverlay.release();
raw->shownValue() | rpl::filter(
!rpl::mappers::_1
) | rpl::take(1) | rpl::start_with_next(crl::guard(raw, [=] {
delete raw;
}), raw->lifetime());
raw->toggle(false, anim::type::normal);
});
forMe->setTextTransform(RoundButton::TextTransform::NoTransform);
forBoth->setTextTransform(RoundButton::TextTransform::NoTransform);
cancel->setTextTransform(RoundButton::TextTransform::NoTransform);
overlay->sizeValue(
) | rpl::start_with_next([=](QSize size) {
const auto padding = st::backgroundConfirmPadding;
const auto width = size.width()
- padding.left()
- padding.right();
const auto height = cancel->height();
auto top = size.height() - padding.bottom() - height;
cancel->setGeometry(padding.left(), top, width, height);
top -= height + padding.top();
forBoth->setGeometry(padding.left(), top, width, height);
top -= height + padding.top();
forMe->setGeometry(padding.left(), top, width, height);
}, _forBothOverlay->lifetime());
_forBothOverlay->hide(anim::type::instant);
_forBothOverlay->show(anim::type::normal);
} else {
if (forChannel()) {
checkLevelForChannel();
return;
} else if (_fromMessageId || !_forPeer->session().premiumPossible()) {
applyForPeer(false);
return;
} else if (_forBothOverlay) {
return;
}
const auto size = this->size() * style::DevicePixelRatio();
const auto bg = Images::DitherImage(
Images::BlurLargeImage(
Ui::GrabWidgetToImage(this).scaled(
size / style::ConvertScale(4),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation),
24).scaled(
size,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation));
_forBothOverlay = std::make_unique<Ui::FadeWrap<>>(
this,
object_ptr<Ui::RpWidget>(this));
const auto overlay = _forBothOverlay->entity();
sizeValue() | rpl::start_with_next([=](QSize size) {
_forBothOverlay->setGeometry({ QPoint(), size });
overlay->setGeometry({ QPoint(), size });
}, _forBothOverlay->lifetime());
overlay->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
auto p = QPainter(overlay);
p.drawImage(0, 0, bg);
p.fillRect(clip, QColor(0, 0, 0, 64));
}, overlay->lifetime());
using namespace Ui;
const auto forMe = CreateChild<RoundButton>(
overlay,
tr::lng_background_apply_me(),
st::backgroundConfirm);
forMe->setClickedCallback([=] {
applyForPeer(false);
});
using namespace rpl::mappers;
const auto forBoth = ::Settings::CreateLockedButton(
overlay,
tr::lng_background_apply_both(
lt_user,
rpl::single(_forPeer->shortName())),
st::backgroundConfirm,
Data::AmPremiumValue(&_forPeer->session()) | rpl::map(!_1));
forBoth->setClickedCallback([=] {
if (_forPeer->session().premium()) {
applyForPeer(true);
} else {
ShowPremiumPreviewBox(
_controller->uiShow(),
PremiumPreview::Wallpapers);
}
});
const auto cancel = CreateChild<RoundButton>(
overlay,
tr::lng_cancel(),
st::backgroundConfirmCancel);
cancel->setClickedCallback([=] {
const auto raw = _forBothOverlay.release();
raw->shownValue() | rpl::filter(
!rpl::mappers::_1
) | rpl::take(1) | rpl::start_with_next(crl::guard(raw, [=] {
delete raw;
}), raw->lifetime());
raw->toggle(false, anim::type::normal);
});
forMe->setTextTransform(RoundButton::TextTransform::NoTransform);
forBoth->setTextTransform(RoundButton::TextTransform::NoTransform);
cancel->setTextTransform(RoundButton::TextTransform::NoTransform);
overlay->sizeValue(
) | rpl::start_with_next([=](QSize size) {
const auto padding = st::backgroundConfirmPadding;
const auto width = size.width()
- padding.left()
- padding.right();
const auto height = cancel->height();
auto top = size.height() - padding.bottom() - height;
cancel->setGeometry(padding.left(), top, width, height);
top -= height + padding.top();
forBoth->setGeometry(padding.left(), top, width, height);
top -= height + padding.top();
forMe->setGeometry(padding.left(), top, width, height);
}, _forBothOverlay->lifetime());
_forBothOverlay->hide(anim::type::instant);
_forBothOverlay->show(anim::type::normal);
}
void BackgroundPreviewBox::applyForPeer(bool both) {
if (Data::IsCustomWallPaper(_paper)) {
using namespace Data;
if (forChannel() && !_paperEmojiId.isEmpty()) {
setExistingForPeer(WallPaper::FromEmojiId(_paperEmojiId), both);
} else if (IsCustomWallPaper(_paper)) {
uploadForPeer(both);
} else {
setExistingForPeer(_paper, both);
@ -855,7 +950,7 @@ int BackgroundPreviewBox::textsTop() const {
- st::historyPaddingBottom
- (_service ? _service->height() : 0)
- _text1->height()
- _text2->height();
- (forChannel() ? _text2->height() : 0);
}
QRect BackgroundPreviewBox::radialRect() const {
@ -885,10 +980,11 @@ void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
context.outbg = _text1->hasOutLayout();
_text1->draw(p, context);
p.translate(0, height1);
context.outbg = _text2->hasOutLayout();
_text2->draw(p, context);
p.translate(0, height2);
if (!forChannel()) {
context.outbg = _text2->hasOutLayout();
_text2->draw(p, context);
p.translate(0, height2);
}
}
void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
@ -988,7 +1084,9 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
_service = GenerateServiceItem(
delegate(),
_serviceHistory,
((_forPeer && !_fromMessageId)
(forChannel()
? tr::lng_background_other_channel(tr::now)
: (_forPeer && !_fromMessageId)
? tr::lng_background_other_info(
tr::now,
lt_user,

View file

@ -94,18 +94,24 @@ private:
void applyDarkMode(bool dark);
[[nodiscard]] OverridenStyle prepareOverridenStyle(bool dark);
[[nodiscard]] bool forChannel() const;
void checkLevelForChannel();
void recreate(bool dark);
void resetTitle();
void rebuildButtons(bool dark);
void createDimmingSlider(bool dark);
const not_null<Window::SessionController*> _controller;
PeerData * const _forPeer = nullptr;
bool _forPeerLevelCheck = false;
FullMsgId _fromMessageId;
std::unique_ptr<Ui::ChatStyle> _chatStyle;
const not_null<History*> _serviceHistory;
AdminLog::OwnedItem _service;
AdminLog::OwnedItem _text1;
AdminLog::OwnedItem _text2;
QString _paperEmojiId;
Data::WallPaper _paper;
std::shared_ptr<Data::DocumentMedia> _media;
QImage _full;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_peer_photo.h"
#include "base/unixtime.h"
#include "boxes/peers/replace_boost_box.h"
#include "boxes/background_box.h"
#include "chat_helpers/compose/compose_show.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
@ -524,13 +525,7 @@ void Apply(
Set(show, peer, values);
close();
} else {
session->api().request(MTPpremium_GetBoostsStatus(
peer->input
)).done([=](const MTPpremium_BoostsStatus &result) {
const auto &data = result.data();
if (const auto channel = peer->asChannel()) {
channel->updateLevelHint(data.vlevel().v);
}
CheckBoostLevel(show, peer, [=](int level) {
const auto peerColors = &peer->session().api().peerColors();
const auto colorRequired = peerColors->requiredLevelFor(
peer->id,
@ -551,38 +546,21 @@ void Apply(
iconRequired,
statusRequired,
});
const auto current = data.vlevel().v;
if (current >= required) {
if (level >= required) {
Set(show, peer, values);
close();
return;
return std::optional<Ui::AskBoostReason>();
}
const auto openStatistics = [=] {
if (const auto controller = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo)) {
controller->showSection(Info::Boosts::Make(peer));
}
};
auto counters = ParseBoostCounters(result);
counters.mine = 0; // Don't show current level as just-reached.
const auto reason = [&]() -> Ui::AskBoostReason {
if (current < statusRequired) {
if (level < statusRequired) {
return { Ui::AskBoostEmojiStatus{ statusRequired } };
} else if (current < iconRequired) {
} else if (level < iconRequired) {
return { Ui::AskBoostChannelColor{ iconRequired } };
}
return { Ui::AskBoostChannelColor{ colorRequired } };
}();
show->show(Box(Ui::AskBoostBox, Ui::AskBoostBoxData{
.link = qs(data.vboost_url()),
.boost = counters,
.reason = reason,
}, openStatistics, nullptr));
cancel();
}).fail([=](const MTP::Error &error) {
show->showToast(error.type());
cancel();
}).send();
return std::make_optional(reason);
}, cancel);
}
}
@ -978,6 +956,23 @@ void EditPeerColorBox(
: tr::lng_settings_color_emoji_about_channel());
if (const auto channel = peer->asChannel()) {
Ui::AddSkip(container, st::settingsColorSampleSkip);
container->add(object_ptr<Ui::SettingsButton>(
container,
tr::lng_edit_channel_wallpaper(),
st::settingsButtonNoIcon)
)->setClickedCallback([=] {
const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
if (const auto strong = show->resolveWindow(usage)) {
show->show(Box<BackgroundBox>(strong, channel));
}
});
Ui::AddSkip(container, st::settingsColorSampleSkip);
Ui::AddDividerText(
container,
tr::lng_edit_channel_wallpaper_about());
// Preload exceptions list.
const auto peerPhoto = &channel->session().api().peerPhoto();
[[maybe_unused]] auto list = peerPhoto->emojiListValue(
@ -1110,3 +1105,39 @@ void AddPeerColorButton(
show->show(Box(EditPeerColorBox, show, peer, style, theme));
});
}
void CheckBoostLevel(
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer,
Fn<std::optional<Ui::AskBoostReason>(int level)> askMore,
Fn<void()> cancel) {
peer->session().api().request(MTPpremium_GetBoostsStatus(
peer->input
)).done([=](const MTPpremium_BoostsStatus &result) {
const auto &data = result.data();
if (const auto channel = peer->asChannel()) {
channel->updateLevelHint(data.vlevel().v);
}
const auto reason = askMore(data.vlevel().v);
if (!reason) {
return;
}
const auto openStatistics = [=] {
if (const auto controller = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo)) {
controller->showSection(Info::Boosts::Make(peer));
}
};
auto counters = ParseBoostCounters(result);
counters.mine = 0; // Don't show current level as just-reached.
show->show(Box(Ui::AskBoostBox, Ui::AskBoostBoxData{
.link = qs(data.vboost_url()),
.boost = counters,
.reason = *reason,
}, openStatistics, nullptr));
cancel();
}).fail([=](const MTP::Error &error) {
show->showToast(error.type());
cancel();
}).send();
}

View file

@ -16,6 +16,7 @@ class GenericBox;
class ChatStyle;
class ChatTheme;
class VerticalLayout;
struct AskBoostReason;
} // namespace Ui
void EditPeerColorBox(
@ -29,3 +30,9 @@ void AddPeerColorButton(
not_null<Ui::VerticalLayout*> container,
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer);
void CheckBoostLevel(
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer,
Fn<std::optional<Ui::AskBoostReason>(int level)> askMore,
Fn<void()> cancel);

View file

@ -698,6 +698,12 @@ std::optional<WallPaper> WallPaper::FromColorsSlug(const QString &slug) {
return result;
}
WallPaper WallPaper::FromEmojiId(const QString &emojiId) {
auto result = WallPaper(0);
result._emojiId = emojiId;
return result;
}
WallPaper WallPaper::ConstructDefault() {
auto result = WallPaper(
kDefaultBackground

View file

@ -103,6 +103,7 @@ public:
qint32 legacyId);
[[nodiscard]] static std::optional<WallPaper> FromColorsSlug(
const QString &slug);
[[nodiscard]] static WallPaper FromEmojiId(const QString &emojiId);
[[nodiscard]] static WallPaper ConstructDefault();
private:

View file

@ -404,6 +404,7 @@ infoIconMediaStoriesRecent: icon {{ "info/info_stories_recent", infoIconFg }};
infoIconShare: icon {{ "info/info_share", infoIconFg }};
infoIconEdit: icon {{ "info/info_edit", infoIconFg }};
infoIconDelete: icon {{ "info/info_delete", infoIconFg }};
infoIconDeleteRed: icon {{ "info/info_delete", attentionButtonFg }};
infoIconReport: icon {{ "info/info_report", attentionButtonFg }};
infoIconLeave: icon {{ "info/info_leave", infoIconFg }};
infoIconBlock: icon {{ "info/info_block", attentionButtonFg }};