init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
This commit is contained in:
237
Telegram/SourceFiles/api/api_single_message_search.cpp
Normal file
237
Telegram/SourceFiles/api/api_single_message_search.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
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(u"tg://privatepost"_q, 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(u"tg://resolve"_q, 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->id, 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(channel->id, postId));
|
||||
ready();
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}).fail([=] {
|
||||
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([=] {
|
||||
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_flags(0),
|
||||
MTP_string(username),
|
||||
MTP_string()
|
||||
)).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([=] {
|
||||
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(u"tg://resolve"_q, 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
|
||||
Reference in New Issue
Block a user