mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-24 17:26:58 +00:00
Allow editing business location in Settings.
This commit is contained in:
parent
2c3ef13b01
commit
8c55364afa
@ -2387,6 +2387,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_location_title" = "Location";
|
||||
"lng_location_about" = "Display the location of your business on your account.";
|
||||
"lng_location_address" = "Enter Address";
|
||||
"lng_location_set_map" = "Set Location on Map";
|
||||
"lng_location_fallback" = "You can set your location on the map from your mobile device.";
|
||||
|
||||
"lng_hours_title" = "Business Hours";
|
||||
@ -3195,6 +3196,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_maps_point" = "Location";
|
||||
"lng_maps_point_send" = "Send This Location";
|
||||
"lng_maps_point_set" = "Set This Location";
|
||||
"lng_maps_or_choose" = "Or choose a venue";
|
||||
"lng_maps_places_in_area" = "Places in this area";
|
||||
"lng_maps_no_places" = "No places found";
|
||||
|
@ -39,7 +39,6 @@ namespace tgcalls {
|
||||
class InstanceImpl;
|
||||
class InstanceV2Impl;
|
||||
class InstanceV2ReferenceImpl;
|
||||
class InstanceV2_4_0_0Impl;
|
||||
class InstanceImplLegacy;
|
||||
void SetLegacyGlobalServerConfig(const std::string &serverConfig);
|
||||
} // namespace tgcalls
|
||||
@ -56,7 +55,6 @@ const auto kDefaultVersion = "2.4.4"_q;
|
||||
const auto Register = tgcalls::Register<tgcalls::InstanceImpl>();
|
||||
const auto RegisterV2 = tgcalls::Register<tgcalls::InstanceV2Impl>();
|
||||
const auto RegV2Ref = tgcalls::Register<tgcalls::InstanceV2ReferenceImpl>();
|
||||
const auto RegisterV240 = tgcalls::Register<tgcalls::InstanceV2_4_0_0Impl>();
|
||||
const auto RegisterLegacy = tgcalls::Register<tgcalls::InstanceImplLegacy>();
|
||||
|
||||
[[nodiscard]] base::flat_set<int64> CollectEndpointIds(
|
||||
|
@ -130,6 +130,42 @@ void BusinessInfo::saveChatIntro(ChatIntro data, Fn<void(QString)> fail) {
|
||||
session->user()->setBusinessDetails(std::move(details));
|
||||
}
|
||||
|
||||
void BusinessInfo::saveLocation(
|
||||
BusinessLocation data,
|
||||
Fn<void(QString)> fail) {
|
||||
const auto session = &_owner->session();
|
||||
auto details = session->user()->businessDetails();
|
||||
const auto &was = details.location;
|
||||
if (was == data) {
|
||||
return;
|
||||
} else {
|
||||
const auto session = &_owner->session();
|
||||
using Flag = MTPaccount_UpdateBusinessLocation::Flag;
|
||||
session->api().request(MTPaccount_UpdateBusinessLocation(
|
||||
MTP_flags((data.point ? Flag::f_geo_point : Flag())
|
||||
| (data.address.isEmpty() ? Flag() : Flag::f_address)),
|
||||
(data.point
|
||||
? MTP_inputGeoPoint(
|
||||
MTP_flags(0),
|
||||
MTP_double(data.point->lat()),
|
||||
MTP_double(data.point->lon()),
|
||||
MTPint()) // accuracy_radius
|
||||
: MTP_inputGeoPointEmpty()),
|
||||
MTP_string(data.address)
|
||||
)).fail([=](const MTP::Error &error) {
|
||||
auto details = session->user()->businessDetails();
|
||||
details.location = was;
|
||||
session->user()->setBusinessDetails(std::move(details));
|
||||
if (fail) {
|
||||
fail(error.type());
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
details.location = std::move(data);
|
||||
session->user()->setBusinessDetails(std::move(details));
|
||||
}
|
||||
|
||||
void BusinessInfo::applyAwaySettings(AwaySettings data) {
|
||||
if (_awaySettings == data) {
|
||||
return;
|
||||
|
@ -22,6 +22,7 @@ public:
|
||||
|
||||
void saveWorkingHours(WorkingHours data, Fn<void(QString)> fail);
|
||||
void saveChatIntro(ChatIntro data, Fn<void(QString)> fail);
|
||||
void saveLocation(BusinessLocation data, Fn<void(QString)> fail);
|
||||
|
||||
void saveAwaySettings(AwaySettings data, Fn<void(QString)> fail);
|
||||
void applyAwaySettings(AwaySettings data);
|
||||
|
@ -26,6 +26,11 @@ LocationPoint::LocationPoint(const MTPDgeoPoint &point)
|
||||
, _access(point.vaccess_hash().v) {
|
||||
}
|
||||
|
||||
LocationPoint::LocationPoint(float64 lat, float64 lon, IgnoreAccessHash)
|
||||
: _lat(lat)
|
||||
, _lon(lon) {
|
||||
}
|
||||
|
||||
QString LocationPoint::latAsString() const {
|
||||
return AsString(_lat);
|
||||
}
|
||||
|
@ -16,6 +16,11 @@ public:
|
||||
LocationPoint() = default;
|
||||
explicit LocationPoint(const MTPDgeoPoint &point);
|
||||
|
||||
enum IgnoreAccessHash {
|
||||
NoAccessHash,
|
||||
};
|
||||
LocationPoint(float64 lat, float64 lon, IgnoreAccessHash);
|
||||
|
||||
[[nodiscard]] QString latAsString() const;
|
||||
[[nodiscard]] QString lonAsString() const;
|
||||
[[nodiscard]] MTPGeoPoint toMTP() const;
|
||||
@ -55,7 +60,7 @@ struct InputVenue {
|
||||
QString venueType;
|
||||
|
||||
[[nodiscard]] bool justLocation() const {
|
||||
return id.isEmpty() && title.isEmpty() && address.isEmpty();
|
||||
return id.isEmpty();
|
||||
}
|
||||
|
||||
friend inline bool operator==(
|
||||
|
@ -1822,6 +1822,7 @@ void ChooseAndSendLocation(
|
||||
Ui::LocationPicker::Show({
|
||||
.parent = controller->widget(),
|
||||
.config = config,
|
||||
.chooseLabel = tr::lng_maps_point_send(),
|
||||
.recipient = action.history->peer,
|
||||
.session = &controller->session(),
|
||||
.callback = crl::guard(controller, callback),
|
||||
|
@ -632,7 +632,6 @@ void ChatIntro::setupContent(
|
||||
}
|
||||
|
||||
void ChatIntro::save() {
|
||||
const auto show = controller()->uiShow();
|
||||
const auto fail = [=](QString error) {
|
||||
};
|
||||
controller()->session().data().businessInfo().saveChatIntro(
|
||||
|
@ -8,16 +8,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "settings/business/settings_location.h"
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "data/business/data_business_info.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "settings/business/settings_recipients_helper.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "ui/controls/location_picker.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace Settings {
|
||||
@ -40,16 +54,37 @@ private:
|
||||
void setupContent(not_null<Window::SessionController*> controller);
|
||||
void save();
|
||||
|
||||
[[nodiscard]] bool mapSupported() const;
|
||||
void setupPicker(not_null<Ui::VerticalLayout*> content);
|
||||
void setupUnsupported(not_null<Ui::VerticalLayout*> content);
|
||||
|
||||
[[nodiscard]] bool mapSupported() const;
|
||||
void chooseOnMap();
|
||||
|
||||
const Ui::LocationPickerConfig _config;
|
||||
rpl::variable<Data::BusinessLocation> _data;
|
||||
rpl::variable<Data::CloudImage*> _map = nullptr;
|
||||
std::shared_ptr<QImage> _view;
|
||||
Ui::RoundRect _bottomSkipRounding;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] Ui::LocationPickerConfig ResolveBusinessMapsConfig(
|
||||
not_null<Main::Session*> session) {
|
||||
const auto &appConfig = session->appConfig();
|
||||
auto map = appConfig.get<base::flat_map<QString, QString>>(
|
||||
u"tdesktop_config_map"_q,
|
||||
base::flat_map<QString, QString>());
|
||||
return {
|
||||
.mapsToken = map[u"bmaps"_q],
|
||||
.geoToken = map[u"bgeo"_q],
|
||||
};
|
||||
}
|
||||
|
||||
Location::Location(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: BusinessSection(parent, controller)
|
||||
, _config(ResolveBusinessMapsConfig(&controller->session()))
|
||||
, _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
|
||||
setupContent(controller);
|
||||
}
|
||||
@ -65,12 +100,23 @@ rpl::producer<QString> Location::title() {
|
||||
}
|
||||
|
||||
void Location::setupContent(
|
||||
not_null<Window::SessionController*> controller) {
|
||||
not_null<Window::SessionController*> controller) {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
|
||||
#if 0 // #TODO location choosing
|
||||
if (mapSupported()) {
|
||||
setupPicker(content);
|
||||
} else {
|
||||
setupUnsupported(content);
|
||||
}
|
||||
|
||||
Ui::ResizeFitChild(this, content);
|
||||
}
|
||||
|
||||
void Location::setupPicker(not_null<Ui::VerticalLayout*> content) {
|
||||
_data = controller()->session().user()->businessDetails().location;
|
||||
|
||||
AddDividerTextWithLottie(content, {
|
||||
.lottie = u"location"_q,
|
||||
.lottieSize = st::settingsCloudPasswordIconSize,
|
||||
@ -86,36 +132,158 @@ void Location::setupContent(
|
||||
st::settingsLocationAddress,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
tr::lng_location_address(),
|
||||
QString()),
|
||||
_data.current().address),
|
||||
st::settingsChatbotsUsernameMargins);
|
||||
|
||||
_data.value(
|
||||
) | rpl::start_with_next([=](const Data::BusinessLocation &location) {
|
||||
address->setText(location.address);
|
||||
}, address->lifetime());
|
||||
|
||||
address->changes() | rpl::start_with_next([=] {
|
||||
auto copy = _data.current();
|
||||
copy.address = address->getLastText();
|
||||
_data = std::move(copy);
|
||||
}, address->lifetime());
|
||||
|
||||
AddDivider(content);
|
||||
AddSkip(content);
|
||||
|
||||
const auto maptoggle = AddButtonWithIcon(
|
||||
content,
|
||||
tr::lng_location_set_map(),
|
||||
st::settingsButton,
|
||||
{ &st::menuIconAddress }
|
||||
)->toggleOn(_data.value(
|
||||
) | rpl::map([](const Data::BusinessLocation &location) {
|
||||
return location.point.has_value();
|
||||
}));
|
||||
|
||||
maptoggle->toggledValue() | rpl::start_with_next([=](bool toggled) {
|
||||
if (!toggled) {
|
||||
auto copy = _data.current();
|
||||
if (copy.point.has_value()) {
|
||||
copy.point = std::nullopt;
|
||||
_data = std::move(copy);
|
||||
}
|
||||
} else if (!_data.current().point.has_value()) {
|
||||
_data.force_assign(_data.current());
|
||||
chooseOnMap();
|
||||
}
|
||||
}, maptoggle->lifetime());
|
||||
|
||||
const auto mapSkip = st::defaultVerticalListSkip;
|
||||
const auto mapWrap = content->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::AbstractButton>>(
|
||||
content,
|
||||
object_ptr<Ui::AbstractButton>(content),
|
||||
st::boxRowPadding + QMargins(0, mapSkip, 0, mapSkip)));
|
||||
mapWrap->toggle(_data.current().point.has_value(), anim::type::instant);
|
||||
|
||||
const auto map = mapWrap->entity();
|
||||
map->resize(map->width(), st::locationSize.height());
|
||||
|
||||
_data.value(
|
||||
) | rpl::start_with_next([=](const Data::BusinessLocation &location) {
|
||||
const auto image = location.point.has_value()
|
||||
? controller()->session().data().location(*location.point).get()
|
||||
: nullptr;
|
||||
if (image) {
|
||||
image->load(&controller()->session(), {});
|
||||
_view = image->createView();
|
||||
}
|
||||
mapWrap->toggle(image != nullptr, anim::type::normal);
|
||||
_map = image;
|
||||
}, mapWrap->lifetime());
|
||||
|
||||
map->paintRequest() | rpl::start_with_next([=] {
|
||||
auto p = QPainter(map);
|
||||
|
||||
const auto left = (map->width() - st::locationSize.width()) / 2;
|
||||
const auto rect = QRect(QPoint(left, 0), st::locationSize);
|
||||
const auto &image = _view ? *_view : QImage();
|
||||
if (!image.isNull()) {
|
||||
p.drawImage(rect, image);
|
||||
}
|
||||
|
||||
const auto paintMarker = [&](const style::icon &icon) {
|
||||
icon.paint(
|
||||
p,
|
||||
rect.x() + ((rect.width() - icon.width()) / 2),
|
||||
rect.y() + (rect.height() / 2) - icon.height(),
|
||||
width());
|
||||
};
|
||||
paintMarker(st::historyMapPoint);
|
||||
paintMarker(st::historyMapPointInner);
|
||||
}, map->lifetime());
|
||||
|
||||
controller()->session().downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
map->update();
|
||||
}, map->lifetime());
|
||||
|
||||
map->setClickedCallback([=] {
|
||||
chooseOnMap();
|
||||
});
|
||||
|
||||
showFinishes() | rpl::start_with_next([=] {
|
||||
address->setFocus();
|
||||
}, address->lifetime());
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!mapSupported()) {
|
||||
AddDividerTextWithLottie(content, {
|
||||
.lottie = u"phone"_q,
|
||||
.lottieSize = st::settingsCloudPasswordIconSize,
|
||||
.lottieMargins = st::peerAppearanceIconPadding,
|
||||
.showFinished = showFinishes(),
|
||||
.about = tr::lng_location_fallback(Ui::Text::WithEntities),
|
||||
.aboutMargins = st::peerAppearanceCoverLabelMargin,
|
||||
.parts = RectPart::Top,
|
||||
});
|
||||
} else {
|
||||
void Location::chooseOnMap() {
|
||||
const auto callback = [=](Data::InputVenue venue) {
|
||||
auto copy = _data.current();
|
||||
copy.point = Data::LocationPoint(
|
||||
venue.lat,
|
||||
venue.lon,
|
||||
Data::LocationPoint::NoAccessHash);
|
||||
copy.address = venue.address;
|
||||
_data = std::move(copy);
|
||||
};
|
||||
const auto session = &controller()->session();
|
||||
const auto current = _data.current().point;
|
||||
const auto initial = current
|
||||
? Core::GeoLocation{
|
||||
.point = { current->lat(), current->lon() },
|
||||
.accuracy = Core::GeoLocationAccuracy::Exact,
|
||||
}
|
||||
: Core::GeoLocation();
|
||||
Ui::LocationPicker::Show({
|
||||
.parent = controller()->widget(),
|
||||
.config = _config,
|
||||
.chooseLabel = tr::lng_maps_point_set(),
|
||||
.session = session,
|
||||
.initial = initial,
|
||||
.callback = crl::guard(this, callback),
|
||||
.quit = [] { Shortcuts::Launch(Shortcuts::Command::Quit); },
|
||||
.storageId = session->local().resolveStorageIdBots(),
|
||||
.closeRequests = controller()->content()->death(),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ui::ResizeFitChild(this, content);
|
||||
void Location::setupUnsupported(not_null<Ui::VerticalLayout*> content) {
|
||||
AddDividerTextWithLottie(content, {
|
||||
.lottie = u"phone"_q,
|
||||
.lottieSize = st::settingsCloudPasswordIconSize,
|
||||
.lottieMargins = st::peerAppearanceIconPadding,
|
||||
.showFinished = showFinishes(),
|
||||
.about = tr::lng_location_fallback(Ui::Text::WithEntities),
|
||||
.aboutMargins = st::peerAppearanceCoverLabelMargin,
|
||||
.parts = RectPart::Top,
|
||||
});
|
||||
}
|
||||
|
||||
void Location::save() {
|
||||
const auto fail = [=](QString error) {
|
||||
};
|
||||
auto value = _data.current();
|
||||
value.address = value.address.trimmed();
|
||||
controller()->session().data().businessInfo().saveLocation(value, fail);
|
||||
}
|
||||
|
||||
bool Location::mapSupported() const {
|
||||
return false;
|
||||
return Ui::LocationPicker::Available(_config);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -427,8 +427,9 @@ void VenuesController::rowPaintIcon(
|
||||
)"_q;
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<AbstractButton> MakeSendLocationButton(
|
||||
[[nodiscard]] object_ptr<AbstractButton> MakeChooseLocationButton(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> label,
|
||||
rpl::producer<QString> address) {
|
||||
auto result = object_ptr<FlatButton>(
|
||||
parent,
|
||||
@ -465,7 +466,7 @@ void VenuesController::rowPaintIcon(
|
||||
});
|
||||
const auto name = CreateChild<FlatLabel>(
|
||||
raw,
|
||||
tr::lng_maps_point_send(tr::now),
|
||||
std::move(label),
|
||||
st::pickLocationButtonText);
|
||||
name->show();
|
||||
const auto status = CreateChild<FlatLabel>(
|
||||
@ -679,10 +680,15 @@ bool LocationPicker::Available(const LocationPickerConfig &config) {
|
||||
void LocationPicker::setup(const Descriptor &descriptor) {
|
||||
setupWindow(descriptor);
|
||||
setupWebview(descriptor);
|
||||
if (LastExactLocation) {
|
||||
venuesRequest(LastExactLocation);
|
||||
resolveAddress(LastExactLocation);
|
||||
venuesSearchEnableAt(LastExactLocation);
|
||||
|
||||
_initialProvided = descriptor.initial.exact();
|
||||
const auto initial = _initialProvided
|
||||
? descriptor.initial
|
||||
: LastExactLocation;
|
||||
if (initial) {
|
||||
venuesRequest(initial);
|
||||
resolveAddress(initial);
|
||||
venuesSearchEnableAt(initial);
|
||||
}
|
||||
}
|
||||
|
||||
@ -717,7 +723,10 @@ void LocationPicker::setupWindow(const Descriptor &descriptor) {
|
||||
const auto toppad = mapControls->add(object_ptr<RpWidget>(controls));
|
||||
|
||||
const auto button = mapControls->add(
|
||||
MakeSendLocationButton(mapControls, _geocoderAddress.value()),
|
||||
MakeChooseLocationButton(
|
||||
mapControls,
|
||||
std::move(descriptor.chooseLabel),
|
||||
_geocoderAddress.value()),
|
||||
{ 0, st::pickLocationButtonSkip, 0, st::pickLocationButtonSkip });
|
||||
button->setClickedCallback([=] {
|
||||
_webview->eval("LocationPicker.send();");
|
||||
@ -809,7 +818,9 @@ void LocationPicker::setupWebview(const Descriptor &descriptor) {
|
||||
const auto event = object.value("event").toString();
|
||||
if (event == u"ready"_q) {
|
||||
mapReady();
|
||||
resolveCurrentLocation();
|
||||
if (!_initialProvided) {
|
||||
resolveCurrentLocation();
|
||||
}
|
||||
if (_webview) {
|
||||
_webview->focus();
|
||||
}
|
||||
@ -820,7 +831,11 @@ void LocationPicker::setupWebview(const Descriptor &descriptor) {
|
||||
} else if (event == u"send"_q) {
|
||||
const auto lat = object.value("latitude").toDouble();
|
||||
const auto lon = object.value("longitude").toDouble();
|
||||
_callback({ lat, lon });
|
||||
_callback({
|
||||
.lat = lat,
|
||||
.lon = lon,
|
||||
.address = _geocoderAddress.current(),
|
||||
});
|
||||
close();
|
||||
} else if (event == u"move_start"_q) {
|
||||
if (const auto now = _geocoderAddress.current()
|
||||
|
@ -80,8 +80,10 @@ public:
|
||||
struct Descriptor {
|
||||
RpWidget *parent = nullptr;
|
||||
LocationPickerConfig config;
|
||||
rpl::producer<QString> chooseLabel;
|
||||
PeerData *recipient = nullptr;
|
||||
not_null<Main::Session*> session;
|
||||
Core::GeoLocation initial;
|
||||
Fn<void(Data::InputVenue)> callback;
|
||||
Fn<void()> quit;
|
||||
Webview::StorageId storageId;
|
||||
@ -132,6 +134,7 @@ private:
|
||||
std::unique_ptr<Webview::Window> _webview;
|
||||
SingleQueuedInvokation _updateStyles;
|
||||
bool _subscribedToColors = false;
|
||||
bool _initialProvided = false;
|
||||
|
||||
base::Timer _geocoderResolveTimer;
|
||||
Core::GeoLocation _geocoderResolvePostponed;
|
||||
|
Loading…
Reference in New Issue
Block a user