settings_greeting.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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/business/settings_greeting.h"
  8. #include "base/event_filter.h"
  9. #include "core/application.h"
  10. #include "data/business/data_business_info.h"
  11. #include "data/business/data_shortcut_messages.h"
  12. #include "data/data_session.h"
  13. #include "lang/lang_keys.h"
  14. #include "main/main_session.h"
  15. #include "settings/business/settings_shortcut_messages.h"
  16. #include "settings/business/settings_recipients_helper.h"
  17. #include "ui/boxes/time_picker_box.h"
  18. #include "ui/layers/generic_box.h"
  19. #include "ui/text/text_utilities.h"
  20. #include "ui/toast/toast.h"
  21. #include "ui/widgets/box_content_divider.h"
  22. #include "ui/widgets/buttons.h"
  23. #include "ui/widgets/vertical_drum_picker.h"
  24. #include "ui/wrap/slide_wrap.h"
  25. #include "ui/wrap/vertical_layout.h"
  26. #include "ui/vertical_list.h"
  27. #include "window/window_session_controller.h"
  28. #include "styles/style_layers.h"
  29. #include "styles/style_settings.h"
  30. namespace Settings {
  31. namespace {
  32. constexpr auto kDefaultNoActivityDays = 7;
  33. class Greeting : public BusinessSection<Greeting> {
  34. public:
  35. Greeting(
  36. QWidget *parent,
  37. not_null<Window::SessionController*> controller);
  38. ~Greeting();
  39. [[nodiscard]] bool closeByOutsideClick() const override;
  40. [[nodiscard]] rpl::producer<QString> title() override;
  41. const Ui::RoundRect *bottomSkipRounding() const override {
  42. return &_bottomSkipRounding;
  43. }
  44. private:
  45. void setupContent(not_null<Window::SessionController*> controller);
  46. void save();
  47. Ui::RoundRect _bottomSkipRounding;
  48. rpl::variable<Data::BusinessRecipients> _recipients;
  49. rpl::variable<bool> _canHave;
  50. rpl::event_stream<> _deactivateOnAttempt;
  51. rpl::variable<int> _noActivityDays;
  52. rpl::variable<bool> _enabled;
  53. };
  54. Greeting::Greeting(
  55. QWidget *parent,
  56. not_null<Window::SessionController*> controller)
  57. : BusinessSection(parent, controller)
  58. , _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
  59. setupContent(controller);
  60. }
  61. void EditPeriodBox(
  62. not_null<Ui::GenericBox*> box,
  63. int days,
  64. Fn<void(int)> save) {
  65. auto values = std::vector{ 7, 14, 21, 28 };
  66. if (!ranges::contains(values, days)) {
  67. values.push_back(days);
  68. ranges::sort(values);
  69. }
  70. const auto phrases = ranges::views::all(
  71. values
  72. ) | ranges::views::transform([](int days) {
  73. return tr::lng_days(tr::now, lt_count, days);
  74. }) | ranges::to_vector;
  75. const auto take = TimePickerBox(box, values, phrases, days);
  76. box->addButton(tr::lng_settings_save(), [=] {
  77. const auto weak = Ui::MakeWeak(box);
  78. save(take());
  79. if (const auto strong = weak.data()) {
  80. strong->closeBox();
  81. }
  82. });
  83. box->addButton(tr::lng_cancel(), [=] {
  84. box->closeBox();
  85. });
  86. }
  87. Greeting::~Greeting() {
  88. if (!Core::Quitting()) {
  89. save();
  90. }
  91. }
  92. bool Greeting::closeByOutsideClick() const {
  93. return false;
  94. }
  95. rpl::producer<QString> Greeting::title() {
  96. return tr::lng_greeting_title();
  97. }
  98. void Greeting::setupContent(
  99. not_null<Window::SessionController*> controller) {
  100. using namespace rpl::mappers;
  101. const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
  102. const auto info = &controller->session().data().businessInfo();
  103. const auto current = info->greetingSettings();
  104. const auto disabled = !current.noActivityDays;
  105. _recipients = disabled
  106. ? Data::BusinessRecipients{ .allButExcluded = true }
  107. : Data::BusinessRecipients::MakeValid(current.recipients);
  108. _noActivityDays = disabled
  109. ? kDefaultNoActivityDays
  110. : current.noActivityDays;
  111. AddDividerTextWithLottie(content, {
  112. .lottie = u"greeting"_q,
  113. .lottieSize = st::settingsCloudPasswordIconSize,
  114. .lottieMargins = st::peerAppearanceIconPadding,
  115. .showFinished = showFinishes(),
  116. .about = tr::lng_greeting_about(Ui::Text::WithEntities),
  117. .aboutMargins = st::peerAppearanceCoverLabelMargin,
  118. });
  119. const auto session = &controller->session();
  120. _canHave = rpl::combine(
  121. ShortcutsCountValue(session),
  122. ShortcutsLimitValue(session),
  123. ShortcutExistsValue(session, u"hello"_q),
  124. (_1 < _2) || _3);
  125. Ui::AddSkip(content);
  126. const auto enabled = content->add(object_ptr<Ui::SettingsButton>(
  127. content,
  128. tr::lng_greeting_enable(),
  129. st::settingsButtonNoIcon
  130. ))->toggleOn(rpl::single(
  131. !disabled
  132. ) | rpl::then(rpl::merge(
  133. _canHave.value() | rpl::filter(!_1),
  134. _deactivateOnAttempt.events() | rpl::map_to(false)
  135. )));
  136. _enabled = enabled->toggledValue();
  137. _enabled.value() | rpl::filter(_1) | rpl::start_with_next([=] {
  138. if (!_canHave.current()) {
  139. controller->showToast({
  140. .text = { tr::lng_greeting_limit_reached(tr::now) },
  141. .adaptive = true,
  142. });
  143. _deactivateOnAttempt.fire({});
  144. }
  145. }, lifetime());
  146. Ui::AddSkip(content);
  147. content->add(
  148. object_ptr<Ui::SlideWrap<Ui::BoxContentDivider>>(
  149. content,
  150. object_ptr<Ui::BoxContentDivider>(
  151. content,
  152. st::boxDividerHeight,
  153. st::boxDividerBg,
  154. RectPart::Top))
  155. )->setDuration(0)->toggleOn(enabled->toggledValue() | rpl::map(!_1));
  156. content->add(
  157. object_ptr<Ui::SlideWrap<Ui::BoxContentDivider>>(
  158. content,
  159. object_ptr<Ui::BoxContentDivider>(
  160. content))
  161. )->setDuration(0)->toggleOn(enabled->toggledValue());
  162. const auto wrap = content->add(
  163. object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
  164. content,
  165. object_ptr<Ui::VerticalLayout>(content)));
  166. const auto inner = wrap->entity();
  167. const auto createWrap = inner->add(
  168. object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
  169. inner,
  170. object_ptr<Ui::VerticalLayout>(inner)));
  171. const auto createInner = createWrap->entity();
  172. Ui::AddSkip(createInner);
  173. const auto create = AddButtonWithLabel(
  174. createInner,
  175. rpl::conditional(
  176. ShortcutExistsValue(session, u"hello"_q),
  177. tr::lng_business_edit_messages(),
  178. tr::lng_greeting_create()),
  179. ShortcutMessagesCountValue(
  180. session,
  181. u"hello"_q
  182. ) | rpl::map([=](int count) {
  183. return count
  184. ? tr::lng_forum_messages(tr::now, lt_count, count)
  185. : QString();
  186. }),
  187. st::settingsButtonLightNoIcon);
  188. create->setClickedCallback([=] {
  189. const auto owner = &controller->session().data();
  190. const auto id = owner->shortcutMessages().emplaceShortcut("hello");
  191. showOther(ShortcutMessagesId(id));
  192. });
  193. Ui::AddSkip(createInner);
  194. Ui::AddDivider(createInner);
  195. createWrap->toggleOn(rpl::single(true));
  196. Ui::AddSkip(inner);
  197. AddBusinessRecipientsSelector(inner, {
  198. .controller = controller,
  199. .title = tr::lng_greeting_recipients(),
  200. .data = &_recipients,
  201. .type = Data::BusinessRecipientsType::Messages,
  202. });
  203. Ui::AddSkip(inner);
  204. Ui::AddDivider(inner);
  205. Ui::AddSkip(inner);
  206. AddButtonWithLabel(
  207. inner,
  208. tr::lng_greeting_period_title(),
  209. _noActivityDays.value(
  210. ) | rpl::map(
  211. [](int days) { return tr::lng_days(tr::now, lt_count, days); }
  212. ),
  213. st::settingsButtonNoIcon
  214. )->setClickedCallback([=] {
  215. controller->show(Box(
  216. EditPeriodBox,
  217. _noActivityDays.current(),
  218. [=](int days) { _noActivityDays = days; }));
  219. });
  220. Ui::AddSkip(inner);
  221. Ui::AddDividerText(
  222. inner,
  223. tr::lng_greeting_period_about(),
  224. st::settingsChatbotsBottomTextMargin,
  225. RectPart::Top);
  226. wrap->toggleOn(enabled->toggledValue());
  227. wrap->finishAnimating();
  228. Ui::ResizeFitChild(this, content);
  229. }
  230. void Greeting::save() {
  231. const auto show = controller()->uiShow();
  232. const auto session = &controller()->session();
  233. const auto fail = [=](QString error) {
  234. if (error == u"BUSINESS_RECIPIENTS_EMPTY"_q) {
  235. show->showToast(tr::lng_greeting_recipients_empty(tr::now));
  236. } else if (error != u"SHORTCUT_INVALID"_q) {
  237. show->showToast(error);
  238. }
  239. };
  240. session->data().businessInfo().saveGreetingSettings(
  241. _enabled.current() ? Data::GreetingSettings{
  242. .recipients = _recipients.current(),
  243. .noActivityDays = _noActivityDays.current(),
  244. .shortcutId = LookupShortcutId(session, u"hello"_q),
  245. } : Data::GreetingSettings(),
  246. fail);
  247. }
  248. } // namespace
  249. Type GreetingId() {
  250. return Greeting::Id();
  251. }
  252. } // namespace Settings