settings_global_ttl.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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 "settings/settings_global_ttl.h"
  8. #include "api/api_self_destruct.h"
  9. #include "apiwrap.h"
  10. #include "boxes/peer_list_controllers.h"
  11. #include "data/data_changes.h"
  12. #include "data/data_chat.h"
  13. #include "data/data_peer.h"
  14. #include "data/data_session.h"
  15. #include "history/history.h"
  16. #include "lang/lang_keys.h"
  17. #include "lottie/lottie_icon.h"
  18. #include "main/main_session.h"
  19. #include "menu/menu_ttl_validator.h"
  20. #include "settings/settings_common_session.h"
  21. #include "ui/boxes/confirm_box.h"
  22. #include "ui/painter.h"
  23. #include "ui/vertical_list.h"
  24. #include "ui/text/format_values.h"
  25. #include "ui/text/text_utilities.h"
  26. #include "ui/widgets/buttons.h"
  27. #include "ui/widgets/checkbox.h"
  28. #include "ui/widgets/labels.h"
  29. #include "ui/wrap/vertical_layout.h"
  30. #include "window/window_session_controller.h"
  31. #include "styles/style_boxes.h"
  32. #include "styles/style_layers.h"
  33. #include "styles/style_settings.h"
  34. #include "styles/style_calls.h"
  35. namespace Settings {
  36. namespace {
  37. class TTLRow : public ChatsListBoxController::Row {
  38. public:
  39. using ChatsListBoxController::Row::Row;
  40. void paintStatusText(
  41. Painter &p,
  42. const style::PeerListItem &st,
  43. int x,
  44. int y,
  45. int availableWidth,
  46. int outerWidth,
  47. bool selected) override;
  48. };
  49. void TTLRow::paintStatusText(
  50. Painter &p,
  51. const style::PeerListItem &st,
  52. int x,
  53. int y,
  54. int availableWidth,
  55. int outerWidth,
  56. bool selected) {
  57. auto icon = history()->peer->messagesTTL()
  58. ? &st::settingsTTLChatsOn
  59. : &st::settingsTTLChatsOff;
  60. icon->paint(
  61. p,
  62. x + st::callArrowPosition.x(),
  63. y + st::callArrowPosition.y(),
  64. outerWidth);
  65. auto shift = st::callArrowPosition.x()
  66. + icon->width()
  67. + st::callArrowSkip;
  68. x += shift;
  69. availableWidth -= shift;
  70. PeerListRow::paintStatusText(
  71. p,
  72. st,
  73. x,
  74. y,
  75. availableWidth,
  76. outerWidth,
  77. selected);
  78. }
  79. class TTLChatsBoxController : public ChatsListBoxController {
  80. public:
  81. TTLChatsBoxController(not_null<Main::Session*> session);
  82. Main::Session &session() const override;
  83. void rowClicked(not_null<PeerListRow*> row) override;
  84. protected:
  85. void prepareViewHook() override;
  86. std::unique_ptr<Row> createRow(not_null<History*> history) override;
  87. private:
  88. const not_null<Main::Session*> _session;
  89. rpl::lifetime _lifetime;
  90. };
  91. TTLChatsBoxController::TTLChatsBoxController(not_null<Main::Session*> session)
  92. : ChatsListBoxController(session)
  93. , _session(session) {
  94. }
  95. Main::Session &TTLChatsBoxController::session() const {
  96. return *_session;
  97. }
  98. void TTLChatsBoxController::prepareViewHook() {
  99. delegate()->peerListSetTitle(tr::lng_settings_ttl_title());
  100. }
  101. void TTLChatsBoxController::rowClicked(not_null<PeerListRow*> row) {
  102. if (!TTLMenu::TTLValidator(nullptr, row->peer()).can()) {
  103. delegate()->peerListUiShow()->showToast(
  104. { tr::lng_settings_ttl_select_chats_sorry(tr::now) });
  105. return;
  106. }
  107. delegate()->peerListSetRowChecked(row, !row->checked());
  108. }
  109. std::unique_ptr<TTLChatsBoxController::Row> TTLChatsBoxController::createRow(
  110. not_null<History*> history) {
  111. const auto peer = history->peer;
  112. if (peer->isSelf() || peer->isRepliesChat() || peer->isVerifyCodes()) {
  113. return nullptr;
  114. } else if (peer->isChat() && peer->asChat()->amIn()) {
  115. } else if (peer->isMegagroup()) {
  116. } else if (!TTLMenu::TTLValidator(nullptr, peer).can()) {
  117. return nullptr;
  118. }
  119. if (session().data().contactsNoChatsList()->contains({ history })) {
  120. return nullptr;
  121. }
  122. auto result = std::make_unique<TTLRow>(history);
  123. const auto applyStatus = [=, raw = result.get()] {
  124. const auto ttl = peer->messagesTTL();
  125. raw->setCustomStatus(
  126. ttl
  127. ? tr::lng_settings_ttl_select_chats_status(
  128. tr::now,
  129. lt_after_duration,
  130. Ui::FormatTTLAfter(ttl))
  131. : tr::lng_settings_ttl_select_chats_status_disabled(tr::now),
  132. ttl);
  133. };
  134. applyStatus();
  135. return result;
  136. }
  137. void SetupTopContent(
  138. not_null<Ui::VerticalLayout*> parent,
  139. rpl::producer<> showFinished) {
  140. const auto divider = Ui::CreateChild<Ui::BoxContentDivider>(parent.get());
  141. const auto verticalLayout = parent->add(
  142. object_ptr<Ui::VerticalLayout>(parent.get()));
  143. auto icon = CreateLottieIcon(
  144. verticalLayout,
  145. {
  146. .name = u"ttl"_q,
  147. .sizeOverride = {
  148. st::settingsCloudPasswordIconSize,
  149. st::settingsCloudPasswordIconSize,
  150. },
  151. },
  152. st::settingsFilterIconPadding);
  153. std::move(
  154. showFinished
  155. ) | rpl::start_with_next([animate = std::move(icon.animate)] {
  156. animate(anim::repeat::loop);
  157. }, verticalLayout->lifetime());
  158. verticalLayout->add(std::move(icon.widget));
  159. verticalLayout->geometryValue(
  160. ) | rpl::start_with_next([=](const QRect &r) {
  161. divider->setGeometry(r);
  162. }, divider->lifetime());
  163. }
  164. } // namespace
  165. class GlobalTTL : public Section<GlobalTTL> {
  166. public:
  167. GlobalTTL(
  168. QWidget *parent,
  169. not_null<Window::SessionController*> controller);
  170. [[nodiscard]] rpl::producer<QString> title() override;
  171. void setupContent();
  172. void showFinished() override final;
  173. private:
  174. void rebuildButtons(TimeId currentTTL) const;
  175. void showSure(TimeId ttl, bool rebuild) const;
  176. void request(TimeId ttl) const;
  177. const not_null<Window::SessionController*> _controller;
  178. const std::shared_ptr<Ui::RadiobuttonGroup> _group;
  179. const std::shared_ptr<Main::SessionShow> _show;
  180. not_null<Ui::VerticalLayout*> _buttons;
  181. rpl::event_stream<> _showFinished;
  182. rpl::lifetime _requestLifetime;
  183. };
  184. GlobalTTL::GlobalTTL(
  185. QWidget *parent,
  186. not_null<Window::SessionController*> controller)
  187. : Section(parent)
  188. , _controller(controller)
  189. , _group(std::make_shared<Ui::RadiobuttonGroup>(0))
  190. , _show(controller->uiShow())
  191. , _buttons(Ui::CreateChild<Ui::VerticalLayout>(this)) {
  192. setupContent();
  193. }
  194. rpl::producer<QString> GlobalTTL::title() {
  195. return tr::lng_settings_ttl_title();
  196. }
  197. void GlobalTTL::request(TimeId ttl) const {
  198. _controller->session().api().selfDestruct().updateDefaultHistoryTTL(ttl);
  199. }
  200. void GlobalTTL::showSure(TimeId ttl, bool rebuild) const {
  201. const auto ttlText = Ui::FormatTTLAfter(ttl);
  202. const auto confirmed = [=] {
  203. if (rebuild) {
  204. rebuildButtons(ttl);
  205. }
  206. _group->setChangedCallback([=](int value) {
  207. _group->setChangedCallback(nullptr);
  208. _show->showToast(tr::lng_settings_ttl_after_toast(
  209. tr::now,
  210. lt_after_duration,
  211. { .text = ttlText },
  212. Ui::Text::WithEntities));
  213. _show->hideLayer(); // Don't use close().
  214. });
  215. request(ttl);
  216. };
  217. if (_group->value()) {
  218. confirmed();
  219. return;
  220. }
  221. _show->showBox(Ui::MakeConfirmBox({
  222. .text = tr::lng_settings_ttl_after_sure(
  223. lt_after_duration,
  224. rpl::single(ttlText)),
  225. .confirmed = confirmed,
  226. .cancelled = [=](Fn<void()> &&close) {
  227. _group->setChangedCallback(nullptr);
  228. close();
  229. },
  230. .confirmText = tr::lng_sure_enable(),
  231. }));
  232. }
  233. void GlobalTTL::rebuildButtons(TimeId currentTTL) const {
  234. auto ttls = std::vector<TimeId>{
  235. 0,
  236. 3600 * 24,
  237. 3600 * 24 * 7,
  238. 3600 * 24 * 31,
  239. };
  240. if (!ranges::contains(ttls, currentTTL)) {
  241. ttls.push_back(currentTTL);
  242. ranges::sort(ttls);
  243. }
  244. if (_buttons->count() > ttls.size()) {
  245. return;
  246. }
  247. _buttons->clear();
  248. for (const auto &ttl : ttls) {
  249. const auto ttlText = Ui::FormatTTLAfter(ttl);
  250. const auto button = _buttons->add(object_ptr<Ui::SettingsButton>(
  251. _buttons,
  252. (!ttl)
  253. ? tr::lng_settings_ttl_after_off()
  254. : tr::lng_settings_ttl_after(
  255. lt_after_duration,
  256. rpl::single(ttlText)),
  257. st::settingsButtonNoIcon));
  258. button->setClickedCallback([=] {
  259. if (_group->current() == ttl) {
  260. return;
  261. }
  262. if (!ttl) {
  263. _group->setChangedCallback(nullptr);
  264. request(ttl);
  265. return;
  266. }
  267. showSure(ttl, false);
  268. });
  269. const auto radio = Ui::CreateChild<Ui::Radiobutton>(
  270. button,
  271. _group,
  272. ttl,
  273. QString());
  274. radio->setAttribute(Qt::WA_TransparentForMouseEvents);
  275. radio->show();
  276. button->sizeValue(
  277. ) | rpl::start_with_next([=] {
  278. radio->moveToRight(0, radio->checkRect().top());
  279. }, radio->lifetime());
  280. }
  281. _buttons->resizeToWidth(width());
  282. }
  283. void GlobalTTL::setupContent() {
  284. setFocusPolicy(Qt::StrongFocus);
  285. setFocus();
  286. const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
  287. SetupTopContent(content, _showFinished.events());
  288. Ui::AddSkip(content);
  289. Ui::AddSubsectionTitle(content, tr::lng_settings_ttl_after_subtitle());
  290. content->add(object_ptr<Ui::VerticalLayout>::fromRaw(_buttons));
  291. {
  292. const auto &apiTTL = _controller->session().api().selfDestruct();
  293. const auto rebuild = [=](TimeId period) {
  294. rebuildButtons(period);
  295. _group->setValue(period);
  296. };
  297. rebuild(apiTTL.periodDefaultHistoryTTLCurrent());
  298. apiTTL.periodDefaultHistoryTTL(
  299. ) | rpl::start_with_next(rebuild, content->lifetime());
  300. }
  301. const auto show = _controller->uiShow();
  302. content->add(object_ptr<Ui::SettingsButton>(
  303. content,
  304. tr::lng_settings_ttl_after_custom(),
  305. st::settingsButtonNoIcon))->setClickedCallback([=] {
  306. struct Args {
  307. std::shared_ptr<Ui::Show> show;
  308. TimeId startTtl;
  309. rpl::producer<QString> about;
  310. Fn<void(TimeId)> callback;
  311. };
  312. show->showBox(Box(TTLMenu::TTLBox, TTLMenu::Args{
  313. .show = show,
  314. .startTtl = _group->current(),
  315. .callback = [=](TimeId ttl, Fn<void()>) { showSure(ttl, true); },
  316. .hideDisable = true,
  317. }));
  318. });
  319. Ui::AddSkip(content);
  320. auto footer = object_ptr<Ui::FlatLabel>(
  321. content,
  322. tr::lng_settings_ttl_after_about(
  323. lt_link,
  324. tr::lng_settings_ttl_after_about_link(
  325. ) | rpl::map([](QString s) { return Ui::Text::Link(s, 1); }),
  326. Ui::Text::WithEntities),
  327. st::boxDividerLabel);
  328. footer->setLink(1, std::make_shared<LambdaClickHandler>([=] {
  329. const auto session = &_controller->session();
  330. auto controller = std::make_unique<TTLChatsBoxController>(session);
  331. auto initBox = [=, controller = controller.get()](
  332. not_null<PeerListBox*> box) {
  333. box->addButton(tr::lng_settings_apply(), crl::guard(this, [=] {
  334. const auto &peers = box->collectSelectedRows();
  335. if (peers.empty()) {
  336. return;
  337. }
  338. const auto &apiTTL = session->api().selfDestruct();
  339. const auto ttl = apiTTL.periodDefaultHistoryTTLCurrent();
  340. for (const auto &peer : peers) {
  341. peer->session().api().request(MTPmessages_SetHistoryTTL(
  342. peer->input,
  343. MTP_int(ttl)
  344. )).done([=](const MTPUpdates &result) {
  345. peer->session().api().applyUpdates(result);
  346. }).send();
  347. }
  348. box->showToast(ttl
  349. ? tr::lng_settings_ttl_select_chats_toast(
  350. tr::now,
  351. lt_count,
  352. peers.size(),
  353. lt_duration,
  354. { .text = Ui::FormatTTL(ttl) },
  355. Ui::Text::WithEntities)
  356. : tr::lng_settings_ttl_select_chats_disabled_toast(
  357. tr::now,
  358. lt_count,
  359. peers.size(),
  360. Ui::Text::WithEntities));
  361. box->closeBox();
  362. }));
  363. box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
  364. };
  365. _controller->show(
  366. Box<PeerListBox>(std::move(controller), std::move(initBox)));
  367. }));
  368. content->add(object_ptr<Ui::DividerLabel>(
  369. content,
  370. std::move(footer),
  371. st::defaultBoxDividerLabelPadding));
  372. Ui::ResizeFitChild(this, content);
  373. }
  374. void GlobalTTL::showFinished() {
  375. _showFinished.fire({});
  376. }
  377. Type GlobalTTLId() {
  378. return GlobalTTL::Id();
  379. }
  380. } // namespace Settings