diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ea98e618d..8312074ad 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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"; diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index 0ae3023e0..877cf556e 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -129,6 +129,8 @@ private: int row) const; void validatePaperThumbnail(const Paper &paper) const; + [[nodiscard]] bool forChannel() const; + const not_null _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( + container, + tr::lng_settings_bg_remove(), + st::infoBlockButton)); + object_ptr( + 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( + _controller, + Data::WallPaper::FromEmojiId(theme.emoticon), + BackgroundPreviewArgs{ _forPeer })); + return; + } + } + } } _controller->show(Box( _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 &&close) { @@ -345,9 +385,16 @@ BackgroundBox::Inner::Inner( , _session(session) , _forPeer(forPeer) , _api(&_session->mtp()) -, _check(std::make_unique(st::overviewCheck, [=] { update(); })) { +, _check( + std::make_unique( + 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) diff --git a/Telegram/SourceFiles/boxes/background_box.h b/Telegram/SourceFiles/boxes/background_box.h index 3fe16d602..8aa63ff91 100644 --- a/Telegram/SourceFiles/boxes/background_box.h +++ b/Telegram/SourceFiles/boxes/background_box.h @@ -38,6 +38,7 @@ private: const Data::WallPaper &paper) const; void removePaper(const Data::WallPaper &paper); void resetForPeer(); + [[nodiscard]] bool forChannel() const; void chooseFromFile(); diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index c54f581ce..4f46697c1 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -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 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(); + } + const auto appConfig = &_forPeer->session().account().appConfig(); + const auto defaultRequired = appConfig->get( + "channel_wallpaper_level_min", + 9); + const auto customRequired = appConfig->get( + "channel_custom_wallpaper_level_min", + 10); + const auto required = _paperEmojiId.isEmpty() + ? customRequired + : defaultRequired; + if (level >= required) { + applyForPeer(false); + return std::optional(); + } + 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>( - this, - object_ptr(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( - 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( - 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>( + this, + object_ptr(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( + 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( + 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 &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, diff --git a/Telegram/SourceFiles/boxes/background_preview_box.h b/Telegram/SourceFiles/boxes/background_preview_box.h index 8c1bd4346..3eb06d1a5 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.h +++ b/Telegram/SourceFiles/boxes/background_preview_box.h @@ -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 _controller; PeerData * const _forPeer = nullptr; + bool _forPeerLevelCheck = false; FullMsgId _fromMessageId; std::unique_ptr _chatStyle; const not_null _serviceHistory; AdminLog::OwnedItem _service; AdminLog::OwnedItem _text1; AdminLog::OwnedItem _text2; + QString _paperEmojiId; Data::WallPaper _paper; std::shared_ptr _media; QImage _full; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp index 4ebee1962..d14a5cea3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp @@ -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(); } - 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( + 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(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 show, + not_null peer, + Fn(int level)> askMore, + Fn 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(); +} diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.h index 68aecaab2..6b7284449 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.h @@ -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 container, std::shared_ptr show, not_null peer); + +void CheckBoostLevel( + std::shared_ptr show, + not_null peer, + Fn(int level)> askMore, + Fn cancel); diff --git a/Telegram/SourceFiles/data/data_wall_paper.cpp b/Telegram/SourceFiles/data/data_wall_paper.cpp index 5b2ca28b5..1bfd65335 100644 --- a/Telegram/SourceFiles/data/data_wall_paper.cpp +++ b/Telegram/SourceFiles/data/data_wall_paper.cpp @@ -698,6 +698,12 @@ std::optional 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 diff --git a/Telegram/SourceFiles/data/data_wall_paper.h b/Telegram/SourceFiles/data/data_wall_paper.h index 93da268cd..e7bfd2d51 100644 --- a/Telegram/SourceFiles/data/data_wall_paper.h +++ b/Telegram/SourceFiles/data/data_wall_paper.h @@ -103,6 +103,7 @@ public: qint32 legacyId); [[nodiscard]] static std::optional FromColorsSlug( const QString &slug); + [[nodiscard]] static WallPaper FromEmojiId(const QString &emojiId); [[nodiscard]] static WallPaper ConstructDefault(); private: diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 2be1c9650..1ed7c486e 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -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 }};