edit_linked_chat_box.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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 "boxes/peers/edit_linked_chat_box.h"
  8. #include "lang/lang_keys.h"
  9. #include "data/data_channel.h"
  10. #include "data/data_chat.h"
  11. #include "settings/settings_common.h" // AddButton.
  12. #include "data/data_changes.h"
  13. #include "ui/widgets/labels.h"
  14. #include "ui/vertical_list.h"
  15. #include "ui/widgets/buttons.h"
  16. #include "ui/wrap/vertical_layout.h"
  17. #include "ui/text/text_utilities.h" // Ui::Text::RichLangValue
  18. #include "boxes/peer_list_box.h"
  19. #include "ui/boxes/confirm_box.h"
  20. #include "boxes/add_contact_box.h"
  21. #include "apiwrap.h"
  22. #include "main/main_session.h"
  23. #include "window/window_session_controller.h"
  24. #include "styles/style_layers.h"
  25. #include "styles/style_menu_icons.h"
  26. #include "styles/style_boxes.h"
  27. #include "styles/style_info.h"
  28. #include "styles/style_settings.h"
  29. namespace {
  30. constexpr auto kEnableSearchRowsCount = 10;
  31. class Controller : public PeerListController, public base::has_weak_ptr {
  32. public:
  33. Controller(
  34. not_null<Window::SessionNavigation*> navigation,
  35. not_null<ChannelData*> channel,
  36. ChannelData *chat,
  37. const std::vector<not_null<PeerData*>> &chats,
  38. Fn<void(ChannelData*)> callback,
  39. Fn<void(not_null<PeerData*>)> showHistoryCallback);
  40. Main::Session &session() const override;
  41. void prepare() override;
  42. void rowClicked(not_null<PeerListRow*> row) override;
  43. int contentWidth() const override;
  44. private:
  45. void choose(not_null<ChannelData*> chat);
  46. void choose(not_null<ChatData*> chat);
  47. not_null<Window::SessionNavigation*> _navigation;
  48. not_null<ChannelData*> _channel;
  49. ChannelData *_chat = nullptr;
  50. std::vector<not_null<PeerData*>> _chats;
  51. Fn<void(ChannelData*)> _callback;
  52. Fn<void(not_null<PeerData*>)> _showHistoryCallback;
  53. ChannelData *_waitForFull = nullptr;
  54. rpl::event_stream<not_null<PeerData*>> _showHistoryRequest;
  55. };
  56. Controller::Controller(
  57. not_null<Window::SessionNavigation*> navigation,
  58. not_null<ChannelData*> channel,
  59. ChannelData *chat,
  60. const std::vector<not_null<PeerData*>> &chats,
  61. Fn<void(ChannelData*)> callback,
  62. Fn<void(not_null<PeerData*>)> showHistoryCallback)
  63. : _navigation(navigation)
  64. , _channel(channel)
  65. , _chat(chat)
  66. , _chats(std::move(chats))
  67. , _callback(std::move(callback))
  68. , _showHistoryCallback(std::move(showHistoryCallback)) {
  69. channel->session().changes().peerUpdates(
  70. Data::PeerUpdate::Flag::FullInfo
  71. ) | rpl::filter([=](const Data::PeerUpdate &update) {
  72. return (update.peer == _waitForFull);
  73. }) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
  74. choose(std::exchange(_waitForFull, nullptr));
  75. }, lifetime());
  76. }
  77. Main::Session &Controller::session() const {
  78. return _channel->session();
  79. }
  80. int Controller::contentWidth() const {
  81. return st::boxWidth;
  82. }
  83. void Controller::prepare() {
  84. const auto appendRow = [&](not_null<PeerData*> chat) {
  85. if (delegate()->peerListFindRow(chat->id.value)) {
  86. return;
  87. }
  88. auto row = std::make_unique<PeerListRow>(chat);
  89. const auto username = chat->username();
  90. row->setCustomStatus(!username.isEmpty()
  91. ? ('@' + username)
  92. : (chat->isChannel() && !chat->isMegagroup())
  93. ? tr::lng_manage_linked_channel_private_status(tr::now)
  94. : tr::lng_manage_discussion_group_private_status(tr::now));
  95. delegate()->peerListAppendRow(std::move(row));
  96. };
  97. if (_chat) {
  98. appendRow(_chat);
  99. } else {
  100. for (const auto chat : _chats) {
  101. appendRow(chat);
  102. }
  103. if (_chats.size() >= kEnableSearchRowsCount) {
  104. delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
  105. }
  106. }
  107. }
  108. void Controller::rowClicked(not_null<PeerListRow*> row) {
  109. if (_chat != nullptr) {
  110. _showHistoryCallback(_chat);
  111. return;
  112. }
  113. const auto peer = row->peer();
  114. if (const auto channel = peer->asChannel()) {
  115. if (channel->wasFullUpdated()) {
  116. choose(channel);
  117. return;
  118. }
  119. _waitForFull = channel;
  120. channel->updateFull();
  121. } else if (const auto chat = peer->asChat()) {
  122. choose(chat);
  123. }
  124. }
  125. void Controller::choose(not_null<ChannelData*> chat) {
  126. if (chat->isForum()) {
  127. ShowForumForDiscussionError(_navigation);
  128. return;
  129. }
  130. auto text = tr::lng_manage_discussion_group_sure(
  131. tr::now,
  132. lt_group,
  133. Ui::Text::Bold(chat->name()),
  134. lt_channel,
  135. Ui::Text::Bold(_channel->name()),
  136. Ui::Text::WithEntities);
  137. if (!_channel->isPublic()) {
  138. text.append(
  139. "\n\n" + tr::lng_manage_linked_channel_private(tr::now));
  140. }
  141. if (!chat->isPublic()) {
  142. text.append(
  143. "\n\n" + tr::lng_manage_discussion_group_private(tr::now));
  144. if (chat->hiddenPreHistory()) {
  145. text.append("\n\n");
  146. text.append(tr::lng_manage_discussion_group_warning(
  147. tr::now,
  148. Ui::Text::RichLangValue));
  149. }
  150. }
  151. const auto sure = [=](Fn<void()> &&close) {
  152. close();
  153. const auto onstack = _callback;
  154. onstack(chat);
  155. };
  156. delegate()->peerListUiShow()->showBox(Ui::MakeConfirmBox({
  157. .text = text,
  158. .confirmed = sure,
  159. .confirmText = tr::lng_manage_discussion_group_link(tr::now),
  160. }));
  161. }
  162. void Controller::choose(not_null<ChatData*> chat) {
  163. auto text = tr::lng_manage_discussion_group_sure(
  164. tr::now,
  165. lt_group,
  166. Ui::Text::Bold(chat->name()),
  167. lt_channel,
  168. Ui::Text::Bold(_channel->name()),
  169. Ui::Text::WithEntities);
  170. if (!_channel->isPublic()) {
  171. text.append("\n\n" + tr::lng_manage_linked_channel_private(tr::now));
  172. }
  173. text.append("\n\n" + tr::lng_manage_discussion_group_private(tr::now));
  174. text.append("\n\n");
  175. text.append(tr::lng_manage_discussion_group_warning(
  176. tr::now,
  177. Ui::Text::RichLangValue));
  178. const auto sure = [=](Fn<void()> &&close) {
  179. close();
  180. const auto done = [=](not_null<ChannelData*> chat) {
  181. const auto onstack = _callback;
  182. onstack(chat);
  183. };
  184. chat->session().api().migrateChat(chat, crl::guard(this, done));
  185. };
  186. delegate()->peerListUiShow()->showBox(Ui::MakeConfirmBox({
  187. .text = text,
  188. .confirmed = sure,
  189. .confirmText = tr::lng_manage_discussion_group_link(tr::now),
  190. }));
  191. }
  192. [[nodiscard]] rpl::producer<TextWithEntities> About(
  193. not_null<ChannelData*> channel,
  194. ChannelData *chat) {
  195. if (!channel->isBroadcast()) {
  196. return tr::lng_manage_linked_channel_about(
  197. lt_channel,
  198. rpl::single(Ui::Text::Bold(chat->name())),
  199. Ui::Text::WithEntities);
  200. } else if (chat != nullptr) {
  201. return tr::lng_manage_discussion_group_about_chosen(
  202. lt_group,
  203. rpl::single(Ui::Text::Bold(chat->name())),
  204. Ui::Text::WithEntities);
  205. }
  206. return tr::lng_manage_discussion_group_about(Ui::Text::WithEntities);
  207. }
  208. [[nodiscard]] object_ptr<Ui::BoxContent> EditLinkedChatBox(
  209. not_null<Window::SessionNavigation*> navigation,
  210. not_null<ChannelData*> channel,
  211. ChannelData *chat,
  212. std::vector<not_null<PeerData*>> &&chats,
  213. bool canEdit,
  214. Fn<void(ChannelData*)> callback) {
  215. Expects((channel->isBroadcast() && canEdit) || (chat != nullptr));
  216. class ListBox final : public PeerListBox {
  217. public:
  218. ListBox(
  219. QWidget *parent,
  220. std::unique_ptr<PeerListController> controller,
  221. Fn<void(not_null<ListBox*>)> init)
  222. : PeerListBox(
  223. parent,
  224. std::move(controller),
  225. [=](not_null<PeerListBox*>) { init(this); }) {
  226. }
  227. void showFinished() override {
  228. _showFinished.fire({});
  229. }
  230. rpl::producer<> showFinishes() const {
  231. return _showFinished.events();
  232. }
  233. private:
  234. rpl::event_stream<> _showFinished;
  235. };
  236. const auto init = [=](not_null<ListBox*> box) {
  237. auto above = object_ptr<Ui::VerticalLayout>(box);
  238. Settings::AddDividerTextWithLottie(above, {
  239. .lottie = u"discussion"_q,
  240. .showFinished = box->showFinishes(),
  241. .about = About(channel, chat),
  242. });
  243. if (!chat) {
  244. Assert(channel->isBroadcast());
  245. Ui::AddSkip(above);
  246. Settings::AddButtonWithIcon(
  247. above,
  248. tr::lng_manage_discussion_group_create(),
  249. st::infoCreateLinkedChatButton,
  250. { &st::menuIconGroupCreate }
  251. )->addClickHandler([=, parent = above.data()] {
  252. const auto guarded = crl::guard(parent, callback);
  253. navigation->uiShow()->showBox(Box<GroupInfoBox>(
  254. navigation,
  255. GroupInfoBox::Type::Megagroup,
  256. channel->name() + " Chat",
  257. guarded));
  258. });
  259. }
  260. box->peerListSetAboveWidget(std::move(above));
  261. auto below = object_ptr<Ui::VerticalLayout>(box);
  262. if (chat && canEdit) {
  263. Settings::AddButtonWithIcon(
  264. below,
  265. (channel->isBroadcast()
  266. ? tr::lng_manage_discussion_group_unlink
  267. : tr::lng_manage_linked_channel_unlink)(),
  268. st::infoUnlinkChatButton,
  269. { &st::menuIconRemove }
  270. )->addClickHandler([=] { callback(nullptr); });
  271. Ui::AddSkip(below);
  272. }
  273. Ui::AddDividerText(
  274. below,
  275. (channel->isBroadcast()
  276. ? tr::lng_manage_discussion_group_posted
  277. : tr::lng_manage_linked_channel_posted)());
  278. box->peerListSetBelowWidget(std::move(below));
  279. box->setTitle(channel->isBroadcast()
  280. ? tr::lng_manage_discussion_group()
  281. : tr::lng_manage_linked_channel());
  282. box->addButton(tr::lng_close(), [=] { box->closeBox(); });
  283. };
  284. auto showHistoryCallback = [=](not_null<PeerData*> peer) {
  285. navigation->showPeerHistory(
  286. peer,
  287. Window::SectionShow::Way::ClearStack,
  288. ShowAtUnreadMsgId);
  289. };
  290. auto controller = std::make_unique<Controller>(
  291. navigation,
  292. channel,
  293. chat,
  294. std::move(chats),
  295. std::move(callback),
  296. std::move(showHistoryCallback));
  297. return Box<ListBox>(std::move(controller), init);
  298. }
  299. } // namespace
  300. object_ptr<Ui::BoxContent> EditLinkedChatBox(
  301. not_null<Window::SessionNavigation*> navigation,
  302. not_null<ChannelData*> channel,
  303. std::vector<not_null<PeerData*>> &&chats,
  304. Fn<void(ChannelData*)> callback) {
  305. return EditLinkedChatBox(
  306. navigation,
  307. channel,
  308. nullptr,
  309. std::move(chats),
  310. true,
  311. callback);
  312. }
  313. object_ptr<Ui::BoxContent> EditLinkedChatBox(
  314. not_null<Window::SessionNavigation*> navigation,
  315. not_null<ChannelData*> channel,
  316. not_null<ChannelData*> chat,
  317. bool canEdit,
  318. Fn<void(ChannelData*)> callback) {
  319. return EditLinkedChatBox(
  320. navigation,
  321. channel,
  322. chat,
  323. {},
  324. canEdit,
  325. callback);
  326. }
  327. void ShowForumForDiscussionError(
  328. not_null<Window::SessionNavigation*> navigation) {
  329. navigation->showToast(
  330. tr::lng_forum_topics_no_discussion(
  331. tr::now,
  332. Ui::Text::RichLangValue));
  333. }