Show toast on share attempt to premium required.

This commit is contained in:
John Preston 2024-01-18 10:39:00 +04:00
parent eda7118df9
commit f3f660a180
12 changed files with 255 additions and 64 deletions

View file

@ -232,9 +232,7 @@ void PeerListBox::resizeEvent(QResizeEvent *e) {
void PeerListBox::paintEvent(QPaintEvent *e) { void PeerListBox::paintEvent(QPaintEvent *e) {
auto p = QPainter(this); auto p = QPainter(this);
const auto &bg = (_controller->listSt() const auto &bg = _controller->computeListSt().bg;
? *_controller->listSt()
: st::peerListBox).bg;
const auto fill = QRect( const auto fill = QRect(
0, 0,
_addedTopScrollSkip, _addedTopScrollSkip,
@ -1255,6 +1253,16 @@ not_null<PeerListRow*> PeerListContent::rowAt(int index) const {
return _rows[index].get(); 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) { void PeerListContent::setDescription(object_ptr<Ui::FlatLabel> description) {
_description = std::move(description); _description = std::move(description);
if (_description) { if (_description) {
@ -1349,6 +1357,7 @@ void PeerListContent::refreshRows() {
if (_mouseSelection) { if (_mouseSelection) {
selectByMouse(QCursor::pos()); selectByMouse(QCursor::pos());
} }
loadProfilePhotos();
update(); update();
} }
@ -1903,7 +1912,9 @@ void PeerListContent::mouseLeftGeometry() {
} }
void PeerListContent::loadProfilePhotos() { void PeerListContent::loadProfilePhotos() {
if (_visibleTop >= _visibleBottom) return; if (_visibleTop >= _visibleBottom) {
return;
}
auto yFrom = _visibleTop; auto yFrom = _visibleTop;
auto yTo = _visibleBottom + (_visibleBottom - _visibleTop) * PreloadHeightsCount; auto yTo = _visibleBottom + (_visibleBottom - _visibleTop) * PreloadHeightsCount;

View file

@ -332,6 +332,8 @@ public:
virtual void peerListScrollToTop() = 0; virtual void peerListScrollToTop() = 0;
virtual int peerListFullRowsCount() = 0; virtual int peerListFullRowsCount() = 0;
virtual PeerListRow *peerListFindRow(PeerListRowId id) = 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 std::optional<QPoint> peerListLastRowMousePosition() = 0;
virtual void peerListSortRows(Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) = 0; virtual void peerListSortRows(Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) = 0;
virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0; virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
@ -627,6 +629,8 @@ public:
void convertRowToSearchResult(not_null<PeerListRow*> row); void convertRowToSearchResult(not_null<PeerListRow*> row);
int fullRowsCount() const; int fullRowsCount() const;
not_null<PeerListRow*> rowAt(int index) 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 setDescription(object_ptr<Ui::FlatLabel> description);
void setSearchLoading(object_ptr<Ui::FlatLabel> loading); void setSearchLoading(object_ptr<Ui::FlatLabel> loading);
void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults); void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults);
@ -908,6 +912,12 @@ public:
not_null<PeerListRow*> peerListRowAt(int index) override { not_null<PeerListRow*> peerListRowAt(int index) override {
return _content->rowAt(index); return _content->rowAt(index);
} }
int peerListSearchRowsCount() override {
return _content->searchRowsCount();
}
not_null<PeerListRow*> peerListSearchRowAt(int index) override {
return _content->searchRowAt(index);
}
void peerListRefreshRows() override { void peerListRefreshRows() override {
_content->refreshRows(); _content->refreshRows();
} }

View file

@ -271,39 +271,17 @@ ChatsListBoxController::Row::Row(
, _delegate(delegate) { , _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( auto ChatsListBoxController::Row::generatePaintUserpicCallback(
bool forceRound) bool forceRound)
-> PaintRoundImageCallback { -> PaintRoundImageCallback {
auto result = PeerListRow::generatePaintUserpicCallback(forceRound); auto result = PeerListRow::generatePaintUserpicCallback(forceRound);
if (_locked) { if (_locked) {
AssertIsDebug(); const auto st = _delegate
const auto st = /*_delegate ? _delegate->rowSt() : */&st::defaultPeerListItem; ? _delegate->rowSt().get()
: &st::defaultPeerListItem;
return [=](Painter &p, int x, int y, int outerWidth, int size) { return [=](Painter &p, int x, int y, int outerWidth, int size) {
result(p, x, y, outerWidth, size); result(p, x, y, outerWidth, size);
PaintLock(p, st, x, y, outerWidth, size); PaintPremiumRequiredLock(p, st, x, y, outerWidth, size);
}; };
} }
return result; return result;
@ -475,7 +453,7 @@ void PeerListStories::process(not_null<PeerListRow*> row) {
bool PeerListStories::handleClick(not_null<PeerData*> peer) { bool PeerListStories::handleClick(not_null<PeerData*> peer) {
const auto point = _delegate->peerListLastRowMousePosition(); 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 (point && point->x() < st.photoPosition.x() + st.photoSize) {
if (const auto window = peer->session().tryResolveWindow()) { if (const auto window = peer->session().tryResolveWindow()) {
if (const auto user = peer->asUser()) { if (const auto user = peer->asUser()) {
@ -492,9 +470,9 @@ bool PeerListStories::handleClick(not_null<PeerData*> peer) {
void PeerListStories::prepare(not_null<PeerListDelegate*> delegate) { void PeerListStories::prepare(not_null<PeerListDelegate*> delegate) {
_delegate = delegate; _delegate = delegate;
_unreadBrush = PeerListStoriesGradient(*_controller->listSt()); _unreadBrush = PeerListStoriesGradient(_controller->computeListSt());
style::PaletteChanged() | rpl::start_with_next([=] { style::PaletteChanged() | rpl::start_with_next([=] {
_unreadBrush = PeerListStoriesGradient(*_controller->listSt()); _unreadBrush = PeerListStoriesGradient(_controller->computeListSt());
updateColors(); updateColors();
}, _lifetime); }, _lifetime);
@ -738,9 +716,7 @@ void ChooseRecipientBoxController::prepareViewHook() {
} }
void ChooseRecipientBoxController::refreshLockedRows() { void ChooseRecipientBoxController::refreshLockedRows() {
auto count = delegate()->peerListFullRowsCount(); const auto process = [&](not_null<PeerListRow*> raw) {
for (auto i = 0; i != count; ++i) {
const auto raw = delegate()->peerListRowAt(i);
const auto row = static_cast<Row*>(raw.get()); const auto row = static_cast<Row*>(raw.get());
if (const auto user = row->peer()->asUser()) { if (const auto user = row->peer()->asUser()) {
const auto history = row->history(); const auto history = row->history();
@ -751,6 +727,14 @@ void ChooseRecipientBoxController::refreshLockedRows() {
delegate()->peerListUpdateRow(row); 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) { void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
if (showLockedError(row)) { if (showLockedError(row)) {
return; return;
@ -845,7 +833,7 @@ auto ChooseRecipientBoxController::createRow(
: ((peer->isBroadcast() && !Data::CanSendAnything(peer)) : ((peer->isBroadcast() && !Data::CanSendAnything(peer))
|| peer->isRepliesChat() || peer->isRepliesChat()
|| (peer->isUser() && (_premiumRequiredError || (peer->isUser() && (_premiumRequiredError
? peer->asUser()->isInaccessible() ? !peer->asUser()->canSendIgnoreRequirePremium()
: !Data::CanSendAnything(peer)))); : !Data::CanSendAnything(peer))));
if (skip) { if (skip) {
return nullptr; return nullptr;
@ -1077,3 +1065,26 @@ auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic)
const auto skip = _filter && !_filter(topic); const auto skip = _filter && !_filter(topic);
return skip ? nullptr : std::make_unique<Row>(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);
}

View file

@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History; class History;
namespace style {
struct PeerListItem;
} // namespace style
namespace Data { namespace Data {
class Thread; class Thread;
class Forum; class Forum;
@ -95,6 +99,8 @@ public:
class RowDelegate { class RowDelegate {
public: public:
virtual void rowPreloadUserpic(not_null<Row*> row); virtual void rowPreloadUserpic(not_null<Row*> row);
[[nodiscard]] virtual auto rowSt()
-> not_null<const style::PeerListItem*> = 0;
}; };
class Row : public PeerListRow { class Row : public PeerListRow {
@ -266,6 +272,7 @@ protected:
private: private:
void refreshLockedRows(); void refreshLockedRows();
void rowPreloadUserpic(not_null<Row*> row) override; void rowPreloadUserpic(not_null<Row*> row) override;
not_null<const style::PeerListItem*> rowSt() override;
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
FnMut<void(not_null<Data::Thread*>)> _callback; FnMut<void(not_null<Data::Thread*>)> _callback;
@ -350,3 +357,11 @@ private:
Fn<bool(not_null<Data::ForumTopic*>)> _filter; 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);

View file

@ -281,9 +281,7 @@ void PeerListsBox::resizeEvent(QResizeEvent *e) {
void PeerListsBox::paintEvent(QPaintEvent *e) { void PeerListsBox::paintEvent(QPaintEvent *e) {
auto p = QPainter(this); auto p = QPainter(this);
const auto &bg = (firstController()->listSt() const auto &bg = firstController()->computeListSt().bg;
? *firstController()->listSt()
: st::peerListBox).bg;
for (const auto &rect : e->region()) { for (const auto &rect : e->region()) {
p.fillRect(rect, bg); p.fillRect(rect, bg);
} }

View file

@ -1194,6 +1194,11 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
} }
}; };
auto filterCallback = [](not_null<Data::Thread*> thread) { auto filterCallback = [](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) {
return true;
}
}
return Data::CanSendTexts(thread); return Data::CanSendTexts(thread);
}; };
auto object = Box<ShareBox>(ShareBox::Descriptor{ auto object = Box<ShareBox>(ShareBox::Descriptor{
@ -1201,6 +1206,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
.copyCallback = std::move(copyCallback), .copyCallback = std::move(copyCallback),
.submitCallback = std::move(submitCallback), .submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback), .filterCallback = std::move(filterCallback),
.premiumRequiredError = SharePremiumRequiredError(),
}); });
*box = Ui::MakeWeak(object.data()); *box = Ui::MakeWeak(object.data());
return object; return object;

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "boxes/share_box.h" #include "boxes/share_box.h"
#include "api/api_premium.h"
#include "base/random.h" #include "base/random.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "base/qthelp_url.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/history_item_helpers.h"
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "history/view/history_view_context_menu.h" // CopyPostLink. #include "history/view/history_view_context_menu.h" // CopyPostLink.
#include "settings/settings_premium.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "chat_helpers/emoji_suggestions_widget.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_game.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_peer_values.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_forum.h" #include "data/data_forum.h"
@ -96,19 +99,23 @@ protected:
private: private:
struct Chat { struct Chat {
Chat( Chat(
not_null<PeerData*> peer, not_null<History*> history,
const style::PeerListItem &st, const style::PeerListItem &st,
Fn<void()> updateCallback); Fn<void()> updateCallback);
not_null<History*> history;
not_null<PeerData*> peer; not_null<PeerData*> peer;
Data::ForumTopic *topic = nullptr; Data::ForumTopic *topic = nullptr;
rpl::lifetime topicLifetime; rpl::lifetime topicLifetime;
Ui::RoundImageCheckbox checkbox; Ui::RoundImageCheckbox checkbox;
Ui::Text::String name; Ui::Text::String name;
Ui::Animations::Simple nameActive; Ui::Animations::Simple nameActive;
bool locked = false;
}; };
void invalidateCache(); void invalidateCache();
bool showLockedError(not_null<Chat*> chat);
void refreshLockedRows();
[[nodiscard]] int displayedChatsCount() const; [[nodiscard]] int displayedChatsCount() const;
[[nodiscard]] not_null<Data::Thread*> chatThread( [[nodiscard]] not_null<Data::Thread*> chatThread(
@ -117,12 +124,14 @@ private:
void paintChat(Painter &p, not_null<Chat*> chat, int index); void paintChat(Painter &p, not_null<Chat*> chat, int index);
void updateChat(not_null<PeerData*> peer); void updateChat(not_null<PeerData*> peer);
void updateChatName(not_null<Chat*> chat); void updateChatName(not_null<Chat*> chat);
void initChatLocked(not_null<Chat*> chat);
void repaintChat(not_null<PeerData*> peer); void repaintChat(not_null<PeerData*> peer);
int chatIndex(not_null<PeerData*> peer) const; int chatIndex(not_null<PeerData*> peer) const;
void repaintChatAtIndex(int index); void repaintChatAtIndex(int index);
Chat *getChatAtIndex(int index); Chat *getChatAtIndex(int index);
void loadProfilePhotos(int yFrom); void loadProfilePhotos();
void preloadUserpic(not_null<Dialogs::Entry*> entry);
void changeCheckState(Chat *chat); void changeCheckState(Chat *chat);
void chooseForumTopic(not_null<Data::Forum*> forum); void chooseForumTopic(not_null<Data::Forum*> forum);
enum class ChangeStateWay { enum class ChangeStateWay {
@ -153,6 +162,7 @@ private:
int _columnCount = 4; int _columnCount = 4;
int _active = -1; int _active = -1;
int _upon = -1; int _upon = -1;
int _visibleTop = 0;
std::unique_ptr<Dialogs::IndexedList> _chatsIndexed; std::unique_ptr<Dialogs::IndexedList> _chatsIndexed;
QString _filter; QString _filter;
@ -649,6 +659,16 @@ ShareBox::Inner::Inner(
_rowHeight = st::shareRowHeight; _rowHeight = st::shareRowHeight;
setAttribute(Qt::WA_OpaquePaintEvent); 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 self = _descriptor.session->user();
const auto selfHistory = self->owner().history(self); const auto selfHistory = self->owner().history(self);
if (_descriptor.filterCallback(selfHistory)) { 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( void ShareBox::Inner::visibleTopBottomUpdated(
int visibleTop, int visibleTop,
int visibleBottom) { int visibleBottom) {
loadProfilePhotos(visibleTop); _visibleTop = visibleTop;
loadProfilePhotos();
} }
void ShareBox::Inner::activateSkipRow(int direction) { 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()); 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) { void ShareBox::Inner::repaintChatAtIndex(int index) {
if (index < 0) return; if (index < 0) return;
@ -829,11 +897,11 @@ int ShareBox::Inner::chatIndex(not_null<PeerData*> peer) const {
return -1; return -1;
} }
void ShareBox::Inner::loadProfilePhotos(int yFrom) { void ShareBox::Inner::loadProfilePhotos() {
if (!parentWidget()) return; if (!parentWidget()) {
if (yFrom < 0) { return;
yFrom = 0;
} }
auto yFrom = std::max(_visibleTop, 0);
if (auto part = (yFrom % _rowHeight)) { if (auto part = (yFrom % _rowHeight)) {
yFrom -= part; yFrom -= part;
} }
@ -853,20 +921,38 @@ void ShareBox::Inner::loadProfilePhotos(int yFrom) {
if (((*i)->index() * _rowHeight) >= yTo) { if (((*i)->index() * _rowHeight) >= yTo) {
break; break;
} }
(*i)->entry()->chatListPreloadData(); preloadUserpic((*i)->entry());
} }
} }
} else if (!_filtered.empty()) { } else {
int from = yFrom / _rowHeight; const auto from = std::max(yFrom / _rowHeight, 0);
if (from < 0) from = 0; const auto to = std::max((yTo / _rowHeight) + 1, from);
if (from < _filtered.size()) {
int to = (yTo / _rowHeight) + 1;
if (to > _filtered.size()) to = _filtered.size();
for (; from < to; ++from) { const auto ffrom = from;
_filtered[from]->entry()->chatListPreloadData(); 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)) { if (const auto data = static_cast<Chat*>(row->attached)) {
return data; 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)) { if (const auto i = _dataMap.find(peer); i != end(_dataMap)) {
row->attached = i->second.get(); row->attached = i->second.get();
return i->second.get(); return i->second.get();
} }
const auto &[i, ok] = _dataMap.emplace( const auto &[i, ok] = _dataMap.emplace(
peer, peer,
std::make_unique<Chat>(peer, _st.item, [=] { repaintChat(peer); })); std::make_unique<Chat>(history, _st.item, [=] {
repaintChat(peer);
}));
updateChatName(i->second.get()); updateChatName(i->second.get());
initChatLocked(i->second.get());
row->attached = i->second.get(); row->attached = i->second.get();
return i->second.get(); return i->second.get();
} }
@ -919,6 +1009,16 @@ void ShareBox::Inner::paintChat(
auto photoTop = st::sharePhotoTop; auto photoTop = st::sharePhotoTop;
chat->checkbox.paint(p, x + photoLeft, y + photoTop, outerWidth); 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.); auto nameActive = chat->nameActive.value((index == _active) ? 1. : 0.);
p.setPen(anim::pen(_st.item.nameFg, _st.item.nameFgChecked, nameActive)); p.setPen(anim::pen(_st.item.nameFg, _st.item.nameFgChecked, nameActive));
@ -929,10 +1029,11 @@ void ShareBox::Inner::paintChat(
} }
ShareBox::Inner::Chat::Chat( ShareBox::Inner::Chat::Chat(
not_null<PeerData*> peer, not_null<History*> history,
const style::PeerListItem &st, const style::PeerListItem &st,
Fn<void()> updateCallback) Fn<void()> updateCallback)
: peer(peer) : history(history)
, peer(history->peer)
, checkbox( , checkbox(
st.checkbox, st.checkbox,
updateCallback, updateCallback,
@ -1062,7 +1163,7 @@ void ShareBox::Inner::resizeEvent(QResizeEvent *e) {
} }
void ShareBox::Inner::changeCheckState(Chat *chat) { void ShareBox::Inner::changeCheckState(Chat *chat) {
if (!chat) { if (!chat || showLockedError(chat)) {
return; return;
} else if (!_filter.isEmpty()) { } else if (!_filter.isEmpty()) {
const auto history = chat->peer->owner().history(chat->peer); const auto history = chat->peer->owner().history(chat->peer);
@ -1194,8 +1295,8 @@ void ShareBox::Inner::updateFilter(QString filter) {
_searchRequests.fire({}); _searchRequests.fire({});
} }
setActive(-1); setActive(-1);
loadProfilePhotos();
update(); update();
loadProfilePhotos(0);
} }
} }
@ -1234,10 +1335,11 @@ void ShareBox::Inner::peopleReceived(
} }
_byUsernameFiltered.push_back(peer); _byUsernameFiltered.push_back(peer);
d_byUsernameFiltered.push_back(std::make_unique<Chat>( d_byUsernameFiltered.push_back(std::make_unique<Chat>(
peer, history,
_st.item, _st.item,
[=] { repaintChat(peer); })); [=] { repaintChat(peer); }));
updateChatName(d_byUsernameFiltered.back().get()); updateChatName(d_byUsernameFiltered.back().get());
initChatLocked(d_byUsernameFiltered.back().get());
} }
} }
}; };
@ -1256,6 +1358,7 @@ void ShareBox::Inner::refresh() {
} else { } else {
resize(width(), st::noContactsHeight); resize(width(), st::noContactsHeight);
} }
loadProfilePhotos();
update(); update();
} }
@ -1525,6 +1628,11 @@ void FastShareMessage(
const auto requiredRight = item->requiredSendRight(); const auto requiredRight = item->requiredSendRight();
const auto requiresInline = item->requiresSendInlineRight(); const auto requiresInline = item->requiresSendInlineRight();
auto filterCallback = [=](not_null<Data::Thread*> thread) { auto filterCallback = [=](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) {
return true;
}
}
return Data::CanSend(thread, requiredRight) return Data::CanSend(thread, requiredRight)
&& (!requiresInline && (!requiresInline
|| Data::CanSend(thread, ChatRestriction::SendInline)) || Data::CanSend(thread, ChatRestriction::SendInline))
@ -1547,10 +1655,16 @@ void FastShareMessage(
.show = !hasOnlyForcedForwardedInfo, .show = !hasOnlyForcedForwardedInfo,
.hasCaptions = hasCaptions, .hasCaptions = hasCaptions,
}, },
.premiumRequiredError = SharePremiumRequiredError(),
}), }),
Ui::LayerOption::CloseOther); Ui::LayerOption::CloseOther);
} }
auto SharePremiumRequiredError()
-> Fn<ChooseRecipientPremiumRequiredError(not_null<UserData*>)> {
return WritePremiumRequiredError;
}
void ShareGameScoreByHash( void ShareGameScoreByHash(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
const QString &hash) { const QString &hash) {

View file

@ -69,6 +69,10 @@ void FastShareMessage(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item); not_null<HistoryItem*> item);
struct ChooseRecipientPremiumRequiredError;
[[nodiscard]] auto SharePremiumRequiredError()
-> Fn<ChooseRecipientPremiumRequiredError(not_null<UserData*>)>;
class ShareBox final : public Ui::BoxContent { class ShareBox final : public Ui::BoxContent {
public: public:
using CopyCallback = Fn<void()>; using CopyCallback = Fn<void()>;
@ -101,6 +105,9 @@ public:
bool hasCaptions = false; bool hasCaptions = false;
} forwardOptions; } forwardOptions;
HistoryView::ScheduleBoxStyleArgs scheduleBoxStyle; HistoryView::ScheduleBoxStyleArgs scheduleBoxStyle;
using PremiumRequiredError = ChooseRecipientPremiumRequiredError;
Fn<PremiumRequiredError(not_null<UserData*>)> premiumRequiredError;
}; };
ShareBox(QWidget*, Descriptor &&descriptor); ShareBox(QWidget*, Descriptor &&descriptor);

View file

@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_group_call.h" #include "data/data_group_call.h"
#include "data/data_user.h"
#include "calls/group/calls_group_rtmp.h" #include "calls/group/calls_group_rtmp.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "data/data_changes.h" #include "data/data_changes.h"
@ -191,6 +192,11 @@ object_ptr<ShareBox> ShareInviteLinkBox(
show->showToast(tr::lng_share_done(tr::now)); show->showToast(tr::lng_share_done(tr::now));
}; };
auto filterCallback = [](not_null<Data::Thread*> thread) { 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); return Data::CanSend(thread, ChatRestriction::SendOther);
}; };
@ -227,6 +233,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
.st = &st::groupCallShareBoxList, .st = &st::groupCallShareBoxList,
.stLabel = &st::groupCallField, .stLabel = &st::groupCallField,
.scheduleBoxStyle = scheduleStyle(), .scheduleBoxStyle = scheduleStyle(),
.premiumRequiredError = SharePremiumRequiredError(),
}); });
*box = result.data(); *box = result.data();
return result; return result;

View file

@ -402,6 +402,10 @@ bool UserData::requirePremiumToWriteKnown() const {
return (flags() & UserDataFlag::RequirePremiumToWriteKnown); return (flags() & UserDataFlag::RequirePremiumToWriteKnown);
} }
bool UserData::canSendIgnoreRequirePremium() const {
return !isInaccessible() && !isRepliesChat();
}
bool UserData::readDatesPrivate() const { bool UserData::readDatesPrivate() const {
return (flags() & UserDataFlag::ReadDatesPrivate); return (flags() & UserDataFlag::ReadDatesPrivate);
} }

View file

@ -131,6 +131,7 @@ public:
[[nodiscard]] bool someRequirePremiumToWrite() const; [[nodiscard]] bool someRequirePremiumToWrite() const;
[[nodiscard]] bool meRequiresPremiumToWrite() const; [[nodiscard]] bool meRequiresPremiumToWrite() const;
[[nodiscard]] bool requirePremiumToWriteKnown() const; [[nodiscard]] bool requirePremiumToWriteKnown() const;
[[nodiscard]] bool canSendIgnoreRequirePremium() const;
[[nodiscard]] bool readDatesPrivate() const; [[nodiscard]] bool readDatesPrivate() const;
[[nodiscard]] bool canShareThisContact() const; [[nodiscard]] bool canShareThisContact() const;

View file

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_stories.h" #include "data/data_stories.h"
#include "data/data_thread.h" #include "data/data_thread.h"
#include "data/data_user.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending. #include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/view/history_view_context_menu.h" // CopyStoryLink. #include "history/view/history_view_context_menu.h" // CopyStoryLink.
@ -61,6 +62,11 @@ namespace Media::Stories {
}; };
const auto state = std::make_shared<State>(); const auto state = std::make_shared<State>();
auto filterCallback = [=](not_null<Data::Thread*> thread) { 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) return Data::CanSend(thread, ChatRestriction::SendPhotos)
&& Data::CanSend(thread, ChatRestriction::SendVideos); && Data::CanSend(thread, ChatRestriction::SendVideos);
}; };
@ -189,6 +195,7 @@ namespace Media::Stories {
.scheduleBoxStyle = (viewerStyle .scheduleBoxStyle = (viewerStyle
? viewerScheduleStyle() ? viewerScheduleStyle()
: HistoryView::ScheduleBoxStyleArgs()), : HistoryView::ScheduleBoxStyleArgs()),
.premiumRequiredError = SharePremiumRequiredError(),
}); });
} }