diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index abd2ef8f8..0474f04b7 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -949,6 +949,8 @@ PRIVATE info/media/info_media_widget.h info/members/info_members_widget.cpp info/members/info_members_widget.h + info/peer_gifts/info_peer_gifts_widget.cpp + info/peer_gifts/info_peer_gifts_widget.h info/polls/info_polls_results_inner_widget.cpp info/polls/info_polls_results_inner_widget.h info/polls/info_polls_results_widget.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f4f8fa65b..42676c7dc 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1323,6 +1323,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_profile_similar_channels#other" = "{count} similar channels"; "lng_profile_saved_messages#one" = "{count} saved message"; "lng_profile_saved_messages#other" = "{count} saved messages"; +"lng_profile_peer_gifts#one" = "{count} gift"; +"lng_profile_peer_gifts#other" = "{count} gifts"; "lng_profile_participants_section" = "Members"; "lng_profile_subscribers_section" = "Subscribers"; "lng_profile_add_contact" = "Add Contact"; @@ -1924,6 +1926,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_similar_channels_premium_all_link" = "Telegram Premium"; "lng_similar_channels_show_more" = "Show more channels"; +"lng_peer_gifts_title" = "Gifts"; + "lng_premium_gift_duration_months#one" = "for {count} month"; "lng_premium_gift_duration_months#other" = "for {count} months"; "lng_premium_gift_duration_years#one" = "for {count} year"; diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index d33a41617..a2c45bb2a 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -84,34 +84,35 @@ struct PeerUpdate { BotCanBeInvited = (1ULL << 22), BotStartToken = (1ULL << 23), CommonChats = (1ULL << 24), - HasCalls = (1ULL << 25), - SupportInfo = (1ULL << 26), - IsBot = (1ULL << 27), - EmojiStatus = (1ULL << 28), - BusinessDetails = (1ULL << 29), - Birthday = (1ULL << 30), - PersonalChannel = (1ULL << 31), + PeerGifts = (1ULL << 25), + HasCalls = (1ULL << 26), + SupportInfo = (1ULL << 27), + IsBot = (1ULL << 28), + EmojiStatus = (1ULL << 29), + BusinessDetails = (1ULL << 30), + Birthday = (1ULL << 31), + PersonalChannel = (1ULL << 32), // For chats and channels - InviteLinks = (1ULL << 32), - Members = (1ULL << 33), - Admins = (1ULL << 34), - BannedUsers = (1ULL << 35), - Rights = (1ULL << 36), - PendingRequests = (1ULL << 37), - Reactions = (1ULL << 38), + InviteLinks = (1ULL << 33), + Members = (1ULL << 34), + Admins = (1ULL << 35), + BannedUsers = (1ULL << 36), + Rights = (1ULL << 37), + PendingRequests = (1ULL << 38), + Reactions = (1ULL << 39), // For channels - ChannelAmIn = (1ULL << 39), - StickersSet = (1ULL << 40), - EmojiSet = (1ULL << 41), - ChannelLinkedChat = (1ULL << 42), - ChannelLocation = (1ULL << 43), - Slowmode = (1ULL << 44), - GroupCall = (1ULL << 45), + ChannelAmIn = (1ULL << 40), + StickersSet = (1ULL << 41), + EmojiSet = (1ULL << 42), + ChannelLinkedChat = (1ULL << 43), + ChannelLocation = (1ULL << 44), + Slowmode = (1ULL << 45), + GroupCall = (1ULL << 46), // For iteration - LastUsedBit = (1ULL << 45), + LastUsedBit = (1ULL << 46), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 729f27dce..77a20da6e 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -130,6 +130,17 @@ void UserData::setCommonChatsCount(int count) { } } +int UserData::peerGiftsCount() const { + return _peerGiftsCount; +} + +void UserData::setPeerGiftsCount(int count) { + if (_peerGiftsCount != count) { + _peerGiftsCount = count; + session().changes().peerUpdated(this, UpdateFlag::PeerGifts); + } +} + bool UserData::hasPrivateForwardName() const { return !_privateForwardName.isEmpty(); } @@ -610,6 +621,7 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { : UserData::CallsStatus::Disabled); user->setAbout(qs(update.vabout().value_or_empty())); user->setCommonChatsCount(update.vcommon_chats_count().v); + user->setPeerGiftsCount(update.vstargifts_count().value_or_empty()); user->checkFolder(update.vfolder_id().value_or_empty()); user->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty())); user->setTranslationDisabled(update.is_translations_disabled()); diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index d891d4937..e84d02ac0 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -185,9 +185,12 @@ public: void setBirthday(Data::Birthday value); void setBirthday(const tl::conditional &value); - int commonChatsCount() const; + [[nodiscard]] int commonChatsCount() const; void setCommonChatsCount(int count); + [[nodiscard]] int peerGiftsCount() const; + void setPeerGiftsCount(int count); + [[nodiscard]] bool hasPrivateForwardName() const; [[nodiscard]] QString privateForwardName() const; void setPrivateForwardName(const QString &name); @@ -222,6 +225,7 @@ private: Data::LastseenStatus _lastseen; Data::Birthday _birthday; int _commonChatsCount = 0; + int _peerGiftsCount = 0; ContactStatus _contactStatus = ContactStatus::Unknown; CallsStatus _callsStatus = CallsStatus::Unknown; diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 65d6c2eb1..ac90070da 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -473,6 +473,7 @@ infoIconMediaStories: icon {{ "info/info_media_stories", infoIconFg }}; infoIconMediaSaved: icon {{ "info/info_media_saved", infoIconFg }}; infoIconMediaStoriesArchive: icon {{ "info/info_stories_archive", infoIconFg }}; infoIconMediaStoriesRecent: icon {{ "info/info_stories_recent", infoIconFg }}; +infoIconMediaGifts: icon {{ "menu/gift_premium", infoIconFg, point(4px, 4px) }}; infoIconShare: icon {{ "info/info_share", infoIconFg }}; infoIconEdit: icon {{ "info/info_edit", infoIconFg }}; diff --git a/Telegram/SourceFiles/info/info_controller.h b/Telegram/SourceFiles/info/info_controller.h index de0337d26..ee4a7fc33 100644 --- a/Telegram/SourceFiles/info/info_controller.h +++ b/Telegram/SourceFiles/info/info_controller.h @@ -127,6 +127,7 @@ public: CommonGroups, SimilarChannels, SavedSublists, + PeerGifts, Members, Settings, Downloads, diff --git a/Telegram/SourceFiles/info/info_memento.cpp b/Telegram/SourceFiles/info/info_memento.cpp index ec4eb246e..a8018f21c 100644 --- a/Telegram/SourceFiles/info/info_memento.cpp +++ b/Telegram/SourceFiles/info/info_memento.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/saved/info_saved_sublists_widget.h" #include "info/settings/info_settings_widget.h" #include "info/similar_channels/info_similar_channels_widget.h" +#include "info/peer_gifts/info_peer_gifts_widget.h" #include "info/polls/info_polls_results_widget.h" #include "info/info_section_widget.h" #include "info/info_layer_widget.h" @@ -148,6 +149,8 @@ std::shared_ptr Memento::DefaultContent( case Section::Type::SimilarChannels: return std::make_shared( peer->asChannel()); + case Section::Type::PeerGifts: + return std::make_shared(peer->asUser()); case Section::Type::SavedSublists: return std::make_shared(&peer->session()); case Section::Type::Members: diff --git a/Telegram/SourceFiles/info/media/info_media_buttons.h b/Telegram/SourceFiles/info/media/info_media_buttons.h index 1331ae07d..af97c6d27 100644 --- a/Telegram/SourceFiles/info/media/info_media_buttons.h +++ b/Telegram/SourceFiles/info/media/info_media_buttons.h @@ -203,4 +203,25 @@ inline auto AddSavedSublistButton( return result; } +inline auto AddPeerGiftsButton( + Ui::VerticalLayout *parent, + not_null navigation, + not_null user, + Ui::MultiSlideTracker &tracker) { + auto result = AddCountedButton( + parent, + Profile::PeerGiftsCountValue(user), + [](int count) { + return tr::lng_profile_peer_gifts(tr::now, lt_count, count); + }, + tracker)->entity(); + result->addClickHandler([=] { + navigation->showSection( + std::make_shared( + user, + Section::Type::PeerGifts)); + }); + return result; +} + } // namespace Info::Media diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp new file mode 100644 index 000000000..8c46130d5 --- /dev/null +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp @@ -0,0 +1,177 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "info/peer_gifts/info_peer_gifts_widget.h" + +#include "data/data_user.h" +#include "info/info_controller.h" +#include "ui/ui_utility.h" +#include "lang/lang_keys.h" +#include "window/window_session_controller.h" +#include "styles/style_info.h" + +namespace Info::PeerGifts { +namespace { + +} // namespace + +class InnerWidget final : public Ui::RpWidget { +public: + InnerWidget( + QWidget *parent, + not_null controller, + not_null user); + + [[nodiscard]] not_null user() const { + return _user; + } + + rpl::producer scrollToRequests() const; + + int desiredHeight() const; + + void saveState(not_null memento); + void restoreState(not_null memento); + +protected: + void visibleTopBottomUpdated( + int visibleTop, + int visibleBottom) override; + +private: + const std::shared_ptr _show; + not_null _controller; + const not_null _user; + + rpl::event_stream _scrollToRequests; + +}; + +InnerWidget::InnerWidget( + QWidget *parent, + not_null controller, + not_null user) +: RpWidget(parent) +, _show(controller->uiShow()) +, _controller(controller) +, _user(user) { +} + +void InnerWidget::visibleTopBottomUpdated( + int visibleTop, + int visibleBottom) { +} + +void InnerWidget::saveState(not_null memento) { + auto state = std::make_unique(); + memento->setListState(std::move(state)); +} + +void InnerWidget::restoreState(not_null memento) { + if (const auto state = memento->listState()) { + + } +} + +rpl::producer InnerWidget::scrollToRequests() const { + return _scrollToRequests.events(); +} + +int InnerWidget::desiredHeight() const { + auto desired = 0; + + return qMax(height(), desired); +} + +Memento::Memento(not_null user) +: ContentMemento(user, nullptr, PeerId()) { +} + +Section Memento::section() const { + return Section(Section::Type::PeerGifts); +} + +not_null Memento::user() const { + return peer()->asUser(); +} + +object_ptr Memento::createWidget( + QWidget *parent, + not_null controller, + const QRect &geometry) { + auto result = object_ptr(parent, controller, user()); + result->setInternalState(geometry, this); + return result; +} + +void Memento::setListState(std::unique_ptr state) { + _listState = std::move(state); +} + +std::unique_ptr Memento::listState() { + return std::move(_listState); +} + +Memento::~Memento() = default; + +Widget::Widget( + QWidget *parent, + not_null controller, + not_null user) +: ContentWidget(parent, controller) { + _inner = setInnerWidget(object_ptr( + this, + controller, + user)); +} + +rpl::producer Widget::title() { + return tr::lng_peer_gifts_title(); +} + +not_null Widget::user() const { + return _inner->user(); +} + +bool Widget::showInternal(not_null memento) { + if (!controller()->validateMementoPeer(memento)) { + return false; + } + if (auto similarMemento = dynamic_cast(memento.get())) { + if (similarMemento->user() == user()) { + restoreState(similarMemento); + return true; + } + } + return false; +} + +void Widget::setInternalState( + const QRect &geometry, + not_null memento) { + setGeometry(geometry); + Ui::SendPendingMoveResizeEvents(this); + restoreState(memento); +} + +std::shared_ptr Widget::doCreateMemento() { + auto result = std::make_shared(user()); + saveState(result.get()); + return result; +} + +void Widget::saveState(not_null memento) { + memento->setScrollTop(scrollTopSave()); + _inner->saveState(memento); +} + +void Widget::restoreState(not_null memento) { + _inner->restoreState(memento); + scrollTopRestore(memento->scrollTop()); +} + +} // namespace Info::PeerGifts diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.h b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.h new file mode 100644 index 000000000..3c6b216c6 --- /dev/null +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.h @@ -0,0 +1,84 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "info/info_content_widget.h" + +class UserData; +struct PeerListState; + +namespace Info::PeerGifts { + +struct ListEntry { + TextWithEntities message; + int64 convertStars = 0; + PeerId fromId = 0; + MsgId messageId = 0; + bool anonymous = false; + bool hidden = false; +}; + +struct ListState { + std::vector list; + QString offset; +}; + +class InnerWidget; + +class Memento final : public ContentMemento { +public: + explicit Memento(not_null user); + + object_ptr createWidget( + QWidget *parent, + not_null controller, + const QRect &geometry) override; + + Section section() const override; + + [[nodiscard]] not_null user() const; + + void setListState(std::unique_ptr state); + std::unique_ptr listState(); + + ~Memento(); + +private: + std::unique_ptr _listState; + +}; + +class Widget final : public ContentWidget { +public: + Widget( + QWidget *parent, + not_null controller, + not_null user); + + [[nodiscard]] not_null user() const; + + bool showInternal( + not_null memento) override; + + void setInternalState( + const QRect &geometry, + not_null memento); + + rpl::producer title() override; + +private: + void saveState(not_null memento); + void restoreState(not_null memento); + + std::shared_ptr doCreateMemento() override; + + InnerWidget *_inner = nullptr; + +}; + +} // namespace Info::PeerGifts diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index fdca311c8..c0ea78d2a 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -231,6 +231,19 @@ object_ptr InnerWidget::setupSharedMedia( icon, st::infoSharedMediaButtonIconPosition); }; + auto addPeerGiftsButton = [&]( + not_null user, + const style::icon &icon) { + auto result = Media::AddPeerGiftsButton( + content, + _controller, + user, + tracker); + object_ptr( + result, + icon, + st::infoSharedMediaButtonIconPosition); + }; if (!_topic) { addStoriesButton(_peer, st::infoIconMediaStories); @@ -245,6 +258,7 @@ object_ptr InnerWidget::setupSharedMedia( addMediaButton(MediaType::GIF, st::infoIconMediaGif); if (const auto user = _peer->asUser()) { addCommonGroupsButton(user, st::infoIconMediaGroup); + addPeerGiftsButton(user, st::infoIconMediaGifts); } else if (const auto channel = _peer->asChannel()) { addSimilarChannelsButton(channel, st::infoIconMediaChannel); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 70719e936..2f0b8a5a3 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -581,6 +581,15 @@ rpl::producer SavedSublistCountValue( return sublist->fullCountValue(); } +rpl::producer PeerGiftsCountValue(not_null user) { + return user->session().changes().peerFlagsValue( + user, + UpdateFlag::PeerGifts + ) | rpl::map([=] { + return user->peerGiftsCount(); + }); +} + rpl::producer CanAddMemberValue(not_null peer) { if (const auto chat = peer->asChat()) { return peer->session().changes().peerFlagsValue( diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h index f292c80a5..981b70aba 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.h +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -120,6 +120,8 @@ struct LinkWithUrl { not_null channel); [[nodiscard]] rpl::producer SavedSublistCountValue( not_null peer); +[[nodiscard]] rpl::producer PeerGiftsCountValue( + not_null user); [[nodiscard]] rpl::producer CanAddMemberValue( not_null peer); [[nodiscard]] rpl::producer FullReactionsCountValue( diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 057d38c7d..96d2cf00a 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -990,9 +990,7 @@ void ReceiptCreditsBox( rpl::single(e.description), st::creditsBoxAbout))); } - if (isStarGift && !canConvert) { - // Nothing - } else if (isStarGift) { + if (isStarGift) { Ui::AddSkip(content); const auto about = box->addRow( object_ptr>(