mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Implement venues search.
This commit is contained in:
parent
917d1841c1
commit
b4dfc25df5
3 changed files with 127 additions and 29 deletions
|
@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/separate_panel.h"
|
#include "ui/widgets/separate_panel.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
|
@ -48,6 +49,7 @@ namespace Ui {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kResolveAddressDelay = 3 * crl::time(1000);
|
constexpr auto kResolveAddressDelay = 3 * crl::time(1000);
|
||||||
|
constexpr auto kSearchDebounceDelay = crl::time(900);
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
const auto kProtocolOverride = "mapboxapihelper";
|
const auto kProtocolOverride = "mapboxapihelper";
|
||||||
|
@ -539,6 +541,12 @@ LocationPicker::LocationPicker(Descriptor &&descriptor)
|
||||||
, _geocoderResolveTimer([=] { resolveAddressByTimer(); })
|
, _geocoderResolveTimer([=] { resolveAddressByTimer(); })
|
||||||
, _venueState(PickerVenueLoading())
|
, _venueState(PickerVenueLoading())
|
||||||
, _session(descriptor.session)
|
, _session(descriptor.session)
|
||||||
|
, _venuesSearchDebounceTimer([=] {
|
||||||
|
Expects(_venuesSearchLocation.has_value());
|
||||||
|
Expects(_venuesSearchQuery.has_value());
|
||||||
|
|
||||||
|
venuesRequest(*_venuesSearchLocation, *_venuesSearchQuery);
|
||||||
|
})
|
||||||
, _api(&_session->mtp()) {
|
, _api(&_session->mtp()) {
|
||||||
std::move(
|
std::move(
|
||||||
descriptor.closeRequests
|
descriptor.closeRequests
|
||||||
|
@ -565,6 +573,7 @@ void LocationPicker::setup(const Descriptor &descriptor) {
|
||||||
if (LastExactLocation) {
|
if (LastExactLocation) {
|
||||||
venuesRequest(LastExactLocation);
|
venuesRequest(LastExactLocation);
|
||||||
resolveAddress(LastExactLocation);
|
resolveAddress(LastExactLocation);
|
||||||
|
venuesSearchEnableAt(LastExactLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,18 +597,27 @@ void LocationPicker::setupWindow(const Descriptor &descriptor) {
|
||||||
_scroll = CreateChild<ScrollArea>(_body.get());
|
_scroll = CreateChild<ScrollArea>(_body.get());
|
||||||
const auto controls = _scroll->setOwnedWidget(
|
const auto controls = _scroll->setOwnedWidget(
|
||||||
object_ptr<VerticalLayout>(_scroll));
|
object_ptr<VerticalLayout>(_scroll));
|
||||||
const auto toppad = controls->add(object_ptr<RpWidget>(controls));
|
|
||||||
|
|
||||||
const auto button = controls->add(
|
_mapControlsWrap = controls->add(
|
||||||
MakeSendLocationButton(controls, _geocoderAddress.value()),
|
object_ptr<SlideWrap<VerticalLayout>>(
|
||||||
|
controls,
|
||||||
|
object_ptr<VerticalLayout>(controls))
|
||||||
|
)->setDuration(0);
|
||||||
|
_mapControlsWrap->show(anim::type::instant);
|
||||||
|
const auto mapControls = _mapControlsWrap->entity();
|
||||||
|
|
||||||
|
const auto toppad = mapControls->add(object_ptr<RpWidget>(controls));
|
||||||
|
|
||||||
|
const auto button = mapControls->add(
|
||||||
|
MakeSendLocationButton(mapControls, _geocoderAddress.value()),
|
||||||
{ 0, st::pickLocationButtonSkip, 0, st::pickLocationButtonSkip });
|
{ 0, st::pickLocationButtonSkip, 0, st::pickLocationButtonSkip });
|
||||||
button->setClickedCallback([=] {
|
button->setClickedCallback([=] {
|
||||||
_webview->eval("LocationPicker.send();");
|
_webview->eval("LocationPicker.send();");
|
||||||
});
|
});
|
||||||
|
|
||||||
AddDivider(controls);
|
AddDivider(mapControls);
|
||||||
AddSkip(controls);
|
AddSkip(mapControls);
|
||||||
AddSubsectionTitle(controls, tr::lng_maps_or_choose());
|
AddSubsectionTitle(mapControls, tr::lng_maps_or_choose());
|
||||||
|
|
||||||
SetupVenues(controls, uiShow(), _venueState.value(
|
SetupVenues(controls, uiShow(), _venueState.value(
|
||||||
) | rpl::filter([=](const PickerVenueState &state) {
|
) | rpl::filter([=](const PickerVenueState &state) {
|
||||||
|
@ -613,17 +631,19 @@ void LocationPicker::setupWindow(const Descriptor &descriptor) {
|
||||||
|
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
_body->sizeValue(),
|
_body->sizeValue(),
|
||||||
_scroll->scrollTopValue()
|
_scroll->scrollTopValue(),
|
||||||
) | rpl::start_with_next([=](QSize size, int scrollTop) {
|
_venuesSearchShown.value()
|
||||||
|
) | rpl::start_with_next([=](QSize size, int scrollTop, bool search) {
|
||||||
const auto width = size.width();
|
const auto width = size.width();
|
||||||
const auto height = size.height();
|
const auto height = size.height();
|
||||||
const auto sub = std::min(
|
const auto sub = std::min(
|
||||||
(st::pickLocationMapHeight - st::pickLocationCollapsedHeight),
|
(st::pickLocationMapHeight - st::pickLocationCollapsedHeight),
|
||||||
scrollTop);
|
scrollTop);
|
||||||
const auto mapHeight = st::pickLocationMapHeight - sub;
|
const auto mapHeight = st::pickLocationMapHeight - sub;
|
||||||
const auto scrollHeight = height - mapHeight;
|
|
||||||
_container->setGeometry(0, 0, width, mapHeight);
|
_container->setGeometry(0, 0, width, mapHeight);
|
||||||
_scroll->setGeometry(0, mapHeight, width, scrollHeight);
|
const auto scrollWidgetTop = search ? 0 : mapHeight;
|
||||||
|
const auto scrollHeight = height - scrollWidgetTop;
|
||||||
|
_scroll->setGeometry(0, scrollWidgetTop, width, scrollHeight);
|
||||||
controls->resizeToWidth(width);
|
controls->resizeToWidth(width);
|
||||||
toppad->resize(width, sub);
|
toppad->resize(width, sub);
|
||||||
}, _container->lifetime());
|
}, _container->lifetime());
|
||||||
|
@ -662,7 +682,7 @@ void LocationPicker::setupWebview(const Descriptor &descriptor) {
|
||||||
close();
|
close();
|
||||||
} else if (e->type() == QEvent::KeyPress) {
|
} else if (e->type() == QEvent::KeyPress) {
|
||||||
const auto event = static_cast<QKeyEvent*>(e.get());
|
const auto event = static_cast<QKeyEvent*>(e.get());
|
||||||
if (event->key() == Qt::Key_Escape) {
|
if (event->key() == Qt::Key_Escape && !_venuesSearchQuery) {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -722,6 +742,7 @@ void LocationPicker::setupWebview(const Descriptor &descriptor) {
|
||||||
_webview->eval(
|
_webview->eval(
|
||||||
"LocationPicker.toggleSearchVenues(true);");
|
"LocationPicker.toggleSearchVenues(true);");
|
||||||
}
|
}
|
||||||
|
venuesSearchEnableAt(location);
|
||||||
} else if (event == u"search_venues"_q) {
|
} else if (event == u"search_venues"_q) {
|
||||||
const auto lat = object.value("latitude").toDouble();
|
const auto lat = object.value("latitude").toDouble();
|
||||||
const auto lon = object.value("longitude").toDouble();
|
const auto lon = object.value("longitude").toDouble();
|
||||||
|
@ -835,26 +856,38 @@ void LocationPicker::mapReady() {
|
||||||
_scroll->show();
|
_scroll->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocationPicker::venuesRequest(
|
bool LocationPicker::venuesFromCache(
|
||||||
Core::GeoLocation location,
|
Core::GeoLocation location,
|
||||||
QString query) {
|
QString query) {
|
||||||
query = NormalizeVenuesQuery(query);
|
const auto normalized = NormalizeVenuesQuery(query);
|
||||||
auto &cache = _venuesCache[query];
|
auto &cache = _venuesCache[normalized];
|
||||||
const auto i = ranges::find_if(cache, [&](const VenuesCacheEntry &v) {
|
const auto i = ranges::find_if(cache, [&](const VenuesCacheEntry &v) {
|
||||||
return AreTheSame(v.location, location);
|
return AreTheSame(v.location, location);
|
||||||
});
|
});
|
||||||
if (i != end(cache)) {
|
if (i == end(cache)) {
|
||||||
_venueState = i->result;
|
return false;
|
||||||
return;
|
}
|
||||||
} else if (AreTheSame(_venuesRequestLocation, location)
|
_venuesRequestLocation = location;
|
||||||
&& _venuesRequestQuery == query) {
|
_venuesRequestQuery = normalized;
|
||||||
|
_venuesInitialQuery = query;
|
||||||
|
venuesApplyResults(i->result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationPicker::venuesRequest(
|
||||||
|
Core::GeoLocation location,
|
||||||
|
QString query) {
|
||||||
|
const auto normalized = NormalizeVenuesQuery(query);
|
||||||
|
if (AreTheSame(_venuesRequestLocation, location)
|
||||||
|
&& _venuesRequestQuery == normalized) {
|
||||||
return;
|
return;
|
||||||
} else if (const auto oldRequestId = base::take(_venuesRequestId)) {
|
} else if (const auto oldRequestId = base::take(_venuesRequestId)) {
|
||||||
_api.request(oldRequestId).cancel();
|
_api.request(oldRequestId).cancel();
|
||||||
}
|
}
|
||||||
_venueState = PickerVenueLoading();
|
_venueState = PickerVenueLoading();
|
||||||
_venuesRequestLocation = location;
|
_venuesRequestLocation = location;
|
||||||
_venuesRequestQuery = query;
|
_venuesRequestQuery = normalized;
|
||||||
|
_venuesInitialQuery = query;
|
||||||
if (_venuesBot) {
|
if (_venuesBot) {
|
||||||
venuesSendRequest();
|
venuesSendRequest();
|
||||||
} else if (_venuesBotRequestId) {
|
} else if (_venuesBotRequestId) {
|
||||||
|
@ -904,16 +937,61 @@ void LocationPicker::venuesSendRequest() {
|
||||||
.location = _venuesRequestLocation,
|
.location = _venuesRequestLocation,
|
||||||
.result = parsed,
|
.result = parsed,
|
||||||
});
|
});
|
||||||
if (parsed.list.empty()) {
|
venuesApplyResults(std::move(parsed));
|
||||||
_venueState = PickerVenueNothingFound{ _venuesRequestQuery };
|
|
||||||
} else {
|
|
||||||
_venueState = std::move(parsed);
|
|
||||||
}
|
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
_venueState = PickerVenueNothingFound{ _venuesRequestQuery };
|
venuesApplyResults({});
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocationPicker::venuesApplyResults(PickerVenueList venues) {
|
||||||
|
_venuesRequestId = 0;
|
||||||
|
if (venues.list.empty()) {
|
||||||
|
_venueState = PickerVenueNothingFound{ _venuesInitialQuery };
|
||||||
|
} else {
|
||||||
|
_venueState = std::move(venues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationPicker::venuesSearchEnableAt(Core::GeoLocation location) {
|
||||||
|
if (!_venuesSearchLocation) {
|
||||||
|
_window->setSearchAllowed(
|
||||||
|
tr::lng_dlg_filter(),
|
||||||
|
[=](std::optional<QString> query) {
|
||||||
|
venuesSearchChanged(query);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_venuesSearchLocation = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationPicker::venuesSearchChanged(
|
||||||
|
const std::optional<QString> &query) {
|
||||||
|
_venuesSearchQuery = query;
|
||||||
|
|
||||||
|
const auto shown = query && !query->trimmed().isEmpty();
|
||||||
|
_venuesSearchShown = shown;
|
||||||
|
if (_container->isHidden() != shown) {
|
||||||
|
_container->setVisible(!shown);
|
||||||
|
_mapControlsWrap->toggle(!shown, anim::type::instant);
|
||||||
|
if (shown) {
|
||||||
|
_venuesNoSearchLocation = _venuesRequestLocation;
|
||||||
|
_venueState = PickerVenueLoading();
|
||||||
|
} else if (_venuesNoSearchLocation) {
|
||||||
|
if (!venuesFromCache(_venuesNoSearchLocation)) {
|
||||||
|
venuesRequest(_venuesNoSearchLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shown
|
||||||
|
&& !venuesFromCache(
|
||||||
|
*_venuesSearchLocation,
|
||||||
|
*_venuesSearchQuery)) {
|
||||||
|
_venuesSearchDebounceTimer.callOnce(kSearchDebounceDelay);
|
||||||
|
} else {
|
||||||
|
_venuesSearchDebounceTimer.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LocationPicker::resolveCurrentLocation() {
|
void LocationPicker::resolveCurrentLocation() {
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
const auto window = _window.get();
|
const auto window = _window.get();
|
||||||
|
@ -924,7 +1002,9 @@ void LocationPicker::resolveCurrentLocation() {
|
||||||
}
|
}
|
||||||
LastExactLocation = location;
|
LastExactLocation = location;
|
||||||
if (location) {
|
if (location) {
|
||||||
venuesRequest(location);
|
if (_venuesSearchQuery.value_or(QString()).isEmpty()) {
|
||||||
|
venuesRequest(location);
|
||||||
|
}
|
||||||
resolveAddress(location);
|
resolveAddress(location);
|
||||||
}
|
}
|
||||||
if (_webview) {
|
if (_webview) {
|
||||||
|
@ -940,7 +1020,11 @@ void LocationPicker::processKey(
|
||||||
const QString &key,
|
const QString &key,
|
||||||
const QString &modifier) {
|
const QString &modifier) {
|
||||||
const auto ctrl = ::Platform::IsMac() ? u"cmd"_q : u"ctrl"_q;
|
const auto ctrl = ::Platform::IsMac() ? u"cmd"_q : u"ctrl"_q;
|
||||||
if (key == u"escape"_q || (key == u"w"_q && modifier == ctrl)) {
|
if (key == u"escape"_q) {
|
||||||
|
if (!_window->closeSearch()) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
} else if (key == u"w"_q && modifier == ctrl) {
|
||||||
close();
|
close();
|
||||||
} else if (key == u"m"_q && modifier == ctrl) {
|
} else if (key == u"m"_q && modifier == ctrl) {
|
||||||
minimize();
|
minimize();
|
||||||
|
|
|
@ -32,6 +32,9 @@ namespace Ui {
|
||||||
class SeparatePanel;
|
class SeparatePanel;
|
||||||
class RpWidget;
|
class RpWidget;
|
||||||
class ScrollArea;
|
class ScrollArea;
|
||||||
|
class VerticalLayout;
|
||||||
|
template <typename Widget>
|
||||||
|
class SlideWrap;
|
||||||
|
|
||||||
struct PickerVenueLoading {
|
struct PickerVenueLoading {
|
||||||
friend inline bool operator==(
|
friend inline bool operator==(
|
||||||
|
@ -110,8 +113,12 @@ private:
|
||||||
void resolveAddress(Core::GeoLocation location);
|
void resolveAddress(Core::GeoLocation location);
|
||||||
void mapReady();
|
void mapReady();
|
||||||
|
|
||||||
|
bool venuesFromCache(Core::GeoLocation location, QString query = {});
|
||||||
void venuesRequest(Core::GeoLocation location, QString query = {});
|
void venuesRequest(Core::GeoLocation location, QString query = {});
|
||||||
void venuesSendRequest();
|
void venuesSendRequest();
|
||||||
|
void venuesApplyResults(PickerVenueList venues);
|
||||||
|
void venuesSearchEnableAt(Core::GeoLocation location);
|
||||||
|
void venuesSearchChanged(const std::optional<QString> &query);
|
||||||
|
|
||||||
LocationPickerConfig _config;
|
LocationPickerConfig _config;
|
||||||
Fn<void(Data::InputVenue)> _callback;
|
Fn<void(Data::InputVenue)> _callback;
|
||||||
|
@ -119,6 +126,7 @@ private:
|
||||||
std::unique_ptr<SeparatePanel> _window;
|
std::unique_ptr<SeparatePanel> _window;
|
||||||
not_null<RpWidget*> _body;
|
not_null<RpWidget*> _body;
|
||||||
RpWidget *_container = nullptr;
|
RpWidget *_container = nullptr;
|
||||||
|
SlideWrap<VerticalLayout> *_mapControlsWrap = nullptr;
|
||||||
ScrollArea *_scroll = nullptr;
|
ScrollArea *_scroll = nullptr;
|
||||||
std::unique_ptr<Webview::Window> _webview;
|
std::unique_ptr<Webview::Window> _webview;
|
||||||
SingleQueuedInvokation _updateStyles;
|
SingleQueuedInvokation _updateStyles;
|
||||||
|
@ -133,13 +141,19 @@ private:
|
||||||
rpl::variable<PickerVenueState> _venueState;
|
rpl::variable<PickerVenueState> _venueState;
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
std::optional<Core::GeoLocation> _venuesSearchLocation;
|
||||||
|
std::optional<QString> _venuesSearchQuery;
|
||||||
|
base::Timer _venuesSearchDebounceTimer;
|
||||||
MTP::Sender _api;
|
MTP::Sender _api;
|
||||||
UserData *_venuesBot = nullptr;
|
UserData *_venuesBot = nullptr;
|
||||||
mtpRequestId _venuesBotRequestId = 0;
|
mtpRequestId _venuesBotRequestId = 0;
|
||||||
mtpRequestId _venuesRequestId = 0;
|
mtpRequestId _venuesRequestId = 0;
|
||||||
Core::GeoLocation _venuesRequestLocation;
|
Core::GeoLocation _venuesRequestLocation;
|
||||||
QString _venuesRequestQuery;
|
QString _venuesRequestQuery;
|
||||||
|
QString _venuesInitialQuery;
|
||||||
base::flat_map<QString, std::vector<VenuesCacheEntry>> _venuesCache;
|
base::flat_map<QString, std::vector<VenuesCacheEntry>> _venuesCache;
|
||||||
|
Core::GeoLocation _venuesNoSearchLocation;
|
||||||
|
rpl::variable<bool> _venuesSearchShown = false;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9b69f3855ac9feb760aef92ce98e874129303d4d
|
Subproject commit a95caea1adac69ca6d95b55fe920eac33cdf9580
|
Loading…
Add table
Reference in a new issue