2024-05-21 14:15:56 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
|
|
|
#include "data/components/factchecks.h"
|
|
|
|
|
2024-05-23 18:58:09 +00:00
|
|
|
#include "api/api_text_entities.h"
|
2024-05-21 14:15:56 +00:00
|
|
|
#include "apiwrap.h"
|
|
|
|
#include "base/random.h"
|
|
|
|
#include "data/data_session.h"
|
2024-05-23 09:07:03 +00:00
|
|
|
#include "data/data_web_page.h"
|
2024-05-21 14:15:56 +00:00
|
|
|
#include "history/view/media/history_view_web_page.h"
|
|
|
|
#include "history/view/history_view_message.h"
|
|
|
|
#include "history/history.h"
|
|
|
|
#include "history/history_item.h"
|
|
|
|
#include "history/history_item_components.h"
|
|
|
|
#include "lang/lang_keys.h"
|
2024-05-23 18:58:09 +00:00
|
|
|
#include "main/main_app_config.h"
|
2024-05-21 14:15:56 +00:00
|
|
|
#include "main/main_session.h"
|
2024-05-24 05:37:38 +00:00
|
|
|
#include "ui/layers/show.h"
|
2024-05-21 14:15:56 +00:00
|
|
|
|
|
|
|
namespace Data {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr auto kRequestDelay = crl::time(1000);
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
Factchecks::Factchecks(not_null<Main::Session*> session)
|
|
|
|
: _session(session)
|
|
|
|
, _requestTimer([=] { request(); }) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void Factchecks::requestFor(not_null<HistoryItem*> item) {
|
|
|
|
subscribeIfNotYet();
|
|
|
|
|
|
|
|
if (const auto factcheck = item->Get<HistoryMessageFactcheck>()) {
|
|
|
|
factcheck->requested = true;
|
|
|
|
}
|
|
|
|
if (!_requestTimer.isActive()) {
|
|
|
|
_requestTimer.callOnce(kRequestDelay);
|
|
|
|
}
|
|
|
|
const auto changed = !_pending.empty()
|
|
|
|
&& (_pending.front()->history() != item->history());
|
|
|
|
const auto added = _pending.emplace(item).second;
|
|
|
|
if (changed) {
|
|
|
|
request();
|
|
|
|
} else if (added && _pending.size() == 1) {
|
|
|
|
_requestTimer.callOnce(kRequestDelay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Factchecks::subscribeIfNotYet() {
|
|
|
|
if (_subscribed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_subscribed = true;
|
|
|
|
|
|
|
|
_session->data().itemRemoved(
|
|
|
|
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
|
|
|
_pending.remove(item);
|
|
|
|
const auto i = ranges::find(_requested, item.get());
|
|
|
|
if (i != end(_requested)) {
|
|
|
|
*i = nullptr;
|
|
|
|
}
|
|
|
|
}, _lifetime);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Factchecks::request() {
|
|
|
|
_requestTimer.cancel();
|
|
|
|
|
|
|
|
if (!_requested.empty() || _pending.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_session->api().request(base::take(_requestId)).cancel();
|
|
|
|
|
|
|
|
auto ids = QVector<MTPint>();
|
|
|
|
ids.reserve(_pending.size());
|
|
|
|
const auto history = _pending.front()->history();
|
|
|
|
for (auto i = begin(_pending); i != end(_pending);) {
|
|
|
|
const auto &item = *i;
|
|
|
|
if (item->history() == history) {
|
|
|
|
_requested.push_back(item);
|
|
|
|
ids.push_back(MTP_int(item->id.bare));
|
|
|
|
i = _pending.erase(i);
|
|
|
|
} else {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_requestId = _session->api().request(MTPmessages_GetFactCheck(
|
|
|
|
history->peer->input,
|
|
|
|
MTP_vector<MTPint>(std::move(ids))
|
|
|
|
)).done([=](const MTPVector<MTPFactCheck> &result) {
|
|
|
|
_requestId = 0;
|
|
|
|
const auto &list = result.v;
|
|
|
|
auto index = 0;
|
|
|
|
for (const auto &item : base::take(_requested)) {
|
|
|
|
if (!item) {
|
|
|
|
} else if (index >= list.size()) {
|
|
|
|
item->setFactcheck({});
|
|
|
|
} else {
|
|
|
|
item->setFactcheck(FromMTP(item, &list[index]));
|
|
|
|
}
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
if (!_pending.empty()) {
|
|
|
|
request();
|
|
|
|
}
|
|
|
|
}).fail([=] {
|
|
|
|
_requestId = 0;
|
|
|
|
for (const auto &item : base::take(_requested)) {
|
|
|
|
if (item) {
|
|
|
|
item->setFactcheck({});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!_pending.empty()) {
|
|
|
|
request();
|
|
|
|
}
|
|
|
|
}).send();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<HistoryView::WebPage> Factchecks::makeMedia(
|
|
|
|
not_null<HistoryView::Message*> view,
|
|
|
|
not_null<HistoryMessageFactcheck*> factcheck) {
|
|
|
|
if (!factcheck->page) {
|
|
|
|
factcheck->page = view->history()->owner().webpage(
|
|
|
|
base::RandomValue<WebPageId>(),
|
|
|
|
tr::lng_factcheck_title(tr::now),
|
|
|
|
factcheck->data.text);
|
2024-05-23 09:07:03 +00:00
|
|
|
factcheck->page->type = WebPageType::Factcheck;
|
2024-05-21 14:15:56 +00:00
|
|
|
}
|
|
|
|
return std::make_unique<HistoryView::WebPage>(
|
|
|
|
view,
|
|
|
|
factcheck->page,
|
|
|
|
MediaWebPageFlags());
|
|
|
|
}
|
|
|
|
|
2024-05-23 18:58:09 +00:00
|
|
|
bool Factchecks::canEdit(not_null<HistoryItem*> item) const {
|
|
|
|
if (!canEdit()
|
|
|
|
|| !item->isRegular()
|
|
|
|
|| !item->history()->peer->isBroadcast()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const auto media = item->media();
|
|
|
|
if (!media || media->webpage() || media->photo()) {
|
|
|
|
return true;
|
|
|
|
} else if (const auto document = media->document()) {
|
|
|
|
return !document->isVideoMessage() && !document->sticker();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Factchecks::canEdit() const {
|
|
|
|
return _session->appConfig().get<bool>(u"can_edit_factcheck"_q, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Factchecks::lengthLimit() const {
|
|
|
|
return _session->appConfig().get<int>(u"factcheck_length_limit"_q, 1024);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Factchecks::save(
|
|
|
|
FullMsgId itemId,
|
|
|
|
TextWithEntities text,
|
|
|
|
Fn<void(QString)> done) {
|
|
|
|
const auto item = _session->data().message(itemId);
|
|
|
|
if (!item) {
|
|
|
|
return;
|
|
|
|
} else if (text.empty()) {
|
|
|
|
_session->api().request(MTPmessages_DeleteFactCheck(
|
|
|
|
item->history()->peer->input,
|
|
|
|
MTP_int(item->id.bare)
|
|
|
|
)).done([=](const MTPUpdates &result) {
|
|
|
|
_session->api().applyUpdates(result);
|
|
|
|
done(QString());
|
|
|
|
}).fail([=](const MTP::Error &error) {
|
|
|
|
done(error.type());
|
|
|
|
}).send();
|
|
|
|
} else {
|
|
|
|
_session->api().request(MTPmessages_EditFactCheck(
|
|
|
|
item->history()->peer->input,
|
|
|
|
MTP_int(item->id.bare),
|
|
|
|
MTP_textWithEntities(
|
|
|
|
MTP_string(text.text),
|
|
|
|
Api::EntitiesToMTP(
|
|
|
|
_session,
|
|
|
|
text.entities,
|
|
|
|
Api::ConvertOption::SkipLocal))
|
|
|
|
)).done([=](const MTPUpdates &result) {
|
|
|
|
_session->api().applyUpdates(result);
|
|
|
|
done(QString());
|
|
|
|
}).fail([=](const MTP::Error &error) {
|
|
|
|
done(error.type());
|
|
|
|
}).send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-24 05:37:38 +00:00
|
|
|
void Factchecks::save(
|
|
|
|
FullMsgId itemId,
|
2024-05-24 07:23:27 +00:00
|
|
|
const TextWithEntities &was,
|
2024-05-24 05:37:38 +00:00
|
|
|
TextWithEntities text,
|
|
|
|
std::shared_ptr<Ui::Show> show) {
|
2024-05-24 07:23:27 +00:00
|
|
|
const auto wasEmpty = was.empty();
|
|
|
|
const auto textEmpty = text.empty();
|
|
|
|
save(itemId, std::move(text), [=](QString error) {
|
2024-05-24 05:37:38 +00:00
|
|
|
show->showToast(!error.isEmpty()
|
|
|
|
? error
|
2024-05-24 07:23:27 +00:00
|
|
|
: textEmpty
|
2024-05-24 14:24:59 +00:00
|
|
|
? tr::lng_factcheck_remove_done(tr::now)
|
|
|
|
: wasEmpty
|
2024-05-24 05:37:38 +00:00
|
|
|
? tr::lng_factcheck_add_done(tr::now)
|
|
|
|
: tr::lng_factcheck_edit_done(tr::now));
|
2024-05-24 07:23:27 +00:00
|
|
|
});
|
2024-05-24 05:37:38 +00:00
|
|
|
}
|
|
|
|
|
2024-05-21 14:15:56 +00:00
|
|
|
} // namespace Data
|