Update API scheme on layer 145.

Restrict send from channels to premium in line with API restrictions.
This commit is contained in:
John Preston 2022-09-02 20:07:53 +04:00
parent e32031963b
commit 9bb2bb09b9
7 changed files with 176 additions and 44 deletions

View File

@ -1935,6 +1935,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_send_anonymous_ph" = "Send anonymously...";
"lng_send_as_title" = "Send message as...";
"lng_send_as_anonymous_admin" = "Anonymous admin";
"lng_send_as_premium_required" = "Subscribe to {link} to be able to comment on behalf of your channels in group chats.";
"lng_send_as_premium_required_link" = "Telegram Premium";
"lng_record_cancel" = "Release outside this field to cancel";
"lng_record_lock_cancel_sure" = "Are you sure you want to stop recording and discard your voice message?";
"lng_record_listen_cancel_sure" = "Are you sure you want to discard your recorded voice message?";

View File

@ -1327,7 +1327,7 @@ searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosi
messages.searchResultsPositions#53b22baf count:int positions:Vector<SearchResultsPosition> = messages.SearchResultsPositions;
channels.sendAsPeers#8356cda9 peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers;
channels.sendAsPeers#f496b0c6 peers:Vector<SendAsPeer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers;
users.userFull#3b6d152e full_user:UserFull chats:Vector<Chat> users:Vector<User> = users.UserFull;
@ -1442,6 +1442,8 @@ account.emailVerifiedLogin#e1bb0d61 email:string sent_code:auth.SentCode = accou
premiumSubscriptionOption#b6f11ebe flags:# current:flags.1?true can_purchase_upgrade:flags.2?true months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumSubscriptionOption;
sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;

View File

@ -663,10 +663,7 @@ int PeerListRow::paintNameIconGetWidth(
int availableWidth,
int outerWidth,
bool selected) {
if (special()
|| _isSavedMessagesChat
|| _isRepliesMessagesChat
|| !_peer->isUser()) {
if (special() || _isSavedMessagesChat || _isRepliesMessagesChat) {
return 0;
}
return _bagde.drawGetWidth(

View File

@ -23,7 +23,7 @@ constexpr auto kRequestEach = 30 * crl::time(1000);
SendAsPeers::SendAsPeers(not_null<Session*> session)
: _session(session)
, _onlyMe({ session->user() }) {
, _onlyMe({ { .peer = session->user(), .premiumRequired = false } }) {
_session->changes().peerUpdates(
Data::PeerUpdate::Flag::Rights
) | rpl::map([=](const Data::PeerUpdate &update) {
@ -60,7 +60,7 @@ void SendAsPeers::refresh(not_null<PeerData*> peer, bool force) {
request(peer);
}
const std::vector<not_null<PeerData*>> &SendAsPeers::list(
const std::vector<SendAsPeer> &SendAsPeers::list(
not_null<PeerData*> peer) const {
const auto i = _lists.find(peer);
return (i != end(_lists)) ? i->second : _onlyMe;
@ -108,13 +108,15 @@ not_null<PeerData*> SendAsPeers::resolveChosen(
not_null<PeerData*> SendAsPeers::ResolveChosen(
not_null<PeerData*> peer,
const std::vector<not_null<PeerData*>> &list,
const std::vector<SendAsPeer> &list,
PeerId chosen) {
const auto i = ranges::find(list, chosen, &PeerData::id);
const auto i = ranges::find(list, chosen, [](const SendAsPeer &as) {
return as.peer->id;
});
return (i != end(list))
? (*i)
? i->peer
: !list.empty()
? list.front()
? list.front().peer
: (peer->isMegagroup() && peer->amAnonymous())
? peer
: peer->session().user();
@ -124,21 +126,28 @@ void SendAsPeers::request(not_null<PeerData*> peer) {
peer->session().api().request(MTPchannels_GetSendAs(
peer->input
)).done([=](const MTPchannels_SendAsPeers &result) {
auto list = std::vector<not_null<PeerData*>>();
auto parsed = std::vector<SendAsPeer>();
auto &owner = peer->owner();
result.match([&](const MTPDchannels_sendAsPeers &data) {
owner.processUsers(data.vusers());
owner.processChats(data.vchats());
for (const auto &id : data.vpeers().v) {
if (const auto peer = owner.peerLoaded(peerFromMTP(id))) {
list.push_back(peer);
const auto &list = data.vpeers().v;
parsed.reserve(list.size());
for (const auto &as : list) {
const auto &data = as.data();
const auto peerId = peerFromMTP(data.vpeer());
if (const auto peer = owner.peerLoaded(peerId)) {
parsed.push_back({
.peer = peer,
.premiumRequired = data.is_premium_required(),
});
}
}
});
if (list.size() > 1) {
if (parsed.size() > 1) {
auto &now = _lists[peer];
if (now != list) {
now = std::move(list);
if (now != parsed) {
now = std::move(parsed);
_updates.fire_copy(peer);
}
} else if (const auto i = _lists.find(peer); i != end(_lists)) {

View File

@ -13,13 +13,20 @@ namespace Main {
class Session;
struct SendAsPeer {
not_null<PeerData*> peer;
bool premiumRequired = false;
friend inline auto operator<=>(SendAsPeer, SendAsPeer) = default;
};
class SendAsPeers final {
public:
explicit SendAsPeers(not_null<Session*> session);
bool shouldChoose(not_null<PeerData*> peer);
void refresh(not_null<PeerData*> peer, bool force = false);
[[nodiscard]] const std::vector<not_null<PeerData*>> &list(
[[nodiscard]] const std::vector<SendAsPeer> &list(
not_null<PeerData*> peer) const;
[[nodiscard]] rpl::producer<not_null<PeerData*>> updated() const;
@ -33,18 +40,16 @@ public:
[[nodiscard]] static not_null<PeerData*> ResolveChosen(
not_null<PeerData*> peer,
const std::vector<not_null<PeerData*>> &list,
const std::vector<SendAsPeer> &list,
PeerId chosen);
private:
void request(not_null<PeerData*> peer);
const not_null<Session*> _session;
const std::vector<not_null<PeerData*>> _onlyMe;
const std::vector<SendAsPeer> _onlyMe;
base::flat_map<
not_null<PeerData*>,
std::vector<not_null<PeerData*>>> _lists;
base::flat_map<not_null<PeerData*>, std::vector<SendAsPeer>> _lists;
base::flat_map<not_null<PeerData*>, crl::time> _lastRequestTime;
base::flat_map<not_null<PeerData*>, PeerId> _chosen;

View File

@ -13,21 +13,45 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_values.h"
#include "history/history.h"
#include "ui/controls/send_as_button.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "window/window_session_controller.h"
#include "main/main_session.h"
#include "main/session/send_as_peers.h"
#include "lang/lang_keys.h"
#include "settings/settings_premium.h"
#include "styles/style_calls.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
namespace Ui {
namespace {
class Row final : public PeerListRow {
public:
explicit Row(const Main::SendAsPeer &sendAsPeer);
int paintNameIconGetWidth(
Painter &p,
Fn<void()> repaint,
crl::time now,
int nameLeft,
int nameTop,
int nameWidth,
int availableWidth,
int outerWidth,
bool selected) override;
private:
bool _premiumRequired = false;
};
class ListController final : public PeerListController {
public:
ListController(
std::vector<not_null<PeerData*>> list,
std::vector<Main::SendAsPeer> list,
not_null<PeerData*> selected);
Main::Session &session() const override;
@ -37,35 +61,78 @@ public:
[[nodiscard]] rpl::producer<not_null<PeerData*>> clicked() const;
private:
std::unique_ptr<PeerListRow> createRow(not_null<PeerData*> peer);
std::unique_ptr<Row> createRow(const Main::SendAsPeer &sendAsPeer);
std::vector<not_null<PeerData*>> _list;
std::vector<Main::SendAsPeer> _list;
not_null<PeerData*> _selected;
rpl::event_stream<not_null<PeerData*>> _clicked;
};
Row::Row(const Main::SendAsPeer &sendAsPeer)
: PeerListRow(sendAsPeer.peer)
, _premiumRequired(sendAsPeer.premiumRequired) {
}
int Row::paintNameIconGetWidth(
Painter &p,
Fn<void()> repaint,
crl::time now,
int nameLeft,
int nameTop,
int nameWidth,
int availableWidth,
int outerWidth,
bool selected) {
if (_premiumRequired && !peer()->session().premium()) {
const auto &icon = st::emojiPremiumRequired;
availableWidth -= icon.width();
const auto x = nameLeft + std::min(nameWidth, availableWidth);
icon.paint(p, x, nameTop, outerWidth);
return icon.width();
}
return PeerListRow::paintNameIconGetWidth(
p,
std::move(repaint),
now,
nameLeft,
nameTop,
nameWidth,
availableWidth,
outerWidth,
selected);
}
ListController::ListController(
std::vector<not_null<PeerData*>> list,
std::vector<Main::SendAsPeer> list,
not_null<PeerData*> selected)
: PeerListController()
, _list(std::move(list))
, _selected(selected) {
Data::AmPremiumValue(
&selected->session()
) | rpl::skip(1) | rpl::start_with_next([=] {
const auto count = delegate()->peerListFullRowsCount();
for (auto i = 0; i != count; ++i) {
delegate()->peerListUpdateRow(
delegate()->peerListRowAt(i));
}
}, lifetime());
}
Main::Session &ListController::session() const {
return _selected->session();
}
std::unique_ptr<PeerListRow> ListController::createRow(
not_null<PeerData*> peer) {
auto result = std::make_unique<PeerListRow>(peer);
if (peer->isSelf()) {
std::unique_ptr<Row> ListController::createRow(
const Main::SendAsPeer &sendAsPeer) {
auto result = std::make_unique<Row>(sendAsPeer);
if (sendAsPeer.peer->isSelf()) {
result->setCustomStatus(
tr::lng_group_call_join_as_personal(tr::now));
} else if (peer->isMegagroup()) {
} else if (sendAsPeer.peer->isMegagroup()) {
result->setCustomStatus(tr::lng_send_as_anonymous_admin(tr::now));
} else if (const auto channel = peer->asChannel()) {
} else if (const auto channel = sendAsPeer.peer->asChannel()) {
result->setCustomStatus(tr::lng_chat_status_subscribers(
tr::now,
lt_count,
@ -76,11 +143,11 @@ std::unique_ptr<PeerListRow> ListController::createRow(
void ListController::prepare() {
delegate()->peerListSetSearchMode(PeerListSearchMode::Disabled);
for (const auto &peer : _list) {
auto row = createRow(peer);
for (const auto &sendAsPeer : _list) {
auto row = createRow(sendAsPeer);
const auto raw = row.get();
delegate()->peerListAppendRow(std::move(row));
if (peer == _selected) {
if (sendAsPeer.peer == _selected) {
delegate()->peerListSetRowChecked(raw, true);
raw->finishCheckedAnimation();
}
@ -100,14 +167,50 @@ rpl::producer<not_null<PeerData*>> ListController::clicked() const {
return _clicked.events();
}
void ShowPremiumPromoToast(not_null<Window::SessionController*> controller) {
using WeakToast = base::weak_ptr<Ui::Toast::Instance>;
const auto toast = std::make_shared<WeakToast>();
auto link = Ui::Text::Link(
tr::lng_send_as_premium_required_link(tr::now));
link.entities.push_back(
EntityInText(EntityType::Semibold, 0, link.text.size()));
const auto config = Ui::Toast::Config{
.text = tr::lng_send_as_premium_required(
tr::now,
lt_link,
link,
Ui::Text::WithEntities),
.st = &st::defaultMultilineToast,
.durationMs = Ui::Toast::kDefaultDuration * 2,
.multiline = true,
.filter = crl::guard(&controller->session(), [=](
const ClickHandlerPtr &,
Qt::MouseButton button) {
if (button == Qt::LeftButton) {
if (const auto strong = toast->get()) {
strong->hideAnimated();
(*toast) = nullptr;
Settings::ShowPremium(controller, "send_as");
return true;
}
}
return false;
}),
};
(*toast) = Ui::Toast::Show(
Window::Show(controller).toastParent(),
config);
}
} // namespace
void ChooseSendAsBox(
not_null<GenericBox*> box,
std::vector<not_null<PeerData*>> list,
std::vector<Main::SendAsPeer> list,
not_null<PeerData*> chosen,
Fn<void(not_null<PeerData*>)> done) {
Expects(ranges::contains(list, chosen));
Fn<bool(not_null<PeerData*>)> done) {
Expects(ranges::contains(list, chosen, &Main::SendAsPeer::peer));
Expects(done != nullptr);
box->setWidth(st::groupCallJoinAsWidth);
@ -132,8 +235,7 @@ void ChooseSendAsBox(
controller->clicked(
) | rpl::start_with_next([=](not_null<PeerData*> peer) {
const auto weak = MakeWeak(box);
done(peer);
if (weak) {
if (done(peer) && weak) {
box->closeBox();
}
}, box->lifetime());
@ -165,7 +267,18 @@ void SetupSendAsButton(
return;
}
const auto done = [=](not_null<PeerData*> sendAs) {
const auto i = ranges::find(
list,
sendAs,
&Main::SendAsPeer::peer);
if (i != end(list)
&& i->premiumRequired
&& !sendAs->session().premium()) {
ShowPremiumPromoToast(window);
return false;
}
session->sendAsPeers().saveChosen(peer, sendAs);
return true;
};
window->show(Box(
Ui::ChooseSendAsBox,

View File

@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class PeerData;
namespace Main {
struct SendAsPeer;
} // namespace Main
namespace Window {
class SessionController;
} // namespace Window
@ -22,9 +26,9 @@ class SendAsButton;
void ChooseSendAsBox(
not_null<GenericBox*> box,
std::vector<not_null<PeerData*>> list,
std::vector<Main::SendAsPeer> list,
not_null<PeerData*> chosen,
Fn<void(not_null<PeerData*>)> done);
Fn<bool(not_null<PeerData*>)> done);
void SetupSendAsButton(
not_null<SendAsButton*> button,