choose_send_as.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "ui/chat/choose_send_as.h"
  8. #include "boxes/peer_list_box.h"
  9. #include "data/data_peer.h"
  10. #include "data/data_channel.h"
  11. #include "data/data_peer_values.h"
  12. #include "history/history.h"
  13. #include "ui/controls/send_as_button.h"
  14. #include "ui/text/text_utilities.h"
  15. #include "ui/painter.h"
  16. #include "window/window_session_controller.h"
  17. #include "main/main_session.h"
  18. #include "main/session/send_as_peers.h"
  19. #include "lang/lang_keys.h"
  20. #include "settings/settings_premium.h"
  21. #include "styles/style_calls.h"
  22. #include "styles/style_boxes.h"
  23. #include "styles/style_chat.h"
  24. #include "styles/style_chat_helpers.h"
  25. namespace Ui {
  26. namespace {
  27. class Row final : public PeerListRow {
  28. public:
  29. explicit Row(const Main::SendAsPeer &sendAsPeer);
  30. int paintNameIconGetWidth(
  31. Painter &p,
  32. Fn<void()> repaint,
  33. crl::time now,
  34. int nameLeft,
  35. int nameTop,
  36. int nameWidth,
  37. int availableWidth,
  38. int outerWidth,
  39. bool selected) override;
  40. private:
  41. bool _premiumRequired = false;
  42. };
  43. class ListController final : public PeerListController {
  44. public:
  45. ListController(
  46. std::vector<Main::SendAsPeer> list,
  47. not_null<PeerData*> selected);
  48. Main::Session &session() const override;
  49. void prepare() override;
  50. void rowClicked(not_null<PeerListRow*> row) override;
  51. [[nodiscard]] rpl::producer<not_null<PeerData*>> clicked() const;
  52. private:
  53. std::unique_ptr<Row> createRow(const Main::SendAsPeer &sendAsPeer);
  54. std::vector<Main::SendAsPeer> _list;
  55. not_null<PeerData*> _selected;
  56. rpl::event_stream<not_null<PeerData*>> _clicked;
  57. };
  58. Row::Row(const Main::SendAsPeer &sendAsPeer)
  59. : PeerListRow(sendAsPeer.peer)
  60. , _premiumRequired(sendAsPeer.premiumRequired) {
  61. }
  62. int Row::paintNameIconGetWidth(
  63. Painter &p,
  64. Fn<void()> repaint,
  65. crl::time now,
  66. int nameLeft,
  67. int nameTop,
  68. int nameWidth,
  69. int availableWidth,
  70. int outerWidth,
  71. bool selected) {
  72. if (_premiumRequired && !peer()->session().premium()) {
  73. const auto &icon = st::emojiPremiumRequired;
  74. availableWidth -= icon.width();
  75. const auto x = nameLeft + std::min(nameWidth, availableWidth);
  76. icon.paint(p, x, nameTop, outerWidth);
  77. return icon.width();
  78. }
  79. return PeerListRow::paintNameIconGetWidth(
  80. p,
  81. std::move(repaint),
  82. now,
  83. nameLeft,
  84. nameTop,
  85. nameWidth,
  86. availableWidth,
  87. outerWidth,
  88. selected);
  89. }
  90. ListController::ListController(
  91. std::vector<Main::SendAsPeer> list,
  92. not_null<PeerData*> selected)
  93. : PeerListController()
  94. , _list(std::move(list))
  95. , _selected(selected) {
  96. Data::AmPremiumValue(
  97. &selected->session()
  98. ) | rpl::skip(1) | rpl::start_with_next([=] {
  99. const auto count = delegate()->peerListFullRowsCount();
  100. for (auto i = 0; i != count; ++i) {
  101. delegate()->peerListUpdateRow(
  102. delegate()->peerListRowAt(i));
  103. }
  104. }, lifetime());
  105. }
  106. Main::Session &ListController::session() const {
  107. return _selected->session();
  108. }
  109. std::unique_ptr<Row> ListController::createRow(
  110. const Main::SendAsPeer &sendAsPeer) {
  111. auto result = std::make_unique<Row>(sendAsPeer);
  112. if (sendAsPeer.peer->isSelf()) {
  113. result->setCustomStatus(
  114. tr::lng_group_call_join_as_personal(tr::now));
  115. } else if (sendAsPeer.peer->isMegagroup()) {
  116. result->setCustomStatus(tr::lng_send_as_anonymous_admin(tr::now));
  117. } else if (const auto channel = sendAsPeer.peer->asChannel()) {
  118. result->setCustomStatus(tr::lng_chat_status_subscribers(
  119. tr::now,
  120. lt_count,
  121. channel->membersCount()));
  122. }
  123. return result;
  124. }
  125. void ListController::prepare() {
  126. delegate()->peerListSetSearchMode(PeerListSearchMode::Disabled);
  127. for (const auto &sendAsPeer : _list) {
  128. auto row = createRow(sendAsPeer);
  129. const auto raw = row.get();
  130. delegate()->peerListAppendRow(std::move(row));
  131. if (sendAsPeer.peer == _selected) {
  132. delegate()->peerListSetRowChecked(raw, true);
  133. raw->finishCheckedAnimation();
  134. }
  135. }
  136. delegate()->peerListRefreshRows();
  137. }
  138. void ListController::rowClicked(not_null<PeerListRow*> row) {
  139. const auto peer = row->peer();
  140. if (peer == _selected) {
  141. return;
  142. }
  143. _clicked.fire_copy(peer);
  144. }
  145. rpl::producer<not_null<PeerData*>> ListController::clicked() const {
  146. return _clicked.events();
  147. }
  148. } // namespace
  149. void ChooseSendAsBox(
  150. not_null<GenericBox*> box,
  151. std::vector<Main::SendAsPeer> list,
  152. not_null<PeerData*> chosen,
  153. Fn<bool(not_null<PeerData*>)> done) {
  154. Expects(ranges::contains(list, chosen, &Main::SendAsPeer::peer));
  155. Expects(done != nullptr);
  156. box->setWidth(st::groupCallJoinAsWidth);
  157. box->setTitle(tr::lng_send_as_title());
  158. const auto &labelSt = st::confirmPhoneAboutLabel;
  159. box->addRow(object_ptr<Ui::FlatLabel>(
  160. box,
  161. tr::lng_group_call_join_as_about(),
  162. labelSt));
  163. auto &lifetime = box->lifetime();
  164. const auto delegate = lifetime.make_state<
  165. PeerListContentDelegateSimple
  166. >();
  167. const auto controller = lifetime.make_state<ListController>(
  168. list,
  169. chosen);
  170. controller->setStyleOverrides(
  171. &st::peerListJoinAsList,
  172. nullptr);
  173. controller->clicked(
  174. ) | rpl::start_with_next([=](not_null<PeerData*> peer) {
  175. const auto weak = MakeWeak(box);
  176. if (done(peer) && weak) {
  177. box->closeBox();
  178. }
  179. }, box->lifetime());
  180. const auto content = box->addRow(
  181. object_ptr<PeerListContent>(box, controller),
  182. style::margins());
  183. delegate->setContent(content);
  184. controller->setDelegate(delegate);
  185. box->addButton(tr::lng_box_done(), [=] { box->closeBox(); });
  186. }
  187. void SetupSendAsButton(
  188. not_null<SendAsButton*> button,
  189. rpl::producer<PeerData*> active,
  190. not_null<Window::SessionController*> window) {
  191. using namespace rpl::mappers;
  192. const auto current = button->lifetime().make_state<
  193. rpl::variable<PeerData*>
  194. >(std::move(active));
  195. button->setClickedCallback([=] {
  196. const auto peer = current->current();
  197. if (!peer) {
  198. return;
  199. }
  200. const auto session = &peer->session();
  201. const auto &list = session->sendAsPeers().list(peer);
  202. if (list.size() < 2) {
  203. return;
  204. }
  205. const auto done = [=](not_null<PeerData*> sendAs) {
  206. const auto i = ranges::find(
  207. list,
  208. sendAs,
  209. &Main::SendAsPeer::peer);
  210. if (i != end(list)
  211. && i->premiumRequired
  212. && !sendAs->session().premium()) {
  213. Settings::ShowPremiumPromoToast(
  214. window->uiShow(),
  215. tr::lng_send_as_premium_required(
  216. tr::now,
  217. lt_link,
  218. Ui::Text::Link(
  219. Ui::Text::Bold(
  220. tr::lng_send_as_premium_required_link(
  221. tr::now))),
  222. Ui::Text::WithEntities),
  223. u"send_as"_q);
  224. return false;
  225. }
  226. session->sendAsPeers().saveChosen(peer, sendAs);
  227. return true;
  228. };
  229. window->show(Box(
  230. Ui::ChooseSendAsBox,
  231. list,
  232. session->sendAsPeers().resolveChosen(peer),
  233. done));
  234. });
  235. auto userpic = current->value(
  236. ) | rpl::filter([=](PeerData *peer) {
  237. return peer && peer->isChannel();
  238. }) | rpl::map([=](not_null<PeerData*> peer) {
  239. const auto channel = peer->asChannel();
  240. auto updates = rpl::single(
  241. rpl::empty
  242. ) | rpl::then(channel->session().sendAsPeers().updated(
  243. ) | rpl::filter(
  244. _1 == channel
  245. ) | rpl::to_empty);
  246. return rpl::combine(
  247. std::move(updates),
  248. channel->adminRightsValue()
  249. ) | rpl::map([=] {
  250. return channel->session().sendAsPeers().resolveChosen(channel);
  251. }) | rpl::distinct_until_changed(
  252. ) | rpl::map([=](not_null<PeerData*> chosen) {
  253. return Data::PeerUserpicImageValue(
  254. chosen,
  255. st::sendAsButton.size * style::DevicePixelRatio());
  256. }) | rpl::flatten_latest();
  257. }) | rpl::flatten_latest();
  258. std::move(
  259. userpic
  260. ) | rpl::start_with_next([=](QImage &&userpic) {
  261. button->setUserpic(std::move(userpic));
  262. }, button->lifetime());
  263. }
  264. void SetupSendAsButton(
  265. not_null<SendAsButton*> button,
  266. not_null<Window::SessionController*> window) {
  267. auto active = window->activeChatValue(
  268. ) | rpl::map([=](Dialogs::Key key) {
  269. return key.history() ? key.history()->peer.get() : nullptr;
  270. });
  271. SetupSendAsButton(button, std::move(active), window);
  272. }
  273. } // namespace Ui