mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Show toast on forward attempt to premium required.
This commit is contained in:
parent
7c468052e6
commit
eda7118df9
12 changed files with 428 additions and 64 deletions
|
@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_peer_values.h"
|
#include "data/data_peer_values.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
#include "main/main_account.h"
|
#include "main/main_account.h"
|
||||||
#include "main/main_app_config.h"
|
#include "main/main_app_config.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -334,6 +338,72 @@ const Data::SubscriptionOptions &Premium::subscriptionOptions() const {
|
||||||
return _subscriptionOptions;
|
return _subscriptionOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> Premium::somePremiumRequiredResolved() const {
|
||||||
|
return _somePremiumRequiredResolved.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Premium::resolvePremiumRequired(not_null<UserData*> user) {
|
||||||
|
_resolvePremiumRequiredUsers.emplace(user);
|
||||||
|
if (!_premiumRequiredRequestScheduled
|
||||||
|
&& _resolvePremiumRequestedUsers.empty()) {
|
||||||
|
_premiumRequiredRequestScheduled = true;
|
||||||
|
crl::on_main(_session, [=] {
|
||||||
|
requestPremiumRequiredSlice();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Premium::requestPremiumRequiredSlice() {
|
||||||
|
_premiumRequiredRequestScheduled = false;
|
||||||
|
if (!_resolvePremiumRequestedUsers.empty()
|
||||||
|
|| _resolvePremiumRequiredUsers.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
constexpr auto kPerRequest = 100;
|
||||||
|
auto users = MTP_vector_from_range(_resolvePremiumRequiredUsers
|
||||||
|
| ranges::views::transform(&UserData::inputUser));
|
||||||
|
if (users.v.size() > kPerRequest) {
|
||||||
|
auto shortened = users.v;
|
||||||
|
shortened.resize(kPerRequest);
|
||||||
|
users = MTP_vector<MTPInputUser>(std::move(shortened));
|
||||||
|
const auto from = begin(_resolvePremiumRequiredUsers);
|
||||||
|
_resolvePremiumRequestedUsers = { from, from + kPerRequest };
|
||||||
|
_resolvePremiumRequiredUsers.erase(from, from + kPerRequest);
|
||||||
|
} else {
|
||||||
|
_resolvePremiumRequestedUsers
|
||||||
|
= base::take(_resolvePremiumRequiredUsers);
|
||||||
|
}
|
||||||
|
const auto finish = [=](const QVector<MTPBool> &list) {
|
||||||
|
constexpr auto me = UserDataFlag::MeRequiresPremiumToWrite;
|
||||||
|
constexpr auto known = UserDataFlag::RequirePremiumToWriteKnown;
|
||||||
|
constexpr auto mask = me | known;
|
||||||
|
|
||||||
|
auto index = 0;
|
||||||
|
for (const auto &user : base::take(_resolvePremiumRequestedUsers)) {
|
||||||
|
const auto require = (index < list.size())
|
||||||
|
&& mtpIsTrue(list[index++]);
|
||||||
|
user->setFlags((user->flags() & ~mask)
|
||||||
|
| known
|
||||||
|
| (require ? me : UserDataFlag()));
|
||||||
|
}
|
||||||
|
if (!_premiumRequiredRequestScheduled
|
||||||
|
&& !_resolvePremiumRequiredUsers.empty()) {
|
||||||
|
_premiumRequiredRequestScheduled = true;
|
||||||
|
crl::on_main(_session, [=] {
|
||||||
|
requestPremiumRequiredSlice();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_somePremiumRequiredResolved.fire({});
|
||||||
|
};
|
||||||
|
_session->api().request(
|
||||||
|
MTPusers_GetIsPremiumRequiredToContact(std::move(users))
|
||||||
|
).done([=](const MTPVector<MTPBool> &result) {
|
||||||
|
finish(result.v);
|
||||||
|
}).fail([=] {
|
||||||
|
finish({});
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
PremiumGiftCodeOptions::PremiumGiftCodeOptions(not_null<PeerData*> peer)
|
PremiumGiftCodeOptions::PremiumGiftCodeOptions(not_null<PeerData*> peer)
|
||||||
: _peer(peer)
|
: _peer(peer)
|
||||||
, _api(&peer->session().api().instance()) {
|
, _api(&peer->session().api().instance()) {
|
||||||
|
@ -494,4 +564,33 @@ bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const {
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RequirePremiumState ResolveRequiresPremiumToWrite(
|
||||||
|
not_null<History*> history) {
|
||||||
|
const auto user = history->peer->asUser();
|
||||||
|
if (!user
|
||||||
|
|| !user->someRequirePremiumToWrite()
|
||||||
|
|| user->session().premium()) {
|
||||||
|
return RequirePremiumState::No;
|
||||||
|
} else if (user->requirePremiumToWriteKnown()) {
|
||||||
|
return user->meRequiresPremiumToWrite()
|
||||||
|
? RequirePremiumState::Yes
|
||||||
|
: RequirePremiumState::No;
|
||||||
|
}
|
||||||
|
// We allow this potentially-heavy loop because in case we've opened
|
||||||
|
// the chat and have a lot of messages `requires_premium` will be known.
|
||||||
|
for (const auto &block : history->blocks) {
|
||||||
|
for (const auto &view : block->messages) {
|
||||||
|
const auto item = view->data();
|
||||||
|
if (!item->out() && !item->isService()) {
|
||||||
|
using Flag = UserDataFlag;
|
||||||
|
constexpr auto known = Flag::RequirePremiumToWriteKnown;
|
||||||
|
constexpr auto me = Flag::MeRequiresPremiumToWrite;
|
||||||
|
user->setFlags((user->flags() | known) & ~me);
|
||||||
|
return RequirePremiumState::No;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RequirePremiumState::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_subscription_option.h"
|
#include "data/data_subscription_option.h"
|
||||||
#include "mtproto/sender.h"
|
#include "mtproto/sender.h"
|
||||||
|
|
||||||
|
class History;
|
||||||
class ApiWrap;
|
class ApiWrap;
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
|
@ -103,10 +104,14 @@ public:
|
||||||
[[nodiscard]] auto subscriptionOptions() const
|
[[nodiscard]] auto subscriptionOptions() const
|
||||||
-> const Data::SubscriptionOptions &;
|
-> const Data::SubscriptionOptions &;
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<> somePremiumRequiredResolved() const;
|
||||||
|
void resolvePremiumRequired(not_null<UserData*> user);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reloadPromo();
|
void reloadPromo();
|
||||||
void reloadStickers();
|
void reloadStickers();
|
||||||
void reloadCloudSet();
|
void reloadCloudSet();
|
||||||
|
void requestPremiumRequiredSlice();
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
MTP::Sender _api;
|
MTP::Sender _api;
|
||||||
|
@ -143,6 +148,11 @@ private:
|
||||||
|
|
||||||
Data::SubscriptionOptions _subscriptionOptions;
|
Data::SubscriptionOptions _subscriptionOptions;
|
||||||
|
|
||||||
|
rpl::event_stream<> _somePremiumRequiredResolved;
|
||||||
|
base::flat_set<not_null<UserData*>> _resolvePremiumRequiredUsers;
|
||||||
|
base::flat_set<not_null<UserData*>> _resolvePremiumRequestedUsers;
|
||||||
|
bool _premiumRequiredRequestScheduled = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class PremiumGiftCodeOptions final {
|
class PremiumGiftCodeOptions final {
|
||||||
|
@ -196,4 +206,12 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class RequirePremiumState {
|
||||||
|
Unknown,
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
};
|
||||||
|
[[nodiscard]] RequirePremiumState ResolveRequiresPremiumToWrite(
|
||||||
|
not_null<History*> history);
|
||||||
|
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
|
@ -544,6 +544,12 @@ bool PeerListRow::checked() const {
|
||||||
return _checkbox && _checkbox->checked();
|
return _checkbox && _checkbox->checked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PeerListRow::preloadUserpic() {
|
||||||
|
if (_peer) {
|
||||||
|
_peer->loadUserpic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PeerListRow::setCustomStatus(const QString &status, bool active) {
|
void PeerListRow::setCustomStatus(const QString &status, bool active) {
|
||||||
setStatusText(status);
|
setStatusText(status);
|
||||||
_statusType = active ? StatusType::CustomActive : StatusType::Custom;
|
_statusType = active ? StatusType::CustomActive : StatusType::Custom;
|
||||||
|
@ -1914,10 +1920,7 @@ void PeerListContent::loadProfilePhotos() {
|
||||||
if (to > rowsCount) to = rowsCount;
|
if (to > rowsCount) to = rowsCount;
|
||||||
|
|
||||||
for (auto index = from; index != to; ++index) {
|
for (auto index = from; index != to; ++index) {
|
||||||
const auto row = getRow(RowIndex(index));
|
getRow(RowIndex(index))->preloadUserpic();
|
||||||
if (!row->special()) {
|
|
||||||
row->peer()->loadUserpic();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,8 @@ public:
|
||||||
[[nodiscard]] virtual auto generateNameWords() const
|
[[nodiscard]] virtual auto generateNameWords() const
|
||||||
-> const base::flat_set<QString> &;
|
-> const base::flat_set<QString> &;
|
||||||
|
|
||||||
|
virtual void preloadUserpic();
|
||||||
|
|
||||||
void setCustomStatus(const QString &status, bool active = false);
|
void setCustomStatus(const QString &status, bool active = false);
|
||||||
void clearCustomStatus();
|
void clearCustomStatus();
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
|
|
||||||
#include "api/api_chat_participants.h"
|
#include "api/api_chat_participants.h"
|
||||||
|
#include "api/api_premium.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
#include "boxes/filters/edit_filter_chats_list.h"
|
#include "boxes/filters/edit_filter_chats_list.h"
|
||||||
|
#include "settings/settings_premium.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/effects/round_checkbox.h"
|
#include "ui/effects/round_checkbox.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
@ -19,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "data/data_peer_values.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_stories.h"
|
#include "data/data_stories.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
@ -44,10 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_profile.h"
|
#include "styles/style_profile.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
#include "data/data_stories.h"
|
|
||||||
#include "dialogs/ui/dialogs_stories_content.h"
|
|
||||||
#include "dialogs/ui/dialogs_stories_list.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -257,9 +258,63 @@ bool PeerListGlobalSearchController::isLoading() {
|
||||||
return _timer.isActive() || _requestId;
|
return _timer.isActive() || _requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatsListBoxController::Row::Row(not_null<History*> history)
|
void ChatsListBoxController::RowDelegate::rowPreloadUserpic(
|
||||||
|
not_null<Row*> row) {
|
||||||
|
row->PeerListRow::preloadUserpic();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatsListBoxController::Row::Row(
|
||||||
|
not_null<History*> history,
|
||||||
|
RowDelegate *delegate)
|
||||||
: PeerListRow(history->peer)
|
: PeerListRow(history->peer)
|
||||||
, _history(history) {
|
, _history(history)
|
||||||
|
, _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;
|
||||||
|
return [=](Painter &p, int x, int y, int outerWidth, int size) {
|
||||||
|
result(p, x, y, outerWidth, size);
|
||||||
|
PaintLock(p, st, x, y, outerWidth, size);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatsListBoxController::Row::preloadUserpic() {
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->rowPreloadUserpic(this);
|
||||||
|
} else {
|
||||||
|
PeerListRow::preloadUserpic();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatsListBoxController::ChatsListBoxController(
|
ChatsListBoxController::ChatsListBoxController(
|
||||||
|
@ -629,14 +684,40 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createRow(
|
||||||
return std::make_unique<PeerListRow>(user);
|
return std::make_unique<PeerListRow>(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChooseRecipientPremiumRequiredError WritePremiumRequiredError(
|
||||||
|
not_null<UserData*> user) {
|
||||||
|
return {
|
||||||
|
.text = tr::lng_send_non_premium_message_toast(
|
||||||
|
tr::now,
|
||||||
|
lt_user,
|
||||||
|
TextWithEntities{ user->shortName() },
|
||||||
|
lt_link,
|
||||||
|
Ui::Text::Link(
|
||||||
|
Ui::Text::Bold(
|
||||||
|
tr::lng_send_non_premium_message_toast_link(
|
||||||
|
tr::now))),
|
||||||
|
Ui::Text::RichLangValue),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
ChooseRecipientBoxController::ChooseRecipientBoxController(
|
ChooseRecipientBoxController::ChooseRecipientBoxController(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
FnMut<void(not_null<Data::Thread*>)> callback,
|
FnMut<void(not_null<Data::Thread*>)> callback,
|
||||||
Fn<bool(not_null<Data::Thread*>)> filter)
|
Fn<bool(not_null<Data::Thread*>)> filter)
|
||||||
: ChatsListBoxController(session)
|
: ChooseRecipientBoxController({
|
||||||
, _session(session)
|
.session = session,
|
||||||
, _callback(std::move(callback))
|
.callback = std::move(callback),
|
||||||
, _filter(std::move(filter)) {
|
.filter = std::move(filter),
|
||||||
|
}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ChooseRecipientBoxController::ChooseRecipientBoxController(
|
||||||
|
ChooseRecipientArgs &&args)
|
||||||
|
: ChatsListBoxController(args.session)
|
||||||
|
, _session(args.session)
|
||||||
|
, _callback(std::move(args.callback))
|
||||||
|
, _filter(std::move(args.filter))
|
||||||
|
, _premiumRequiredError(std::move(args.premiumRequiredError)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Main::Session &ChooseRecipientBoxController::session() const {
|
Main::Session &ChooseRecipientBoxController::session() const {
|
||||||
|
@ -645,9 +726,50 @@ Main::Session &ChooseRecipientBoxController::session() const {
|
||||||
|
|
||||||
void ChooseRecipientBoxController::prepareViewHook() {
|
void ChooseRecipientBoxController::prepareViewHook() {
|
||||||
delegate()->peerListSetTitle(tr::lng_forward_choose());
|
delegate()->peerListSetTitle(tr::lng_forward_choose());
|
||||||
|
|
||||||
|
if (_premiumRequiredError) {
|
||||||
|
rpl::merge(
|
||||||
|
Data::AmPremiumValue(_session) | rpl::to_empty,
|
||||||
|
_session->api().premium().somePremiumRequiredResolved()
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
refreshLockedRows();
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChooseRecipientBoxController::refreshLockedRows() {
|
||||||
|
auto count = delegate()->peerListFullRowsCount();
|
||||||
|
for (auto i = 0; i != count; ++i) {
|
||||||
|
const auto raw = delegate()->peerListRowAt(i);
|
||||||
|
const auto row = static_cast<Row*>(raw.get());
|
||||||
|
if (const auto user = row->peer()->asUser()) {
|
||||||
|
const auto history = row->history();
|
||||||
|
const auto locked = (Api::ResolveRequiresPremiumToWrite(history)
|
||||||
|
== Api::RequirePremiumState::Yes);
|
||||||
|
if (row->locked() != locked) {
|
||||||
|
row->setLocked(locked);
|
||||||
|
delegate()->peerListUpdateRow(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChooseRecipientBoxController::rowPreloadUserpic(not_null<Row*> row) {
|
||||||
|
row->PeerListRow::preloadUserpic();
|
||||||
|
|
||||||
|
if (!_premiumRequiredError) {
|
||||||
|
return;
|
||||||
|
} else if (Api::ResolveRequiresPremiumToWrite(row->history())
|
||||||
|
== Api::RequirePremiumState::Unknown) {
|
||||||
|
const auto user = row->peer()->asUser();
|
||||||
|
session().api().premium().resolvePremiumRequired(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
|
void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
|
if (showLockedError(row)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto guard = base::make_weak(this);
|
auto guard = base::make_weak(this);
|
||||||
const auto peer = row->peer();
|
const auto peer = row->peer();
|
||||||
if (const auto forum = peer->forum()) {
|
if (const auto forum = peer->forum()) {
|
||||||
|
@ -698,6 +820,19 @@ void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChooseRecipientBoxController::showLockedError(
|
||||||
|
not_null<PeerListRow*> row) const {
|
||||||
|
if (!static_cast<Row*>(row.get())->locked()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
::Settings::ShowPremiumPromoToast(
|
||||||
|
delegate()->peerListUiShow(),
|
||||||
|
ChatHelpers::ResolveWindowDefault(),
|
||||||
|
_premiumRequiredError(row->peer()->asUser()).text,
|
||||||
|
u"require_premium"_q);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QString ChooseRecipientBoxController::savedMessagesChatStatus() const {
|
QString ChooseRecipientBoxController::savedMessagesChatStatus() const {
|
||||||
return tr::lng_saved_forward_here(tr::now);
|
return tr::lng_saved_forward_here(tr::now);
|
||||||
}
|
}
|
||||||
|
@ -708,8 +843,23 @@ auto ChooseRecipientBoxController::createRow(
|
||||||
const auto skip = _filter
|
const auto skip = _filter
|
||||||
? !_filter(history)
|
? !_filter(history)
|
||||||
: ((peer->isBroadcast() && !Data::CanSendAnything(peer))
|
: ((peer->isBroadcast() && !Data::CanSendAnything(peer))
|
||||||
|| (peer->isUser() && !Data::CanSendAnything(peer)));
|
|| peer->isRepliesChat()
|
||||||
return skip ? nullptr : std::make_unique<Row>(history);
|
|| (peer->isUser() && (_premiumRequiredError
|
||||||
|
? peer->asUser()->isInaccessible()
|
||||||
|
: !Data::CanSendAnything(peer))));
|
||||||
|
if (skip) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto result = std::make_unique<Row>(
|
||||||
|
history,
|
||||||
|
static_cast<RowDelegate*>(this));
|
||||||
|
if (_premiumRequiredError) {
|
||||||
|
const auto require = Api::ResolveRequiresPremiumToWrite(history);
|
||||||
|
if (require == Api::RequirePremiumState::Yes) {
|
||||||
|
result->setLocked(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChooseTopicSearchController::ChooseTopicSearchController(
|
ChooseTopicSearchController::ChooseTopicSearchController(
|
||||||
|
|
|
@ -91,16 +91,34 @@ private:
|
||||||
|
|
||||||
class ChatsListBoxController : public PeerListController {
|
class ChatsListBoxController : public PeerListController {
|
||||||
public:
|
public:
|
||||||
|
class Row;
|
||||||
|
class RowDelegate {
|
||||||
|
public:
|
||||||
|
virtual void rowPreloadUserpic(not_null<Row*> row);
|
||||||
|
};
|
||||||
|
|
||||||
class Row : public PeerListRow {
|
class Row : public PeerListRow {
|
||||||
public:
|
public:
|
||||||
Row(not_null<History*> history);
|
Row(not_null<History*> history, RowDelegate *delegate = nullptr);
|
||||||
|
|
||||||
not_null<History*> history() const {
|
[[nodiscard]] not_null<History*> history() const {
|
||||||
return _history;
|
return _history;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] bool locked() const {
|
||||||
|
return _locked;
|
||||||
|
}
|
||||||
|
void setLocked(bool locked) {
|
||||||
|
_locked = locked;
|
||||||
|
}
|
||||||
|
PaintRoundImageCallback generatePaintUserpicCallback(
|
||||||
|
bool forceRound) override;
|
||||||
|
|
||||||
|
void preloadUserpic() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
not_null<History*> _history;
|
const not_null<History*> _history;
|
||||||
|
RowDelegate *_delegate = nullptr;
|
||||||
|
bool _locked = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -207,14 +225,32 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ChooseRecipientPremiumRequiredError {
|
||||||
|
TextWithEntities text;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] ChooseRecipientPremiumRequiredError WritePremiumRequiredError(
|
||||||
|
not_null<UserData*> user);
|
||||||
|
|
||||||
|
struct ChooseRecipientArgs {
|
||||||
|
not_null<Main::Session*> session;
|
||||||
|
FnMut<void(not_null<Data::Thread*>)> callback;
|
||||||
|
Fn<bool(not_null<Data::Thread*>)> filter;
|
||||||
|
|
||||||
|
using PremiumRequiredError = ChooseRecipientPremiumRequiredError;
|
||||||
|
Fn<PremiumRequiredError(not_null<UserData*>)> premiumRequiredError;
|
||||||
|
};
|
||||||
|
|
||||||
class ChooseRecipientBoxController
|
class ChooseRecipientBoxController
|
||||||
: public ChatsListBoxController
|
: public ChatsListBoxController
|
||||||
, public base::has_weak_ptr {
|
, public base::has_weak_ptr
|
||||||
|
, private ChatsListBoxController::RowDelegate {
|
||||||
public:
|
public:
|
||||||
ChooseRecipientBoxController(
|
ChooseRecipientBoxController(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
FnMut<void(not_null<Data::Thread*>)> callback,
|
FnMut<void(not_null<Data::Thread*>)> callback,
|
||||||
Fn<bool(not_null<Data::Thread*>)> filter = nullptr);
|
Fn<bool(not_null<Data::Thread*>)> filter = nullptr);
|
||||||
|
explicit ChooseRecipientBoxController(ChooseRecipientArgs &&args);
|
||||||
|
|
||||||
Main::Session &session() const override;
|
Main::Session &session() const override;
|
||||||
void rowClicked(not_null<PeerListRow*> row) override;
|
void rowClicked(not_null<PeerListRow*> row) override;
|
||||||
|
@ -225,10 +261,19 @@ protected:
|
||||||
void prepareViewHook() override;
|
void prepareViewHook() override;
|
||||||
std::unique_ptr<Row> createRow(not_null<History*> history) override;
|
std::unique_ptr<Row> createRow(not_null<History*> history) override;
|
||||||
|
|
||||||
|
[[nodiscard]] bool showLockedError(not_null<PeerListRow*> row) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void refreshLockedRows();
|
||||||
|
void rowPreloadUserpic(not_null<Row*> row) 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;
|
||||||
Fn<bool(not_null<Data::Thread*>)> _filter;
|
Fn<bool(not_null<Data::Thread*>)> _filter;
|
||||||
|
Fn<ChooseRecipientPremiumRequiredError(
|
||||||
|
not_null<UserData*>)> _premiumRequiredError;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,29 +18,35 @@ rpl::producer<bool> Show::adjustShadowLeft() const {
|
||||||
return rpl::single(false);
|
return rpl::single(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::SessionController *Show::resolveWindow(WindowUsage usage) const {
|
ResolveWindow ResolveWindowDefault() {
|
||||||
const auto session = &this->session();
|
return [](not_null<Main::Session*> session, WindowUsage usage)
|
||||||
const auto check = [&](Window::Controller *window) {
|
-> Window::SessionController* {
|
||||||
if (const auto controller = window->sessionController()) {
|
const auto check = [&](Window::Controller *window) {
|
||||||
if (&controller->session() == session) {
|
if (const auto controller = window->sessionController()) {
|
||||||
return controller;
|
if (&controller->session() == session) {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return (Window::SessionController*)nullptr;
|
||||||
|
};
|
||||||
|
auto &app = Core::App();
|
||||||
|
if (const auto a = check(app.activeWindow())) {
|
||||||
|
return a;
|
||||||
|
} else if (const auto b = check(app.activePrimaryWindow())) {
|
||||||
|
return b;
|
||||||
|
} else if (const auto c = check(app.windowFor(&session->account()))) {
|
||||||
|
return c;
|
||||||
|
} else if (const auto d = check(
|
||||||
|
app.ensureSeparateWindowForAccount(
|
||||||
|
&session->account()))) {
|
||||||
|
return d;
|
||||||
}
|
}
|
||||||
return (Window::SessionController*)nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
auto &app = Core::App();
|
}
|
||||||
if (const auto a = check(app.activeWindow())) {
|
|
||||||
return a;
|
Window::SessionController *Show::resolveWindow(WindowUsage usage) const {
|
||||||
} else if (const auto b = check(app.activePrimaryWindow())) {
|
return ResolveWindowDefault()(&session(), usage);
|
||||||
return b;
|
|
||||||
} else if (const auto c = check(app.windowFor(&session->account()))) {
|
|
||||||
return c;
|
|
||||||
} else if (const auto d = check(
|
|
||||||
app.ensureSeparateWindowForAccount(
|
|
||||||
&session->account()))) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ChatHelpers
|
} // namespace ChatHelpers
|
||||||
|
|
|
@ -44,6 +44,11 @@ enum class WindowUsage {
|
||||||
PremiumPromo,
|
PremiumPromo,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using ResolveWindow = Fn<Window::SessionController*(
|
||||||
|
not_null<Main::Session*>,
|
||||||
|
WindowUsage)>;
|
||||||
|
[[nodiscard]] ResolveWindow ResolveWindowDefault();
|
||||||
|
|
||||||
class Show : public Main::SessionShow {
|
class Show : public Main::SessionShow {
|
||||||
public:
|
public:
|
||||||
virtual void activate() = 0;
|
virtual void activate() = 0;
|
||||||
|
|
|
@ -892,14 +892,13 @@ void ShowReplyToChatBox(
|
||||||
using Chosen = not_null<Data::Thread*>;
|
using Chosen = not_null<Data::Thread*>;
|
||||||
|
|
||||||
Controller(not_null<Main::Session*> session)
|
Controller(not_null<Main::Session*> session)
|
||||||
: ChooseRecipientBoxController(
|
: ChooseRecipientBoxController({
|
||||||
session,
|
.session = session,
|
||||||
[=](Chosen thread) mutable { _singleChosen.fire_copy(thread); },
|
.callback = [=](Chosen thread) {
|
||||||
nullptr) {
|
_singleChosen.fire_copy(thread);
|
||||||
}
|
},
|
||||||
|
.premiumRequiredError = WritePremiumRequiredError,
|
||||||
void rowClicked(not_null<PeerListRow*> row) override final {
|
}) {
|
||||||
ChooseRecipientBoxController::rowClicked(row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<Chosen> singleChosen() const {
|
[[nodiscard]] rpl::producer<Chosen> singleChosen() const {
|
||||||
|
@ -912,6 +911,7 @@ void ShowReplyToChatBox(
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void prepareViewHook() override {
|
void prepareViewHook() override {
|
||||||
|
ChooseRecipientBoxController::prepareViewHook();
|
||||||
delegate()->peerListSetTitle(tr::lng_reply_in_another_title());
|
delegate()->peerListSetTitle(tr::lng_reply_in_another_title());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1316,6 +1316,22 @@ void ShowPremiumPromoToast(
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
TextWithEntities textWithLink,
|
TextWithEntities textWithLink,
|
||||||
const QString &ref) {
|
const QString &ref) {
|
||||||
|
ShowPremiumPromoToast(show, [=](
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
ChatHelpers::WindowUsage usage) {
|
||||||
|
Expects(&show->session() == session);
|
||||||
|
|
||||||
|
return show->resolveWindow(usage);
|
||||||
|
}, std::move(textWithLink), ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowPremiumPromoToast(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
Fn<Window::SessionController*(
|
||||||
|
not_null<Main::Session*>,
|
||||||
|
ChatHelpers::WindowUsage)> resolveWindow,
|
||||||
|
TextWithEntities textWithLink,
|
||||||
|
const QString &ref) {
|
||||||
using WeakToast = base::weak_ptr<Ui::Toast::Instance>;
|
using WeakToast = base::weak_ptr<Ui::Toast::Instance>;
|
||||||
const auto toast = std::make_shared<WeakToast>();
|
const auto toast = std::make_shared<WeakToast>();
|
||||||
(*toast) = show->showToast({
|
(*toast) = show->showToast({
|
||||||
|
@ -1331,7 +1347,8 @@ void ShowPremiumPromoToast(
|
||||||
if (const auto strong = toast->get()) {
|
if (const auto strong = toast->get()) {
|
||||||
strong->hideAnimated();
|
strong->hideAnimated();
|
||||||
(*toast) = nullptr;
|
(*toast) = nullptr;
|
||||||
if (const auto controller = show->resolveWindow(
|
if (const auto controller = resolveWindow(
|
||||||
|
&show->session(),
|
||||||
ChatHelpers::WindowUsage::PremiumPromo)) {
|
ChatHelpers::WindowUsage::PremiumPromo)) {
|
||||||
Settings::ShowPremium(controller, ref);
|
Settings::ShowPremium(controller, ref);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ struct RoundButton;
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
class Show;
|
class Show;
|
||||||
|
enum class WindowUsage;
|
||||||
} // namespace ChatHelpers
|
} // namespace ChatHelpers
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -28,6 +29,7 @@ class VerticalLayout;
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
|
class SessionShow;
|
||||||
} // namespace Main
|
} // namespace Main
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
@ -61,6 +63,13 @@ void ShowPremiumPromoToast(
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
TextWithEntities textWithLink,
|
TextWithEntities textWithLink,
|
||||||
const QString &ref);
|
const QString &ref);
|
||||||
|
void ShowPremiumPromoToast(
|
||||||
|
std::shared_ptr<::Main::SessionShow> show,
|
||||||
|
Fn<Window::SessionController*(
|
||||||
|
not_null<::Main::Session*>,
|
||||||
|
ChatHelpers::WindowUsage)> resolveWindow,
|
||||||
|
TextWithEntities textWithLink,
|
||||||
|
const QString &ref);
|
||||||
|
|
||||||
struct SubscribeButtonArgs final {
|
struct SubscribeButtonArgs final {
|
||||||
Window::SessionController *controller = nullptr;
|
Window::SessionController *controller = nullptr;
|
||||||
|
|
|
@ -1504,8 +1504,11 @@ void PeerMenuShareContactBox(
|
||||||
*weak = navigation->parentController()->show(
|
*weak = navigation->parentController()->show(
|
||||||
Box<PeerListBox>(
|
Box<PeerListBox>(
|
||||||
std::make_unique<ChooseRecipientBoxController>(
|
std::make_unique<ChooseRecipientBoxController>(
|
||||||
&navigation->session(),
|
ChooseRecipientArgs{
|
||||||
std::move(callback)),
|
.session = &navigation->session(),
|
||||||
|
.callback = std::move(callback),
|
||||||
|
.premiumRequiredError = WritePremiumRequiredError,
|
||||||
|
}),
|
||||||
[](not_null<PeerListBox*> box) {
|
[](not_null<PeerListBox*> box) {
|
||||||
box->addButton(tr::lng_cancel(), [=] {
|
box->addButton(tr::lng_cancel(), [=] {
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
|
@ -1742,10 +1745,12 @@ QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
*weak = navigation->parentController()->show(Box<PeerListBox>(
|
*weak = navigation->parentController()->show(Box<PeerListBox>(
|
||||||
std::make_unique<ChooseRecipientBoxController>(
|
std::make_unique<ChooseRecipientBoxController>(ChooseRecipientArgs{
|
||||||
&navigation->session(),
|
.session = &navigation->session(),
|
||||||
std::move(callback),
|
.callback = std::move(callback),
|
||||||
std::move(filter)),
|
.filter = std::move(filter),
|
||||||
|
.premiumRequiredError = WritePremiumRequiredError,
|
||||||
|
}),
|
||||||
std::move(initBox)));
|
std::move(initBox)));
|
||||||
return weak->data();
|
return weak->data();
|
||||||
}
|
}
|
||||||
|
@ -1799,15 +1804,18 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
using Chosen = not_null<Data::Thread*>;
|
using Chosen = not_null<Data::Thread*>;
|
||||||
|
|
||||||
Controller(not_null<Main::Session*> session)
|
Controller(not_null<Main::Session*> session)
|
||||||
: ChooseRecipientBoxController(
|
: ChooseRecipientBoxController({
|
||||||
session,
|
.session = session,
|
||||||
[=](Chosen thread) mutable { _singleChosen.fire_copy(thread); },
|
.callback = [=](Chosen thread) {
|
||||||
nullptr) {
|
_singleChosen.fire_copy(thread);
|
||||||
|
},
|
||||||
|
.premiumRequiredError = WritePremiumRequiredError,
|
||||||
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void rowClicked(not_null<PeerListRow*> row) override final {
|
void rowClicked(not_null<PeerListRow*> row) override final {
|
||||||
const auto count = delegate()->peerListSelectedRowsCount();
|
const auto count = delegate()->peerListSelectedRowsCount();
|
||||||
if (count && row->peer()->isForum()) {
|
if (showLockedError(row) || (count && row->peer()->isForum())) {
|
||||||
return;
|
return;
|
||||||
} else if (!count || row->peer()->isForum()) {
|
} else if (!count || row->peer()->isForum()) {
|
||||||
ChooseRecipientBoxController::rowClicked(row);
|
ChooseRecipientBoxController::rowClicked(row);
|
||||||
|
@ -2126,10 +2134,12 @@ QPointer<Ui::BoxContent> ShowShareGameBox(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
*weak = navigation->parentController()->show(Box<PeerListBox>(
|
*weak = navigation->parentController()->show(Box<PeerListBox>(
|
||||||
std::make_unique<ChooseRecipientBoxController>(
|
std::make_unique<ChooseRecipientBoxController>(ChooseRecipientArgs{
|
||||||
&navigation->session(),
|
.session = &navigation->session(),
|
||||||
std::move(chosen),
|
.callback = std::move(chosen),
|
||||||
std::move(filter)),
|
.filter = std::move(filter),
|
||||||
|
.premiumRequiredError = WritePremiumRequiredError,
|
||||||
|
}),
|
||||||
std::move(initBox)));
|
std::move(initBox)));
|
||||||
return weak->data();
|
return weak->data();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue