From f3f660a1802435dcafcad99829370a6c224d7ce0 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 18 Jan 2024 10:39:00 +0400 Subject: [PATCH] Show toast on share attempt to premium required. --- Telegram/SourceFiles/boxes/peer_list_box.cpp | 19 +- Telegram/SourceFiles/boxes/peer_list_box.h | 10 ++ .../boxes/peer_list_controllers.cpp | 77 +++++---- .../SourceFiles/boxes/peer_list_controllers.h | 15 ++ Telegram/SourceFiles/boxes/peer_lists_box.cpp | 4 +- .../boxes/peers/edit_peer_invite_link.cpp | 6 + Telegram/SourceFiles/boxes/share_box.cpp | 162 +++++++++++++++--- Telegram/SourceFiles/boxes/share_box.h | 7 + .../calls/group/calls_group_settings.cpp | 7 + Telegram/SourceFiles/data/data_user.cpp | 4 + Telegram/SourceFiles/data/data_user.h | 1 + .../media/stories/media_stories_share.cpp | 7 + 12 files changed, 255 insertions(+), 64 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 3278088ec..fe3a3f28e 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -232,9 +232,7 @@ void PeerListBox::resizeEvent(QResizeEvent *e) { void PeerListBox::paintEvent(QPaintEvent *e) { auto p = QPainter(this); - const auto &bg = (_controller->listSt() - ? *_controller->listSt() - : st::peerListBox).bg; + const auto &bg = _controller->computeListSt().bg; const auto fill = QRect( 0, _addedTopScrollSkip, @@ -1255,6 +1253,16 @@ not_null<PeerListRow*> PeerListContent::rowAt(int index) const { return _rows[index].get(); } +int PeerListContent::searchRowsCount() const { + return _searchRows.size(); +} + +not_null<PeerListRow*> PeerListContent::searchRowAt(int index) const { + Expects(index >= 0 && index < _searchRows.size()); + + return _searchRows[index].get(); +} + void PeerListContent::setDescription(object_ptr<Ui::FlatLabel> description) { _description = std::move(description); if (_description) { @@ -1349,6 +1357,7 @@ void PeerListContent::refreshRows() { if (_mouseSelection) { selectByMouse(QCursor::pos()); } + loadProfilePhotos(); update(); } @@ -1903,7 +1912,9 @@ void PeerListContent::mouseLeftGeometry() { } void PeerListContent::loadProfilePhotos() { - if (_visibleTop >= _visibleBottom) return; + if (_visibleTop >= _visibleBottom) { + return; + } auto yFrom = _visibleTop; auto yTo = _visibleBottom + (_visibleBottom - _visibleTop) * PreloadHeightsCount; diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 25e96f60b..50a37263e 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -332,6 +332,8 @@ public: virtual void peerListScrollToTop() = 0; virtual int peerListFullRowsCount() = 0; virtual PeerListRow *peerListFindRow(PeerListRowId id) = 0; + virtual int peerListSearchRowsCount() = 0; + virtual not_null<PeerListRow*> peerListSearchRowAt(int index) = 0; virtual std::optional<QPoint> peerListLastRowMousePosition() = 0; virtual void peerListSortRows(Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) = 0; virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0; @@ -627,6 +629,8 @@ public: void convertRowToSearchResult(not_null<PeerListRow*> row); int fullRowsCount() const; not_null<PeerListRow*> rowAt(int index) const; + int searchRowsCount() const; + not_null<PeerListRow*> searchRowAt(int index) const; void setDescription(object_ptr<Ui::FlatLabel> description); void setSearchLoading(object_ptr<Ui::FlatLabel> loading); void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults); @@ -908,6 +912,12 @@ public: not_null<PeerListRow*> peerListRowAt(int index) override { return _content->rowAt(index); } + int peerListSearchRowsCount() override { + return _content->searchRowsCount(); + } + not_null<PeerListRow*> peerListSearchRowAt(int index) override { + return _content->searchRowAt(index); + } void peerListRefreshRows() override { _content->refreshRows(); } diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 7d8c2d291..382301725 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -271,39 +271,17 @@ ChatsListBoxController::Row::Row( , _delegate(delegate) { } -void PaintLock( - Painter &p, - not_null<const style::PeerListItem*> st, - int x, - int y, - int outerWidth, - int size) { - auto hq = PainterHighQualityEnabler(p); - const auto &check = st->checkbox.check; - auto pen = check.border->p; - pen.setWidthF(check.width); - p.setPen(pen); - p.setBrush(st::premiumButtonBg2); - const auto &icon = st::stickersPremiumLock; - const auto width = icon.width(); - const auto height = icon.height(); - const auto rect = QRect( - QPoint(x + size - width, y + size - height), - icon.size()); - p.drawEllipse(rect); - icon.paintInCenter(p, rect); -} - auto ChatsListBoxController::Row::generatePaintUserpicCallback( bool forceRound) -> PaintRoundImageCallback { auto result = PeerListRow::generatePaintUserpicCallback(forceRound); if (_locked) { - AssertIsDebug(); - const auto st = /*_delegate ? _delegate->rowSt() : */&st::defaultPeerListItem; + const auto st = _delegate + ? _delegate->rowSt().get() + : &st::defaultPeerListItem; return [=](Painter &p, int x, int y, int outerWidth, int size) { result(p, x, y, outerWidth, size); - PaintLock(p, st, x, y, outerWidth, size); + PaintPremiumRequiredLock(p, st, x, y, outerWidth, size); }; } return result; @@ -475,7 +453,7 @@ void PeerListStories::process(not_null<PeerListRow*> row) { bool PeerListStories::handleClick(not_null<PeerData*> peer) { const auto point = _delegate->peerListLastRowMousePosition(); - const auto &st = _controller->listSt()->item; + const auto &st = _controller->computeListSt().item; if (point && point->x() < st.photoPosition.x() + st.photoSize) { if (const auto window = peer->session().tryResolveWindow()) { if (const auto user = peer->asUser()) { @@ -492,9 +470,9 @@ bool PeerListStories::handleClick(not_null<PeerData*> peer) { void PeerListStories::prepare(not_null<PeerListDelegate*> delegate) { _delegate = delegate; - _unreadBrush = PeerListStoriesGradient(*_controller->listSt()); + _unreadBrush = PeerListStoriesGradient(_controller->computeListSt()); style::PaletteChanged() | rpl::start_with_next([=] { - _unreadBrush = PeerListStoriesGradient(*_controller->listSt()); + _unreadBrush = PeerListStoriesGradient(_controller->computeListSt()); updateColors(); }, _lifetime); @@ -738,9 +716,7 @@ void ChooseRecipientBoxController::prepareViewHook() { } void ChooseRecipientBoxController::refreshLockedRows() { - auto count = delegate()->peerListFullRowsCount(); - for (auto i = 0; i != count; ++i) { - const auto raw = delegate()->peerListRowAt(i); + const auto process = [&](not_null<PeerListRow*> raw) { const auto row = static_cast<Row*>(raw.get()); if (const auto user = row->peer()->asUser()) { const auto history = row->history(); @@ -751,6 +727,14 @@ void ChooseRecipientBoxController::refreshLockedRows() { delegate()->peerListUpdateRow(row); } } + }; + auto count = delegate()->peerListFullRowsCount(); + for (auto i = 0; i != count; ++i) { + process(delegate()->peerListRowAt(i)); + } + count = delegate()->peerListSearchRowsCount(); + for (auto i = 0; i != count; ++i) { + process(delegate()->peerListSearchRowAt(i)); } } @@ -766,6 +750,10 @@ void ChooseRecipientBoxController::rowPreloadUserpic(not_null<Row*> row) { } } +not_null<const style::PeerListItem*> ChooseRecipientBoxController::rowSt() { + return &computeListSt().item; +} + void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) { if (showLockedError(row)) { return; @@ -845,7 +833,7 @@ auto ChooseRecipientBoxController::createRow( : ((peer->isBroadcast() && !Data::CanSendAnything(peer)) || peer->isRepliesChat() || (peer->isUser() && (_premiumRequiredError - ? peer->asUser()->isInaccessible() + ? !peer->asUser()->canSendIgnoreRequirePremium() : !Data::CanSendAnything(peer)))); if (skip) { return nullptr; @@ -1077,3 +1065,26 @@ auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic) const auto skip = _filter && !_filter(topic); return skip ? nullptr : std::make_unique<Row>(topic); }; + +void PaintPremiumRequiredLock( + Painter &p, + not_null<const style::PeerListItem*> st, + int x, + int y, + int outerWidth, + int size) { + auto hq = PainterHighQualityEnabler(p); + const auto &check = st->checkbox.check; + auto pen = check.border->p; + pen.setWidthF(check.width); + p.setPen(pen); + p.setBrush(st::premiumButtonBg2); + const auto &icon = st::stickersPremiumLock; + const auto width = icon.width(); + const auto height = icon.height(); + const auto rect = QRect( + QPoint(x + size - width, y + size - height), + icon.size()); + p.drawEllipse(rect); + icon.paintInCenter(p, rect); +} diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h index 9ce3dd0bf..73522ba4d 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.h +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h @@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class History; +namespace style { +struct PeerListItem; +} // namespace style + namespace Data { class Thread; class Forum; @@ -95,6 +99,8 @@ public: class RowDelegate { public: virtual void rowPreloadUserpic(not_null<Row*> row); + [[nodiscard]] virtual auto rowSt() + -> not_null<const style::PeerListItem*> = 0; }; class Row : public PeerListRow { @@ -266,6 +272,7 @@ protected: private: void refreshLockedRows(); void rowPreloadUserpic(not_null<Row*> row) override; + not_null<const style::PeerListItem*> rowSt() override; const not_null<Main::Session*> _session; FnMut<void(not_null<Data::Thread*>)> _callback; @@ -350,3 +357,11 @@ private: Fn<bool(not_null<Data::ForumTopic*>)> _filter; }; + +void PaintPremiumRequiredLock( + Painter &p, + not_null<const style::PeerListItem*> st, + int x, + int y, + int outerWidth, + int size); diff --git a/Telegram/SourceFiles/boxes/peer_lists_box.cpp b/Telegram/SourceFiles/boxes/peer_lists_box.cpp index 80e2a9334..e1ced558c 100644 --- a/Telegram/SourceFiles/boxes/peer_lists_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_lists_box.cpp @@ -281,9 +281,7 @@ void PeerListsBox::resizeEvent(QResizeEvent *e) { void PeerListsBox::paintEvent(QPaintEvent *e) { auto p = QPainter(this); - const auto &bg = (firstController()->listSt() - ? *firstController()->listSt() - : st::peerListBox).bg; + const auto &bg = firstController()->computeListSt().bg; for (const auto &rect : e->region()) { p.fillRect(rect, bg); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 03bf6108d..d8adfbc41 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -1194,6 +1194,11 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox( } }; auto filterCallback = [](not_null<Data::Thread*> thread) { + if (const auto user = thread->peer()->asUser()) { + if (user->canSendIgnoreRequirePremium()) { + return true; + } + } return Data::CanSendTexts(thread); }; auto object = Box<ShareBox>(ShareBox::Descriptor{ @@ -1201,6 +1206,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox( .copyCallback = std::move(copyCallback), .submitCallback = std::move(submitCallback), .filterCallback = std::move(filterCallback), + .premiumRequiredError = SharePremiumRequiredError(), }); *box = Ui::MakeWeak(object.data()); return object; diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 007355317..b5a549330 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/share_box.h" +#include "api/api_premium.h" #include "base/random.h" #include "lang/lang_keys.h" #include "base/qthelp_url.h" @@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_helpers.h" #include "history/view/history_view_element.h" #include "history/view/history_view_context_menu.h" // CopyPostLink. +#include "settings/settings_premium.h" #include "window/window_session_controller.h" #include "boxes/peer_list_controllers.h" #include "chat_helpers/emoji_suggestions_widget.h" @@ -38,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_game.h" #include "data/data_histories.h" #include "data/data_user.h" +#include "data/data_peer_values.h" #include "data/data_session.h" #include "data/data_folder.h" #include "data/data_forum.h" @@ -96,19 +99,23 @@ protected: private: struct Chat { Chat( - not_null<PeerData*> peer, + not_null<History*> history, const style::PeerListItem &st, Fn<void()> updateCallback); + not_null<History*> history; not_null<PeerData*> peer; Data::ForumTopic *topic = nullptr; rpl::lifetime topicLifetime; Ui::RoundImageCheckbox checkbox; Ui::Text::String name; Ui::Animations::Simple nameActive; + bool locked = false; }; void invalidateCache(); + bool showLockedError(not_null<Chat*> chat); + void refreshLockedRows(); [[nodiscard]] int displayedChatsCount() const; [[nodiscard]] not_null<Data::Thread*> chatThread( @@ -117,12 +124,14 @@ private: void paintChat(Painter &p, not_null<Chat*> chat, int index); void updateChat(not_null<PeerData*> peer); void updateChatName(not_null<Chat*> chat); + void initChatLocked(not_null<Chat*> chat); void repaintChat(not_null<PeerData*> peer); int chatIndex(not_null<PeerData*> peer) const; void repaintChatAtIndex(int index); Chat *getChatAtIndex(int index); - void loadProfilePhotos(int yFrom); + void loadProfilePhotos(); + void preloadUserpic(not_null<Dialogs::Entry*> entry); void changeCheckState(Chat *chat); void chooseForumTopic(not_null<Data::Forum*> forum); enum class ChangeStateWay { @@ -153,6 +162,7 @@ private: int _columnCount = 4; int _active = -1; int _upon = -1; + int _visibleTop = 0; std::unique_ptr<Dialogs::IndexedList> _chatsIndexed; QString _filter; @@ -649,6 +659,16 @@ ShareBox::Inner::Inner( _rowHeight = st::shareRowHeight; setAttribute(Qt::WA_OpaquePaintEvent); + if (_descriptor.premiumRequiredError) { + const auto session = _descriptor.session; + rpl::merge( + Data::AmPremiumValue(session) | rpl::to_empty, + session->api().premium().somePremiumRequiredResolved() + ) | rpl::start_with_next([=] { + refreshLockedRows(); + }, lifetime()); + } + const auto self = _descriptor.session->user(); const auto selfHistory = self->owner().history(self); if (_descriptor.filterCallback(selfHistory)) { @@ -705,10 +725,48 @@ void ShareBox::Inner::invalidateCache() { } } +bool ShareBox::Inner::showLockedError(not_null<Chat*> chat) { + if (!chat->locked) { + return false; + } + ::Settings::ShowPremiumPromoToast( + Main::MakeSessionShow(_show, _descriptor.session), + ChatHelpers::ResolveWindowDefault(), + _descriptor.premiumRequiredError(chat->peer->asUser()).text, + u"require_premium"_q); + return true; +} + +void ShareBox::Inner::refreshLockedRows() { + auto changed = false; + for (const auto &[peer, data] : _dataMap) { + const auto history = data->history; + const auto locked = (Api::ResolveRequiresPremiumToWrite(history) + == Api::RequirePremiumState::Yes); + if (data->locked != locked) { + data->locked = locked; + changed = true; + } + } + for (const auto &data : d_byUsernameFiltered) { + const auto history = data->history; + const auto locked = (Api::ResolveRequiresPremiumToWrite(history) + == Api::RequirePremiumState::Yes); + if (data->locked != locked) { + data->locked = locked; + changed = true; + } + } + if (changed) { + update(); + } +} + void ShareBox::Inner::visibleTopBottomUpdated( int visibleTop, int visibleBottom) { - loadProfilePhotos(visibleTop); + _visibleTop = visibleTop; + loadProfilePhotos(); } void ShareBox::Inner::activateSkipRow(int direction) { @@ -760,6 +818,16 @@ void ShareBox::Inner::updateChatName(not_null<Chat*> chat) { chat->name.setText(_st.item.nameStyle, text, Ui::NameTextOptions()); } +void ShareBox::Inner::initChatLocked(not_null<Chat*> chat) { + if (_descriptor.premiumRequiredError) { + const auto history = chat->history; + const auto require = Api::ResolveRequiresPremiumToWrite(history); + if (require == Api::RequirePremiumState::Yes) { + chat->locked = true; + } + } +} + void ShareBox::Inner::repaintChatAtIndex(int index) { if (index < 0) return; @@ -829,11 +897,11 @@ int ShareBox::Inner::chatIndex(not_null<PeerData*> peer) const { return -1; } -void ShareBox::Inner::loadProfilePhotos(int yFrom) { - if (!parentWidget()) return; - if (yFrom < 0) { - yFrom = 0; +void ShareBox::Inner::loadProfilePhotos() { + if (!parentWidget()) { + return; } + auto yFrom = std::max(_visibleTop, 0); if (auto part = (yFrom % _rowHeight)) { yFrom -= part; } @@ -853,20 +921,38 @@ void ShareBox::Inner::loadProfilePhotos(int yFrom) { if (((*i)->index() * _rowHeight) >= yTo) { break; } - (*i)->entry()->chatListPreloadData(); + preloadUserpic((*i)->entry()); } } - } else if (!_filtered.empty()) { - int from = yFrom / _rowHeight; - if (from < 0) from = 0; - if (from < _filtered.size()) { - int to = (yTo / _rowHeight) + 1; - if (to > _filtered.size()) to = _filtered.size(); + } else { + const auto from = std::max(yFrom / _rowHeight, 0); + const auto to = std::max((yTo / _rowHeight) + 1, from); - for (; from < to; ++from) { - _filtered[from]->entry()->chatListPreloadData(); - } + const auto ffrom = from; + const auto fto = std::min(to, int(_filtered.size())); + for (auto i = ffrom; i != fto; ++i) { + preloadUserpic(_filtered[i]->entry()); } + + const auto ufrom = std::max(from - int(_filtered.size()), 0); + const auto uto = std::min( + to - int(_filtered.size()), + int(d_byUsernameFiltered.size())); + for (auto i = ufrom; i != uto; ++i) { + preloadUserpic(d_byUsernameFiltered[i]->history); + } + } +} + +void ShareBox::Inner::preloadUserpic(not_null<Dialogs::Entry*> entry) { + entry->chatListPreloadData(); + const auto history = entry->asHistory(); + if (!_descriptor.premiumRequiredError || !history) { + return; + } else if (Api::ResolveRequiresPremiumToWrite(history) + == Api::RequirePremiumState::Unknown) { + const auto user = history->peer->asUser(); + _descriptor.session->api().premium().resolvePremiumRequired(user); } } @@ -877,15 +963,19 @@ auto ShareBox::Inner::getChat(not_null<Dialogs::Row*> row) if (const auto data = static_cast<Chat*>(row->attached)) { return data; } - const auto peer = row->history()->peer; + const auto history = row->history(); + const auto peer = history->peer; if (const auto i = _dataMap.find(peer); i != end(_dataMap)) { row->attached = i->second.get(); return i->second.get(); } const auto &[i, ok] = _dataMap.emplace( peer, - std::make_unique<Chat>(peer, _st.item, [=] { repaintChat(peer); })); + std::make_unique<Chat>(history, _st.item, [=] { + repaintChat(peer); + })); updateChatName(i->second.get()); + initChatLocked(i->second.get()); row->attached = i->second.get(); return i->second.get(); } @@ -919,6 +1009,16 @@ void ShareBox::Inner::paintChat( auto photoTop = st::sharePhotoTop; chat->checkbox.paint(p, x + photoLeft, y + photoTop, outerWidth); + if (chat->locked) { + PaintPremiumRequiredLock( + p, + &_st.item, + x + photoLeft, + y + photoTop, + outerWidth, + _st.item.checkbox.imageRadius * 2); + } + auto nameActive = chat->nameActive.value((index == _active) ? 1. : 0.); p.setPen(anim::pen(_st.item.nameFg, _st.item.nameFgChecked, nameActive)); @@ -929,10 +1029,11 @@ void ShareBox::Inner::paintChat( } ShareBox::Inner::Chat::Chat( - not_null<PeerData*> peer, + not_null<History*> history, const style::PeerListItem &st, Fn<void()> updateCallback) -: peer(peer) +: history(history) +, peer(history->peer) , checkbox( st.checkbox, updateCallback, @@ -1062,7 +1163,7 @@ void ShareBox::Inner::resizeEvent(QResizeEvent *e) { } void ShareBox::Inner::changeCheckState(Chat *chat) { - if (!chat) { + if (!chat || showLockedError(chat)) { return; } else if (!_filter.isEmpty()) { const auto history = chat->peer->owner().history(chat->peer); @@ -1194,8 +1295,8 @@ void ShareBox::Inner::updateFilter(QString filter) { _searchRequests.fire({}); } setActive(-1); + loadProfilePhotos(); update(); - loadProfilePhotos(0); } } @@ -1234,10 +1335,11 @@ void ShareBox::Inner::peopleReceived( } _byUsernameFiltered.push_back(peer); d_byUsernameFiltered.push_back(std::make_unique<Chat>( - peer, + history, _st.item, [=] { repaintChat(peer); })); updateChatName(d_byUsernameFiltered.back().get()); + initChatLocked(d_byUsernameFiltered.back().get()); } } }; @@ -1256,6 +1358,7 @@ void ShareBox::Inner::refresh() { } else { resize(width(), st::noContactsHeight); } + loadProfilePhotos(); update(); } @@ -1525,6 +1628,11 @@ void FastShareMessage( const auto requiredRight = item->requiredSendRight(); const auto requiresInline = item->requiresSendInlineRight(); auto filterCallback = [=](not_null<Data::Thread*> thread) { + if (const auto user = thread->peer()->asUser()) { + if (user->canSendIgnoreRequirePremium()) { + return true; + } + } return Data::CanSend(thread, requiredRight) && (!requiresInline || Data::CanSend(thread, ChatRestriction::SendInline)) @@ -1547,10 +1655,16 @@ void FastShareMessage( .show = !hasOnlyForcedForwardedInfo, .hasCaptions = hasCaptions, }, + .premiumRequiredError = SharePremiumRequiredError(), }), Ui::LayerOption::CloseOther); } +auto SharePremiumRequiredError() +-> Fn<ChooseRecipientPremiumRequiredError(not_null<UserData*>)> { + return WritePremiumRequiredError; +} + void ShareGameScoreByHash( not_null<Window::SessionController*> controller, const QString &hash) { diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h index af6bde3a1..00314f3ad 100644 --- a/Telegram/SourceFiles/boxes/share_box.h +++ b/Telegram/SourceFiles/boxes/share_box.h @@ -69,6 +69,10 @@ void FastShareMessage( not_null<Window::SessionController*> controller, not_null<HistoryItem*> item); +struct ChooseRecipientPremiumRequiredError; +[[nodiscard]] auto SharePremiumRequiredError() +-> Fn<ChooseRecipientPremiumRequiredError(not_null<UserData*>)>; + class ShareBox final : public Ui::BoxContent { public: using CopyCallback = Fn<void()>; @@ -101,6 +105,9 @@ public: bool hasCaptions = false; } forwardOptions; HistoryView::ScheduleBoxStyleArgs scheduleBoxStyle; + + using PremiumRequiredError = ChooseRecipientPremiumRequiredError; + Fn<PremiumRequiredError(not_null<UserData*>)> premiumRequiredError; }; ShareBox(QWidget*, Descriptor &&descriptor); diff --git a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp index 8e0df2f3e..4a7b50458 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp @@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_group_call.h" +#include "data/data_user.h" #include "calls/group/calls_group_rtmp.h" #include "ui/toast/toast.h" #include "data/data_changes.h" @@ -191,6 +192,11 @@ object_ptr<ShareBox> ShareInviteLinkBox( show->showToast(tr::lng_share_done(tr::now)); }; auto filterCallback = [](not_null<Data::Thread*> thread) { + if (const auto user = thread->peer()->asUser()) { + if (user->canSendIgnoreRequirePremium()) { + return true; + } + } return Data::CanSend(thread, ChatRestriction::SendOther); }; @@ -227,6 +233,7 @@ object_ptr<ShareBox> ShareInviteLinkBox( .st = &st::groupCallShareBoxList, .stLabel = &st::groupCallField, .scheduleBoxStyle = scheduleStyle(), + .premiumRequiredError = SharePremiumRequiredError(), }); *box = result.data(); return result; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 4e5029afe..5014bdfb9 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -402,6 +402,10 @@ bool UserData::requirePremiumToWriteKnown() const { return (flags() & UserDataFlag::RequirePremiumToWriteKnown); } +bool UserData::canSendIgnoreRequirePremium() const { + return !isInaccessible() && !isRepliesChat(); +} + bool UserData::readDatesPrivate() const { return (flags() & UserDataFlag::ReadDatesPrivate); } diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index ff3062b81..f6ba39749 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -131,6 +131,7 @@ public: [[nodiscard]] bool someRequirePremiumToWrite() const; [[nodiscard]] bool meRequiresPremiumToWrite() const; [[nodiscard]] bool requirePremiumToWriteKnown() const; + [[nodiscard]] bool canSendIgnoreRequirePremium() const; [[nodiscard]] bool readDatesPrivate() const; [[nodiscard]] bool canShareThisContact() const; diff --git a/Telegram/SourceFiles/media/stories/media_stories_share.cpp b/Telegram/SourceFiles/media/stories/media_stories_share.cpp index 91fc29002..deb291cc1 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_share.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_share.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_stories.h" #include "data/data_thread.h" +#include "data/data_user.h" #include "history/history.h" #include "history/history_item_helpers.h" // GetErrorTextForSending. #include "history/view/history_view_context_menu.h" // CopyStoryLink. @@ -61,6 +62,11 @@ namespace Media::Stories { }; const auto state = std::make_shared<State>(); auto filterCallback = [=](not_null<Data::Thread*> thread) { + if (const auto user = thread->peer()->asUser()) { + if (user->canSendIgnoreRequirePremium()) { + return true; + } + } return Data::CanSend(thread, ChatRestriction::SendPhotos) && Data::CanSend(thread, ChatRestriction::SendVideos); }; @@ -189,6 +195,7 @@ namespace Media::Stories { .scheduleBoxStyle = (viewerStyle ? viewerScheduleStyle() : HistoryView::ScheduleBoxStyleArgs()), + .premiumRequiredError = SharePremiumRequiredError(), }); }