tdesktop/Telegram/SourceFiles/api/api_single_message_search.cpp

236 lines
6.4 KiB
C++

/*
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 "api/api_single_message_search.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_channel.h"
#include "data/data_search_controller.h"
#include "core/local_url_handlers.h"
#include "history/history_item.h"
#include "base/qthelp_url.h"
#include "apiwrap.h"
namespace Api {
namespace {
using Key = details::SingleMessageSearchKey;
Key ExtractKey(const QString &query) {
const auto trimmed = query.trimmed();
const auto local = Core::TryConvertUrlToLocal(trimmed);
const auto check = local.isEmpty() ? trimmed : local;
const auto parse = [&] {
const auto delimeter = check.indexOf('?');
return (delimeter > 0)
? qthelp::url_parse_params(
check.mid(delimeter + 1),
qthelp::UrlParamNameTransform::ToLower)
: QMap<QString, QString>();
};
if (check.startsWith(qstr("tg://privatepost"), Qt::CaseInsensitive)) {
const auto params = parse();
const auto channel = params.value("channel");
const auto post = params.value("post").toInt();
return (channel.toULongLong() && post) ? Key{ channel, post } : Key();
} else if (check.startsWith(qstr("tg://resolve"), Qt::CaseInsensitive)) {
const auto params = parse();
const auto domain = params.value("domain");
const auto post = params.value("post").toInt();
return (!domain.isEmpty() && post) ? Key{ domain, post } : Key();
}
return Key();
}
} // namespace
SingleMessageSearch::SingleMessageSearch(not_null<Main::Session*> session)
: _session(session) {
}
SingleMessageSearch::~SingleMessageSearch() {
clear();
}
void SingleMessageSearch::clear() {
_cache.clear();
_requestKey = Key();
_session->api().request(base::take(_requestId)).cancel();
}
std::optional<HistoryItem*> SingleMessageSearch::lookup(
const QString &query,
Fn<void()> ready) {
const auto key = ExtractKey(query);
if (!key) {
return nullptr;
}
const auto i = _cache.find(key);
if (i != end(_cache)) {
return _session->data().message(i->second);
}
if (!(_requestKey == key)) {
_session->api().request(base::take(_requestId)).cancel();
_requestKey = key;
}
return performLookup(ready);
}
std::optional<HistoryItem*> SingleMessageSearch::performLookupByChannel(
not_null<ChannelData*> channel,
Fn<void()> ready) {
Expects(!_requestKey.empty());
const auto postId = _requestKey.postId;
if (const auto item = _session->data().message(channel, postId)) {
_cache.emplace(_requestKey, item->fullId());
return item;
} else if (!ready) {
return nullptr;
}
const auto fail = [=] {
_cache.emplace(_requestKey, FullMsgId());
ready();
};
_requestId = _session->api().request(MTPchannels_GetMessages(
channel->inputChannel,
MTP_vector<MTPInputMessage>(1, MTP_inputMessageID(MTP_int(postId)))
)).done([=](const MTPmessages_Messages &result) {
const auto received = Api::ParseSearchResult(
channel,
Storage::SharedMediaType::kCount,
postId,
Data::LoadDirection::Around,
result);
if (!received.messageIds.empty()
&& received.messageIds.front() == postId) {
_cache.emplace(
_requestKey,
FullMsgId(peerToChannel(channel->id), postId));
ready();
} else {
fail();
}
}).fail([=](const MTP::Error &error) {
fail();
}).send();
return std::nullopt;
}
std::optional<HistoryItem*> SingleMessageSearch::performLookupById(
ChannelId channelId,
Fn<void()> ready) {
Expects(!_requestKey.empty());
if (const auto channel = _session->data().channelLoaded(channelId)) {
return performLookupByChannel(channel, ready);
} else if (!ready) {
return nullptr;
}
const auto fail = [=] {
_cache.emplace(_requestKey, FullMsgId());
ready();
};
_requestId = _session->api().request(MTPchannels_GetChannels(
MTP_vector<MTPInputChannel>(
1,
MTP_inputChannel(MTP_long(channelId.bare), MTP_long(0)))
)).done([=](const MTPmessages_Chats &result) {
result.match([&](const auto &data) {
const auto peer = _session->data().processChats(data.vchats());
if (peer && peer->id == peerFromChannel(channelId)) {
if (performLookupByChannel(peer->asChannel(), ready)) {
ready();
}
} else {
fail();
}
});
}).fail([=](const MTP::Error &error) {
fail();
}).send();
return std::nullopt;
}
std::optional<HistoryItem*> SingleMessageSearch::performLookupByUsername(
const QString &username,
Fn<void()> ready) {
Expects(!_requestKey.empty());
if (const auto peer = _session->data().peerByUsername(username)) {
if (const auto channel = peer->asChannel()) {
return performLookupByChannel(channel, ready);
}
_cache.emplace(_requestKey, FullMsgId());
return nullptr;
} else if (!ready) {
return nullptr;
}
const auto fail = [=] {
_cache.emplace(_requestKey, FullMsgId());
ready();
};
_requestId = _session->api().request(MTPcontacts_ResolveUsername(
MTP_string(username)
)).done([=](const MTPcontacts_ResolvedPeer &result) {
result.match([&](const MTPDcontacts_resolvedPeer &data) {
_session->data().processUsers(data.vusers());
_session->data().processChats(data.vchats());
const auto peerId = peerFromMTP(data.vpeer());
const auto peer = peerId
? _session->data().peerLoaded(peerId)
: nullptr;
if (const auto channel = peer ? peer->asChannel() : nullptr) {
if (performLookupByChannel(channel, ready)) {
ready();
}
} else {
fail();
}
});
}).fail([=](const MTP::Error &error) {
fail();
}).send();
return std::nullopt;
}
std::optional<HistoryItem*> SingleMessageSearch::performLookup(
Fn<void()> ready) {
Expects(!_requestKey.empty());
if (!_requestKey.domainOrId[0].isDigit()) {
return performLookupByUsername(_requestKey.domainOrId, ready);
}
const auto channelId = ChannelId(_requestKey.domainOrId.toULongLong());
return performLookupById(channelId, ready);
}
QString ConvertPeerSearchQuery(const QString &query) {
const auto trimmed = query.trimmed();
const auto local = Core::TryConvertUrlToLocal(trimmed);
const auto check = local.isEmpty() ? trimmed : local;
if (!check.startsWith(qstr("tg://resolve"), Qt::CaseInsensitive)) {
return query;
}
const auto delimeter = check.indexOf('?');
const auto params = (delimeter > 0)
? qthelp::url_parse_params(
check.mid(delimeter + 1),
qthelp::UrlParamNameTransform::ToLower)
: QMap<QString, QString>();
return params.value("domain", query);
}
} // namespace Api