From fa4e74ffef9e299f9a99eac00f75765234f80027 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2025 11:58:00 +0400 Subject: [PATCH] Show and edit channel gifts notify settings. --- Telegram/Resources/langs/lang.strings | 4 + Telegram/SourceFiles/data/data_peer.cpp | 7 + Telegram/SourceFiles/data/data_peer.h | 1 + Telegram/SourceFiles/history/history_item.cpp | 58 +++++--- .../SourceFiles/info/info_content_widget.cpp | 2 +- .../SourceFiles/info/info_content_widget.h | 3 +- .../peer_gifts/info_peer_gifts_widget.cpp | 136 +++++++++++++++--- .../info/peer_gifts/info_peer_gifts_widget.h | 15 ++ .../settings/settings_credits_graphics.cpp | 61 ++++---- 9 files changed, 217 insertions(+), 70 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d5bf92e78..026b82d49 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2025,9 +2025,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_gift_sent" = "You sent a gift for {cost}"; "lng_action_gift_unique_sent" = "You sent a unique collectible item"; "lng_action_gift_upgraded" = "{user} turned the gift from you into a unique collectible"; +"lng_action_gift_upgraded_channel" = "{user} turned this gift to {channel} into a unique collectible"; "lng_action_gift_upgraded_mine" = "You turned the gift from {user} into a unique collectible"; "lng_action_gift_upgraded_self" = "You turned this gift into a unique collectible"; "lng_action_gift_transferred" = "{user} transferred you a gift"; +"lng_action_gift_transferred_channel" = "{user} transferred a gift to {channel}"; "lng_action_gift_transferred_mine" = "You transferred a gift to {user}"; "lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}"; "lng_action_gift_sent_channel" = "{user} sent a gift to {name} for {cost}"; @@ -3323,6 +3325,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_convert_sure_title" = "Convert Gift to Stars"; "lng_gift_convert_sure_confirm#one" = "Do you want to convert this gift from {user} to **{count} Star**?"; "lng_gift_convert_sure_confirm#other" = "Do you want to convert this gift from {user} to **{count} Stars**?"; +"lng_gift_convert_sure_confirm_channel#one" = "Do you want to convert this gift to {channel} to **{count} Star**?"; +"lng_gift_convert_sure_confirm_channel#other" = "Do you want to convert this gift to {channel} to **{count} Stars**?"; "lng_gift_convert_sure_limit#one" = "Conversion is available for the next **{count} day**."; "lng_gift_convert_sure_limit#other" = "Conversion is available for the next **{count} days**."; "lng_gift_convert_sure_caution" = "This action cannot be undone. This will permanently destroy the gift."; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 9059994e0..b26234ee6 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -659,6 +659,13 @@ bool PeerData::canManageGifts() const { return isSelf(); } +bool PeerData::canTransferGifts() const { + if (const auto channel = asChannel()) { + return channel->amCreator(); + } + return isSelf(); +} + bool PeerData::canEditMessagesIndefinitely() const { if (const auto user = asUser()) { return user->isSelf(); diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 83174a5fc..89d5e7d18 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -385,6 +385,7 @@ public: [[nodiscard]] bool canCreateTopics() const; [[nodiscard]] bool canManageTopics() const; [[nodiscard]] bool canManageGifts() const; + [[nodiscard]] bool canTransferGifts() const; [[nodiscard]] bool canExportChatHistory() const; // Returns true if about text was changed. diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 33ea0b2e9..7abc084db 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -5456,7 +5456,6 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { const auto from = fromId ? peer->owner().peer(fromId) : peer; const auto channel = peer->owner().channel( peerToChannel(giftPeer)); - Assert(channel != nullptr); result.links.push_back(from->createOpenLink()); result.links.push_back(channel->createOpenLink()); result.text = tr::lng_action_gift_sent_channel( @@ -5500,23 +5499,48 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { const MTPDmessageActionStarGiftUnique &action) { auto result = PreparedServiceText(); const auto isSelf = _from->isSelf(); + const auto giftPeer = action.vpeer() + ? peerFromMTP(*action.vpeer()) + : PeerId(); + const auto service = _from->isServiceUser(); + const auto toChannel = service && peerIsChannel(giftPeer); const auto peer = isSelf ? _history->peer : _from; - result.links.push_back(peer->createOpenLink()); - result.text = _history->peer->isSelf() - ? tr::lng_action_gift_upgraded_self( - tr::now, - Ui::Text::WithEntities) - : (action.is_upgrade() - ? (isSelf - ? tr::lng_action_gift_upgraded_mine - : tr::lng_action_gift_upgraded) - : (isSelf - ? tr::lng_action_gift_transferred_mine - : tr::lng_action_gift_transferred))( - tr::now, - lt_user, - Ui::Text::Link(peer->shortName(), 1), // Link 1. - Ui::Text::WithEntities); + if (toChannel) { + const auto fromId = action.vfrom_id() + ? peerFromMTP(*action.vfrom_id()) + : PeerId(); + const auto channel = peer->owner().channel( + peerToChannel(giftPeer)); + const auto from = fromId ? peer->owner().peer(fromId) : peer; + result.links.push_back(from->createOpenLink()); + result.links.push_back(channel->createOpenLink()); + result.text = (action.is_upgrade() + ? tr::lng_action_gift_upgraded_channel + : tr::lng_action_gift_transferred_channel)( + tr::now, + lt_user, + Ui::Text::Link(from->shortName(), 1), + lt_channel, + Ui::Text::Link(channel->name(), 2), + Ui::Text::WithEntities); + } else { + result.links.push_back(peer->createOpenLink()); + result.text = _history->peer->isSelf() + ? tr::lng_action_gift_upgraded_self( + tr::now, + Ui::Text::WithEntities) + : (action.is_upgrade() + ? (isSelf + ? tr::lng_action_gift_upgraded_mine + : tr::lng_action_gift_upgraded) + : (isSelf + ? tr::lng_action_gift_transferred_mine + : tr::lng_action_gift_transferred))( + tr::now, + lt_user, + Ui::Text::Link(peer->shortName(), 1), // Link 1. + Ui::Text::WithEntities); + } return result; }; diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index f1aeb8641..9187b0251 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -366,7 +366,7 @@ rpl::producer ContentWidget::scrollBottomSkipValue() const { return _scrollBottomSkip.value(); } -rpl::producer ContentWidget::desiredBottomShadowVisibility() const { +rpl::producer ContentWidget::desiredBottomShadowVisibility() { using namespace rpl::mappers; return rpl::combine( _scroll->scrollTopValue(), diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index e2e019b3c..0fafdfe01 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -131,7 +131,8 @@ public: [[nodiscard]] int scrollBottomSkip() const; [[nodiscard]] rpl::producer scrollBottomSkipValue() const; - [[nodiscard]] rpl::producer desiredBottomShadowVisibility() const; + [[nodiscard]] virtual auto desiredBottomShadowVisibility() + -> rpl::producer; protected: template diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp index 027e8f8a4..427ba2ec9 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/peer_gifts/info_peer_gifts_widget.h" #include "api/api_premium.h" +#include "apiwrap.h" #include "data/data_channel.h" #include "data/data_session.h" #include "data/data_user.h" @@ -16,7 +17,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" #include "ui/widgets/box_content_divider.h" +#include "ui/widgets/checkbox.h" #include "ui/widgets/labels.h" +#include "ui/wrap/slide_wrap.h" #include "ui/ui_utility.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -24,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "settings/settings_credits_graphics.h" #include "styles/style_info.h" +#include "styles/style_layers.h" // boxRadius #include "styles/style_credits.h" // giftBoxPadding namespace Info::PeerGifts { @@ -58,6 +62,9 @@ public: [[nodiscard]] not_null peer() const { return _peer; } + [[nodiscard]] rpl::producer notifyEnabled() const { + return _notifyEnabled.events(); + } void saveState(not_null memento); void restoreState(not_null memento); @@ -98,6 +105,7 @@ private: QString _offset; bool _allLoaded = false; + rpl::event_stream _notifyEnabled; std::vector _views; int _viewsForWidth = 0; int _viewsFromRow = 0; @@ -119,22 +127,23 @@ InnerWidget::InnerWidget( , _window(controller->parentController()) , _delegate(_window, GiftButtonMode::Minimal) , _controller(controller) -, _about(std::make_unique( - this, - (peer->isSelf() - ? tr::lng_peer_gifts_about_mine(Ui::Text::RichLangValue) - : tr::lng_peer_gifts_about( - lt_user, - rpl::single(Ui::Text::Bold(peer->shortName())), - Ui::Text::RichLangValue)), - st::giftListAbout)) +, _about((!peer->isSelf() && peer->canManageGifts()) + ? nullptr + : std::make_unique( + this, + (peer->isSelf() + ? tr::lng_peer_gifts_about_mine(Ui::Text::RichLangValue) + : tr::lng_peer_gifts_about( + lt_user, + rpl::single(Ui::Text::Bold(peer->shortName())), + Ui::Text::RichLangValue)), + st::giftListAbout)) , _peer(peer) , _totalCount(_peer->peerGiftsCount()) , _api(&_peer->session().mtp()) { _singleMin = _delegate.buttonSize(); - const auto channel = peer->asBroadcast(); - if (peer->isSelf() || (channel && channel->canManageGifts())) { + if (peer->canManageGifts()) { subscribeToUpdates(); } } @@ -196,11 +205,15 @@ void InnerWidget::visibleTopBottomUpdated( void InnerWidget::paintEvent(QPaintEvent *e) { auto p = QPainter(this); - const auto aboutSize = _about->size().grownBy(st::giftListAboutMargin); + const auto aboutSize = _about + ? _about->size().grownBy(st::giftListAboutMargin) + : QSize(); const auto skips = QMargins(0, 0, 0, aboutSize.height()); p.fillRect(rect().marginsRemoved(skips), st::boxDividerBg->c); paintTop(p); - paintBottom(p, skips.bottom()); + if (const auto bottom = skips.bottom()) { + paintBottom(p, bottom); + } } void InnerWidget::loadMore() { @@ -208,8 +221,7 @@ void InnerWidget::loadMore() { return; } using Flag = MTPpayments_GetSavedStarGifts::Flag; - const auto withUnsaved = _peer->isSelf() - || (_peer->isChannel() && _peer->asChannel()->canManageGifts()); + const auto withUnsaved = _peer->canManageGifts(); _loadMoreRequestId = _api.request(MTPpayments_GetSavedStarGifts( MTP_flags(withUnsaved ? Flag() : Flag::f_exclude_unsaved), _peer->input, @@ -218,6 +230,9 @@ void InnerWidget::loadMore() { )).done([=](const MTPpayments_SavedStarGifts &result) { _loadMoreRequestId = 0; const auto &data = result.data(); + if (const auto enabled = data.vchat_notifications_enabled()) { + _notifyEnabled.fire(mtpIsTrue(*enabled)); + } if (const auto next = data.vnext_offset()) { _offset = qs(*next); } else { @@ -361,10 +376,12 @@ int InnerWidget::resizeGetHeight(int width) { auto result = padding.bottom() * 2 + rows * (singleh + skiph) - skiph; - const auto margin = st::giftListAboutMargin; - _about->resizeToWidth(width - margin.left() - margin.right()); - _about->moveToLeft(margin.left(), result + margin.top()); - result += margin.top() + _about->height() + margin.bottom(); + if (const auto about = _about.get()) { + const auto margin = st::giftListAboutMargin; + about->resizeToWidth(width - margin.left() - margin.right()); + about->moveToLeft(margin.left(), result + margin.top()); + result += margin.top() + about->height() + margin.bottom(); + } return result; } @@ -412,16 +429,91 @@ Widget::Widget( not_null controller, not_null peer) : ContentWidget(parent, controller) { - _inner = setInnerWidget(object_ptr( + _inner = setInnerWidget( + object_ptr(this, controller, peer)); + _inner->notifyEnabled( + ) | rpl::take(1) | rpl::start_with_next([=](bool enabled) { + setupNotifyCheckbox(enabled); + }, _inner->lifetime()); +} + +void Widget::showFinished() { + _shown = true; + if (const auto bottom = _pinnedToBottom.data()) { + bottom->toggle(true, anim::type::normal); + } +} + +void Widget::setupNotifyCheckbox(bool enabled) { + _pinnedToBottom = Ui::CreateChild>( this, - controller, - peer)); + object_ptr(this)); + const auto wrap = _pinnedToBottom.data(); + wrap->toggle(false, anim::type::instant); + + const auto bottom = wrap->entity(); + bottom->show(); + + const auto notify = Ui::CreateChild( + bottom, + tr::lng_peer_gifts_notify(), + enabled); + notify->show(); + + notify->checkedChanges() | rpl::start_with_next([=](bool checked) { + const auto api = &controller()->session().api(); + using Flag = MTPpayments_ToggleChatStarGiftNotifications::Flag; + api->request(MTPpayments_ToggleChatStarGiftNotifications( + MTP_flags(checked ? Flag::f_enabled : Flag()), + _inner->peer()->input + )).send(); + }, notify->lifetime()); + + const auto &checkSt = st::defaultCheckbox; + const auto checkTop = st::boxRadius + checkSt.margin.top(); + bottom->widthValue() | rpl::start_with_next([=](int width) { + const auto normal = notify->naturalWidth() + - checkSt.margin.left() + - checkSt.margin.right(); + notify->resizeToWidth(normal); + const auto checkLeft = (width - normal) / 2; + notify->moveToLeft(checkLeft, checkTop); + }, notify->lifetime()); + + notify->heightValue() | rpl::start_with_next([=](int height) { + bottom->resize(bottom->width(), st::boxRadius + height); + }, notify->lifetime()); + + const auto processHeight = [=] { + setScrollBottomSkip(wrap->height()); + wrap->moveToLeft(wrap->x(), height() - wrap->height()); + }; + + _inner->sizeValue( + ) | rpl::start_with_next([=](const QSize &s) { + wrap->resizeToWidth(s.width()); + crl::on_main(wrap, processHeight); + }, wrap->lifetime()); + + rpl::combine( + wrap->heightValue(), + heightValue() + ) | rpl::start_with_next(processHeight, wrap->lifetime()); + + if (_shown) { + wrap->toggle(true, anim::type::normal); + } + _hasPinnedToBottom = true; } rpl::producer Widget::title() { return tr::lng_peer_gifts_title(); } +rpl::producer Widget::desiredBottomShadowVisibility() { + return _hasPinnedToBottom.value(); +} + not_null Widget::peer() const { return _inner->peer(); } diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.h b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.h index 26423e71e..c482fd473 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.h +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.h @@ -13,6 +13,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class UserData; struct PeerListState; +namespace Ui { +class RpWidget; +template +class SlideWrap; +} // namespace Ui + namespace Info::PeerGifts { struct ListState { @@ -61,13 +67,22 @@ public: rpl::producer title() override; + rpl::producer desiredBottomShadowVisibility() override; + + void showFinished() override; + private: void saveState(not_null memento); void restoreState(not_null memento); std::shared_ptr doCreateMemento() override; + void setupNotifyCheckbox(bool enabled); + InnerWidget *_inner = nullptr; + QPointer> _pinnedToBottom; + rpl::variable _hasPinnedToBottom; + bool _shown = false; }; diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 6adaf6e68..ccf6da961 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -184,7 +184,6 @@ private: void ToggleStarGiftSaved( std::shared_ptr show, - not_null sender, Data::SavedStarGiftId savedId, bool save, Fn done) { @@ -206,17 +205,12 @@ void ToggleStarGiftSaved( void ConfirmConvertStarGift( std::shared_ptr show, - QString name, + rpl::producer confirmText, int stars, int daysLeft, Fn convert) { auto text = rpl::combine( - tr::lng_gift_convert_sure_confirm( - lt_count, - rpl::single(stars * 1.), - lt_user, - rpl::single(Ui::Text::Bold(name)), - Ui::Text::RichLangValue), + std::move(confirmText), tr::lng_gift_convert_sure_limit( lt_count, rpl::single(daysLeft * 1.), @@ -238,7 +232,6 @@ void ConfirmConvertStarGift( void ConvertStarGift( std::shared_ptr show, - not_null sender, Data::SavedStarGiftId savedId, int stars, Fn done) { @@ -931,6 +924,7 @@ void GenericCreditsEntryBox( const Data::SubscriptionEntry &s, CreditsEntryBoxStyleOverrides st) { const auto session = &show->session(); + const auto selfPeerId = session->userPeerId().value; const auto owner = &session->data(); const auto item = owner->message( PeerId(e.barePeerId), @@ -939,7 +933,19 @@ void GenericCreditsEntryBox( const auto creditsHistoryStarGift = isStarGift && !e.id.isEmpty(); const auto sentStarGift = creditsHistoryStarGift && !e.in; const auto convertedStarGift = creditsHistoryStarGift && e.converted; - const auto gotStarGift = isStarGift && !creditsHistoryStarGift && e.in; + const auto giftToSelf = isStarGift + && (e.barePeerId == selfPeerId) + && (e.in || e.bareGiftOwnerId == selfPeerId); + const auto giftChannel = (isStarGift && e.giftSavedId) + ? session->data().peer( + PeerId(e.bareGiftListPeerId))->asChannel() + : nullptr; + const auto giftToChannel = (giftChannel != nullptr); + const auto giftToChannelCanManage = giftToChannel + && giftChannel->canManageGifts(); + const auto gotStarGift = isStarGift + && !creditsHistoryStarGift + && (e.in || giftToChannelCanManage); const auto starGiftSender = (isStarGift && item) ? item->history()->peer->asUser() : (isStarGift && e.in) @@ -1109,16 +1115,6 @@ void GenericCreditsEntryBox( }, widget->lifetime()); } - const auto selfPeerId = session->userPeerId().value; - const auto giftToSelf = isStarGift - && (e.barePeerId == selfPeerId) - && (e.in || e.bareGiftOwnerId == selfPeerId); - const auto giftToChannel = isStarGift && e.giftSavedId; - const auto giftToChannelCanManage = isStarGift - && e.giftSavedId - && session->data().peer( - PeerId(e.bareGiftListPeerId))->canManageGifts(); - if (!uniqueGift) { Ui::AddSkip(content); Ui::AddSkip(content); @@ -1428,7 +1424,7 @@ void GenericCreditsEntryBox( } } }; - ToggleStarGiftSaved(show, starGiftSender, savedId, save, done); + ToggleStarGiftSaved(show, savedId, save, done); }; const auto upgradeGuard = std::make_shared(); @@ -1469,8 +1465,20 @@ void GenericCreditsEntryBox( const auto convert = [=, weak = Ui::MakeWeak(box)] { const auto stars = e.starsConverted; const auto days = canConvert ? ((timeLeft + 86399) / 86400) : 0; - const auto name = starGiftSender->shortName(); - ConfirmConvertStarGift(show, name, stars, days, [=] { + auto text = giftToChannelCanManage + ? tr::lng_gift_convert_sure_confirm_channel( + lt_count, + rpl::single(stars * 1.), + lt_channel, + rpl::single(Ui::Text::Bold(giftChannel->name())), + Ui::Text::RichLangValue) + : tr::lng_gift_convert_sure_confirm( + lt_count, + rpl::single(stars * 1.), + lt_user, + rpl::single(Ui::Text::Bold(starGiftSender->shortName())), + Ui::Text::RichLangValue); + ConfirmConvertStarGift(show, std::move(text), stars, days, [=] { if (state->convertButtonBusy.current() || state->confirmButtonBusy.current()) { return; @@ -1496,12 +1504,7 @@ void GenericCreditsEntryBox( } } }; - ConvertStarGift( - show, - starGiftSender, - savedId, - stars, - done); + ConvertStarGift(show, savedId, stars, done); } }); };