factchecks.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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 "data/components/factchecks.h"
  8. #include "api/api_text_entities.h"
  9. #include "apiwrap.h"
  10. #include "base/random.h"
  11. #include "data/data_session.h"
  12. #include "data/data_web_page.h"
  13. #include "history/view/media/history_view_web_page.h"
  14. #include "history/view/history_view_message.h"
  15. #include "history/history.h"
  16. #include "history/history_item.h"
  17. #include "history/history_item_components.h"
  18. #include "lang/lang_keys.h"
  19. #include "main/main_app_config.h"
  20. #include "main/main_session.h"
  21. #include "ui/layers/show.h"
  22. namespace Data {
  23. namespace {
  24. constexpr auto kRequestDelay = crl::time(1000);
  25. } // namespace
  26. Factchecks::Factchecks(not_null<Main::Session*> session)
  27. : _session(session)
  28. , _requestTimer([=] { request(); }) {
  29. }
  30. void Factchecks::requestFor(not_null<HistoryItem*> item) {
  31. subscribeIfNotYet();
  32. if (const auto factcheck = item->Get<HistoryMessageFactcheck>()) {
  33. factcheck->requested = true;
  34. }
  35. if (!_requestTimer.isActive()) {
  36. _requestTimer.callOnce(kRequestDelay);
  37. }
  38. const auto changed = !_pending.empty()
  39. && (_pending.front()->history() != item->history());
  40. const auto added = _pending.emplace(item).second;
  41. if (changed) {
  42. request();
  43. } else if (added && _pending.size() == 1) {
  44. _requestTimer.callOnce(kRequestDelay);
  45. }
  46. }
  47. void Factchecks::subscribeIfNotYet() {
  48. if (_subscribed) {
  49. return;
  50. }
  51. _subscribed = true;
  52. _session->data().itemRemoved(
  53. ) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
  54. _pending.remove(item);
  55. const auto i = ranges::find(_requested, item.get());
  56. if (i != end(_requested)) {
  57. *i = nullptr;
  58. }
  59. }, _lifetime);
  60. }
  61. void Factchecks::request() {
  62. _requestTimer.cancel();
  63. if (!_requested.empty() || _pending.empty()) {
  64. return;
  65. }
  66. _session->api().request(base::take(_requestId)).cancel();
  67. auto ids = QVector<MTPint>();
  68. ids.reserve(_pending.size());
  69. const auto history = _pending.front()->history();
  70. for (auto i = begin(_pending); i != end(_pending);) {
  71. const auto &item = *i;
  72. if (item->history() == history) {
  73. _requested.push_back(item);
  74. ids.push_back(MTP_int(item->id.bare));
  75. i = _pending.erase(i);
  76. } else {
  77. ++i;
  78. }
  79. }
  80. _requestId = _session->api().request(MTPmessages_GetFactCheck(
  81. history->peer->input,
  82. MTP_vector<MTPint>(std::move(ids))
  83. )).done([=](const MTPVector<MTPFactCheck> &result) {
  84. _requestId = 0;
  85. const auto &list = result.v;
  86. auto index = 0;
  87. for (const auto &item : base::take(_requested)) {
  88. if (!item) {
  89. } else if (index >= list.size()) {
  90. item->setFactcheck({});
  91. } else {
  92. item->setFactcheck(FromMTP(item, &list[index]));
  93. }
  94. ++index;
  95. }
  96. if (!_pending.empty()) {
  97. request();
  98. }
  99. }).fail([=] {
  100. _requestId = 0;
  101. for (const auto &item : base::take(_requested)) {
  102. if (item) {
  103. item->setFactcheck({});
  104. }
  105. }
  106. if (!_pending.empty()) {
  107. request();
  108. }
  109. }).send();
  110. }
  111. std::unique_ptr<HistoryView::WebPage> Factchecks::makeMedia(
  112. not_null<HistoryView::Message*> view,
  113. not_null<HistoryMessageFactcheck*> factcheck) {
  114. if (!factcheck->page) {
  115. factcheck->page = view->history()->owner().webpage(
  116. base::RandomValue<WebPageId>(),
  117. tr::lng_factcheck_title(tr::now),
  118. factcheck->data.text);
  119. factcheck->page->type = WebPageType::Factcheck;
  120. }
  121. return std::make_unique<HistoryView::WebPage>(
  122. view,
  123. factcheck->page,
  124. MediaWebPageFlags());
  125. }
  126. bool Factchecks::canEdit(not_null<HistoryItem*> item) const {
  127. if (!canEdit()
  128. || !item->isRegular()
  129. || !item->history()->peer->isBroadcast()) {
  130. return false;
  131. }
  132. const auto media = item->media();
  133. if (!media || media->webpage() || media->photo()) {
  134. return true;
  135. } else if (const auto document = media->document()) {
  136. return !document->isVideoMessage() && !document->sticker();
  137. }
  138. return false;
  139. }
  140. bool Factchecks::canEdit() const {
  141. return _session->appConfig().get<bool>(u"can_edit_factcheck"_q, false);
  142. }
  143. int Factchecks::lengthLimit() const {
  144. return _session->appConfig().get<int>(u"factcheck_length_limit"_q, 1024);
  145. }
  146. void Factchecks::save(
  147. FullMsgId itemId,
  148. TextWithEntities text,
  149. Fn<void(QString)> done) {
  150. const auto item = _session->data().message(itemId);
  151. if (!item) {
  152. return;
  153. } else if (text.empty()) {
  154. _session->api().request(MTPmessages_DeleteFactCheck(
  155. item->history()->peer->input,
  156. MTP_int(item->id.bare)
  157. )).done([=](const MTPUpdates &result) {
  158. _session->api().applyUpdates(result);
  159. done(QString());
  160. }).fail([=](const MTP::Error &error) {
  161. done(error.type());
  162. }).send();
  163. } else {
  164. _session->api().request(MTPmessages_EditFactCheck(
  165. item->history()->peer->input,
  166. MTP_int(item->id.bare),
  167. MTP_textWithEntities(
  168. MTP_string(text.text),
  169. Api::EntitiesToMTP(
  170. _session,
  171. text.entities,
  172. Api::ConvertOption::SkipLocal))
  173. )).done([=](const MTPUpdates &result) {
  174. _session->api().applyUpdates(result);
  175. done(QString());
  176. }).fail([=](const MTP::Error &error) {
  177. done(error.type());
  178. }).send();
  179. }
  180. }
  181. void Factchecks::save(
  182. FullMsgId itemId,
  183. const TextWithEntities &was,
  184. TextWithEntities text,
  185. std::shared_ptr<Ui::Show> show) {
  186. const auto wasEmpty = was.empty();
  187. const auto textEmpty = text.empty();
  188. save(itemId, std::move(text), [=](QString error) {
  189. show->showToast(!error.isEmpty()
  190. ? error
  191. : textEmpty
  192. ? tr::lng_factcheck_remove_done(tr::now)
  193. : wasEmpty
  194. ? tr::lng_factcheck_add_done(tr::now)
  195. : tr::lng_factcheck_edit_done(tr::now));
  196. });
  197. }
  198. } // namespace Data