Handle located groups as public.

This commit is contained in:
John Preston 2019-06-21 14:27:46 +02:00
parent 6537e524b8
commit 7d585ab72f
37 changed files with 468 additions and 275 deletions

View File

@ -780,6 +780,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_info_username_label" = "Username"; "lng_info_username_label" = "Username";
"lng_info_bio_label" = "Bio"; "lng_info_bio_label" = "Bio";
"lng_info_link_label" = "Link"; "lng_info_link_label" = "Link";
"lng_info_location_label" = "Location";
"lng_info_about_label" = "About"; "lng_info_about_label" = "About";
"lng_info_user_title" = "User Info"; "lng_info_user_title" = "User Info";
"lng_info_bot_title" = "Bot Info"; "lng_info_bot_title" = "Bot Info";
@ -844,6 +845,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_peer_group_type" = "Group type"; "lng_manage_peer_group_type" = "Group type";
"lng_manage_peer_channel_type" = "Channel type"; "lng_manage_peer_channel_type" = "Channel type";
"lng_manage_peer_link_type" = "Link type";
"lng_manage_peer_link_permanent" = "Permanent link";
"lng_manage_peer_link_invite" = "Invite link";
"lng_manage_private_group_title" = "Private"; "lng_manage_private_group_title" = "Private";
"lng_manage_public_group_title" = "Public"; "lng_manage_public_group_title" = "Public";
"lng_manage_private_peer_title" = "Private"; "lng_manage_private_peer_title" = "Private";
@ -924,6 +928,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_create_public_group_about" = "Anyone can find the group in search and join, chat history is available to everybody"; "lng_create_public_group_about" = "Anyone can find the group in search and join, chat history is available to everybody";
"lng_create_private_group_title" = "Private Group"; "lng_create_private_group_title" = "Private Group";
"lng_create_private_group_about" = "People can only join if they were invited or have an invite link"; "lng_create_private_group_about" = "People can only join if they were invited or have an invite link";
"lng_create_permanent_link_title" = "Permanent link";
"lng_create_invite_link_title" = "Invite link";
"lng_create_invite_link_about" = "People can join if they were invited, have an invite link, or from \"Groups nearby\"";
"lng_create_group_skip" = "Skip"; "lng_create_group_skip" = "Skip";
"lng_create_channel_link_about" = "You can use a-z, 0-9 and underscores.\nMinimum length is 5 characters."; "lng_create_channel_link_about" = "You can use a-z, 0-9 and underscores.\nMinimum length is 5 characters.";
@ -1048,6 +1056,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_channels_too_much_public_revoke_confirm_channel" = "Are you sure you want to revoke the link {link}?\n\nThe channel «{group}» will become private."; "lng_channels_too_much_public_revoke_confirm_channel" = "Are you sure you want to revoke the link {link}?\n\nThe channel «{group}» will become private.";
"lng_channels_too_much_public_revoke" = "Revoke"; "lng_channels_too_much_public_revoke" = "Revoke";
"lng_channels_too_much_public_other" = "Sorry, the target user has too many public groups or channels already. Please ask them to make one of their existing groups or channels private first."; "lng_channels_too_much_public_other" = "Sorry, the target user has too many public groups or channels already. Please ask them to make one of their existing groups or channels private first.";
"lng_channels_too_much_located_other" = "Sorry, the target user has too many location-based groups already. Please ask them to delete or transfer one of their existing ones first.";
"lng_group_invite_bad_link" = "This invite link is broken or has expired."; "lng_group_invite_bad_link" = "This invite link is broken or has expired.";
"lng_group_invite_join" = "Join"; "lng_group_invite_join" = "Join";

View File

@ -691,11 +691,11 @@ QString ApiWrap::exportDirectMessageLink(not_null<HistoryItem*> item) {
const auto itemId = item->fullId(); const auto itemId = item->fullId();
const auto channel = item->history()->peer->asChannel(); const auto channel = item->history()->peer->asChannel();
const auto fallback = [&] { const auto fallback = [&] {
const auto base = channel->isPublic() const auto base = channel->hasUsername()
? channel->username ? channel->username
: "c/" + QString::number(channel->bareId()); : "c/" + QString::number(channel->bareId());
const auto query = base + '/' + QString::number(item->id); const auto query = base + '/' + QString::number(item->id);
if (channel->isPublic() && !channel->isMegagroup()) { if (channel->hasUsername() && !channel->isMegagroup()) {
if (const auto media = item->media()) { if (const auto media = item->media()) {
if (const auto document = media->document()) { if (const auto document = media->document()) {
if (document->isVideoMessage()) { if (document->isVideoMessage()) {

View File

@ -537,6 +537,8 @@ void EditAdminBox::sendTransferRequestFrom(
const auto problem = [&] { const auto problem = [&] {
if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) { if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
return tr::lng_channels_too_much_public_other(tr::now); return tr::lng_channels_too_much_public_other(tr::now);
} else if (type == qstr("CHANNELS_ADMIN_LOCATED_TOO_MUCH")) {
return tr::lng_channels_too_much_located_other(tr::now);
} else if (type == qstr("ADMINS_TOO_MUCH")) { } else if (type == qstr("ADMINS_TOO_MUCH")) {
return (channel->isBroadcast() return (channel->isBroadcast()
? tr::lng_error_admin_limit_channel ? tr::lng_error_admin_limit_channel

View File

@ -240,6 +240,7 @@ private:
std::optional<Privacy> _privacySavedValue; std::optional<Privacy> _privacySavedValue;
std::optional<ChannelData*> _linkedChatSavedValue; std::optional<ChannelData*> _linkedChatSavedValue;
ChannelData *_linkedChatOriginalValue = nullptr; ChannelData *_linkedChatOriginalValue = nullptr;
bool _channelHasLocationOriginalValue = false;
std::optional<HistoryVisibility> _historyVisibilitySavedValue; std::optional<HistoryVisibility> _historyVisibilitySavedValue;
std::optional<QString> _usernameSavedValue; std::optional<QString> _usernameSavedValue;
std::optional<bool> _signaturesSavedValue; std::optional<bool> _signaturesSavedValue;
@ -489,7 +490,8 @@ void Controller::refreshHistoryVisibility(anim::type animated) {
return; return;
} }
_controls.historyVisibilityWrap->toggle( _controls.historyVisibilityWrap->toggle(
(_privacySavedValue != Privacy::Public (_privacySavedValue != Privacy::HasUsername
&& !_channelHasLocationOriginalValue
&& (!_linkedChatSavedValue || !*_linkedChatSavedValue)), && (!_linkedChatSavedValue || !*_linkedChatSavedValue)),
animated); animated);
}; };
@ -506,6 +508,7 @@ void Controller::showEditPeerTypeBox(
Ui::show( Ui::show(
Box<EditPeerTypeBox>( Box<EditPeerTypeBox>(
_peer, _peer,
_channelHasLocationOriginalValue,
boxCallback, boxCallback,
_privacySavedValue, _privacySavedValue,
_usernameSavedValue, _usernameSavedValue,
@ -569,26 +572,34 @@ void Controller::fillPrivacyTypeButton() {
Expects(_controls.buttonsLayout != nullptr); Expects(_controls.buttonsLayout != nullptr);
// Create Privacy Button. // Create Privacy Button.
const auto hasLocation = _peer->isChannel()
&& _peer->asChannel()->hasLocation();
_privacySavedValue = (_peer->isChannel() _privacySavedValue = (_peer->isChannel()
&& _peer->asChannel()->isPublic()) && _peer->asChannel()->hasUsername())
? Privacy::Public ? Privacy::HasUsername
: Privacy::Private; : Privacy::NoUsername;
const auto isGroup = (_peer->isChat() || _peer->isMegagroup()); const auto isGroup = (_peer->isChat() || _peer->isMegagroup());
AddButtonWithText( AddButtonWithText(
_controls.buttonsLayout, _controls.buttonsLayout,
(isGroup (hasLocation
? tr::lng_manage_peer_link_type
: isGroup
? tr::lng_manage_peer_group_type ? tr::lng_manage_peer_group_type
: tr::lng_manage_peer_channel_type)(), : tr::lng_manage_peer_channel_type)(),
_privacyTypeUpdates.events( _privacyTypeUpdates.events(
) | rpl::map([=](Privacy flag) { ) | rpl::map([=](Privacy flag) {
return (Privacy::Public == flag return (flag == Privacy::HasUsername)
? (isGroup ? (hasLocation
? tr::lng_manage_peer_link_permanent
: isGroup
? tr::lng_manage_public_group_title ? tr::lng_manage_public_group_title
: tr::lng_manage_public_peer_title) : tr::lng_manage_public_peer_title)()
: (isGroup : (hasLocation
? tr::lng_manage_peer_link_invite
: isGroup
? tr::lng_manage_private_group_title ? tr::lng_manage_private_group_title
: tr::lng_manage_private_peer_title))(); : tr::lng_manage_private_peer_title)();
}) | rpl::flatten_latest(), }) | rpl::flatten_latest(),
[=] { showEditPeerTypeBox(); }); [=] { showEditPeerTypeBox(); });
@ -640,9 +651,7 @@ void Controller::fillInviteLinkButton() {
Expects(_controls.buttonsLayout != nullptr); Expects(_controls.buttonsLayout != nullptr);
const auto buttonCallback = [=] { const auto buttonCallback = [=] {
Ui::show( Ui::show(Box<EditPeerTypeBox>(_peer), LayerOption::KeepOther);
Box<EditPeerTypeBox>(_peer),
LayerOption::KeepOther);
}; };
AddButtonWithText( AddButtonWithText(
@ -685,6 +694,7 @@ void Controller::fillHistoryVisibilityButton() {
_historyVisibilitySavedValue = (!channel || channel->hiddenPreHistory()) _historyVisibilitySavedValue = (!channel || channel->hiddenPreHistory())
? HistoryVisibility::Hidden ? HistoryVisibility::Hidden
: HistoryVisibility::Visible; : HistoryVisibility::Visible;
_channelHasLocationOriginalValue = channel && channel->hasLocation();
const auto updateHistoryVisibility = const auto updateHistoryVisibility =
std::make_shared<rpl::event_stream<HistoryVisibility>>(); std::make_shared<rpl::event_stream<HistoryVisibility>>();
@ -945,7 +955,7 @@ std::optional<Controller::Saving> Controller::validate() const {
bool Controller::validateUsername(Saving &to) const { bool Controller::validateUsername(Saving &to) const {
if (!_privacySavedValue) { if (!_privacySavedValue) {
return true; return true;
} else if (_privacySavedValue != Privacy::Public) { } else if (_privacySavedValue != Privacy::HasUsername) {
to.username = QString(); to.username = QString();
return true; return true;
} }
@ -994,7 +1004,8 @@ bool Controller::validateDescription(Saving &to) const {
bool Controller::validateHistoryVisibility(Saving &to) const { bool Controller::validateHistoryVisibility(Saving &to) const {
if (!_controls.historyVisibilityWrap if (!_controls.historyVisibilityWrap
|| !_controls.historyVisibilityWrap->toggled() || !_controls.historyVisibilityWrap->toggled()
|| (_privacySavedValue == Privacy::Public)) { || _channelHasLocationOriginalValue
|| (_privacySavedValue == Privacy::HasUsername)) {
return true; return true;
} }
to.hiddenPreHistory to.hiddenPreHistory

View File

@ -51,6 +51,7 @@ public:
Controller( Controller(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer, not_null<PeerData*> peer,
bool useLocationPhrases,
std::optional<Privacy> privacySavedValue, std::optional<Privacy> privacySavedValue,
std::optional<QString> usernameSavedValue); std::optional<QString> usernameSavedValue);
@ -129,10 +130,8 @@ private:
void addRoundButton( void addRoundButton(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
Privacy value, Privacy value,
const QString &groupText, const QString &text,
const QString &channelText, rpl::producer<QString> about);
rpl::producer<QString> groupAbout,
rpl::producer<QString> channelAbout);
bool inviteLinkShown(); bool inviteLinkShown();
QString inviteLinkText(); QString inviteLinkText();
@ -141,6 +140,7 @@ private:
std::optional<Privacy> _privacySavedValue; std::optional<Privacy> _privacySavedValue;
std::optional<QString> _usernameSavedValue; std::optional<QString> _usernameSavedValue;
bool _useLocationPhrases = false;
bool _isGroup = false; bool _isGroup = false;
bool _isInviteLink = false; bool _isInviteLink = false;
bool _isAllowSave = false; bool _isAllowSave = false;
@ -158,11 +158,13 @@ private:
Controller::Controller( Controller::Controller(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer, not_null<PeerData*> peer,
bool useLocationPhrases,
std::optional<Privacy> privacySavedValue, std::optional<Privacy> privacySavedValue,
std::optional<QString> usernameSavedValue) std::optional<QString> usernameSavedValue)
: _peer(peer) : _peer(peer)
, _privacySavedValue(privacySavedValue) , _privacySavedValue(privacySavedValue)
, _usernameSavedValue(usernameSavedValue) , _usernameSavedValue(usernameSavedValue)
, _useLocationPhrases(useLocationPhrases)
, _isGroup(_peer->isChat() || _peer->isMegagroup()) , _isGroup(_peer->isChat() || _peer->isMegagroup())
, _isInviteLink(!_privacySavedValue.has_value() , _isInviteLink(!_privacySavedValue.has_value()
&& !_usernameSavedValue.has_value()) && !_usernameSavedValue.has_value())
@ -189,7 +191,7 @@ void Controller::createContent() {
_wrap->add(createInviteLinkEdit()); _wrap->add(createInviteLinkEdit());
_wrap->add(createUsernameEdit()); _wrap->add(createUsernameEdit());
if (_controls.privacy->value() == Privacy::Private) { if (_controls.privacy->value() == Privacy::NoUsername) {
checkUsernameAvailability(); checkUsernameAvailability();
} }
} }
@ -197,21 +199,19 @@ void Controller::createContent() {
void Controller::addRoundButton( void Controller::addRoundButton(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
Privacy value, Privacy value,
const QString &groupText, const QString &text,
const QString &channelText, rpl::producer<QString> about) {
rpl::producer<QString> groupAbout,
rpl::producer<QString> channelAbout) {
container->add(object_ptr<Ui::Radioenum<Privacy>>( container->add(object_ptr<Ui::Radioenum<Privacy>>(
container, container,
_controls.privacy, _controls.privacy,
value, value,
(_isGroup ? groupText : channelText), text,
st::editPeerPrivacyBoxCheckbox)); st::editPeerPrivacyBoxCheckbox));
container->add(object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>( container->add(object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
container, container,
object_ptr<Ui::FlatLabel>( object_ptr<Ui::FlatLabel>(
container, container,
std::move(_isGroup ? groupAbout : channelAbout), std::move(about),
st::editPeerPrivacyLabel), st::editPeerPrivacyLabel),
st::editPeerPrivacyLabelMargins)); st::editPeerPrivacyLabelMargins));
container->add(object_ptr<Ui::FixedHeightWidget>( container->add(object_ptr<Ui::FixedHeightWidget>(
@ -242,24 +242,35 @@ void Controller::fillPrivaciesButtons(
const auto container = result->entity(); const auto container = result->entity();
const auto isPublic = _peer->isChannel() const auto isPublic = _peer->isChannel()
&& _peer->asChannel()->isPublic(); && _peer->asChannel()->hasUsername();
_controls.privacy = std::make_shared<Ui::RadioenumGroup<Privacy>>( _controls.privacy = std::make_shared<Ui::RadioenumGroup<Privacy>>(
savedValue.value_or(isPublic ? Privacy::Public : Privacy::Private)); savedValue.value_or(
isPublic ? Privacy::HasUsername : Privacy::NoUsername));
addRoundButton( addRoundButton(
container, container,
Privacy::Public, Privacy::HasUsername,
tr::lng_create_public_group_title(tr::now), (_useLocationPhrases
tr::lng_create_public_channel_title(tr::now), ? tr::lng_create_permanent_link_title
tr::lng_create_public_group_about(), : _isGroup
tr::lng_create_public_channel_about()); ? tr::lng_create_public_group_title
: tr::lng_create_public_channel_title)(tr::now),
(_isGroup
? tr::lng_create_public_group_about
: tr::lng_create_public_channel_about)());
addRoundButton( addRoundButton(
container, container,
Privacy::Private, Privacy::NoUsername,
tr::lng_create_private_group_title(tr::now), (_useLocationPhrases
tr::lng_create_private_channel_title(tr::now), ? tr::lng_create_invite_link_title
tr::lng_create_private_group_about(), : _isGroup
tr::lng_create_private_channel_about()); ? tr::lng_create_private_group_title
: tr::lng_create_private_channel_title)(tr::now),
(_useLocationPhrases
? tr::lng_create_invite_link_about
: _isGroup
? tr::lng_create_private_group_about
: tr::lng_create_private_channel_about)());
_controls.privacy->setChangedCallback([=](Privacy value) { _controls.privacy->setChangedCallback([=](Privacy value) {
privacyChanged(value); privacyChanged(value);
@ -343,7 +354,7 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
&Ui::UsernameInput::changed, &Ui::UsernameInput::changed,
[this] { usernameChanged(); }); [this] { usernameChanged(); });
const auto shown = (_controls.privacy->value() == Privacy::Public); const auto shown = (_controls.privacy->value() == Privacy::HasUsername);
result->toggle(shown, anim::type::instant); result->toggle(shown, anim::type::instant);
return std::move(result); return std::move(result);
@ -352,14 +363,14 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
void Controller::privacyChanged(Privacy value) { void Controller::privacyChanged(Privacy value) {
const auto toggleEditUsername = [&] { const auto toggleEditUsername = [&] {
_controls.usernameWrap->toggle( _controls.usernameWrap->toggle(
(value == Privacy::Public), (value == Privacy::HasUsername),
anim::type::instant); anim::type::instant);
}; };
const auto refreshVisibilities = [&] { const auto refreshVisibilities = [&] {
// Now first we need to hide that was shown. // Now first we need to hide that was shown.
// Otherwise box will change own Y position. // Otherwise box will change own Y position.
if (value == Privacy::Public) { if (value == Privacy::HasUsername) {
refreshCreateInviteLink(); refreshCreateInviteLink();
refreshEditInviteLink(); refreshEditInviteLink();
toggleEditUsername(); toggleEditUsername();
@ -372,12 +383,12 @@ void Controller::privacyChanged(Privacy value) {
refreshEditInviteLink(); refreshEditInviteLink();
} }
}; };
if (value == Privacy::Public) { if (value == Privacy::HasUsername) {
if (_usernameState == UsernameState::TooMany) { if (_usernameState == UsernameState::TooMany) {
askUsernameRevoke(); askUsernameRevoke();
return; return;
} else if (_usernameState == UsernameState::NotAvailable) { } else if (_usernameState == UsernameState::NotAvailable) {
_controls.privacy->setValue(Privacy::Private); _controls.privacy->setValue(Privacy::NoUsername);
return; return;
} }
refreshVisibilities(); refreshVisibilities();
@ -394,7 +405,7 @@ void Controller::checkUsernameAvailability() {
if (!_controls.usernameInput) { if (!_controls.usernameInput) {
return; return;
} }
const auto initial = (_controls.privacy->value() != Privacy::Public); const auto initial = (_controls.privacy->value() != Privacy::HasUsername);
const auto checking = initial const auto checking = initial
? qsl(".bad.") ? qsl(".bad.")
: getUsernameInput(); : getUsernameInput();
@ -425,14 +436,14 @@ void Controller::checkUsernameAvailability() {
_usernameState = UsernameState::Normal; _usernameState = UsernameState::Normal;
if (type == qstr("CHANNEL_PUBLIC_GROUP_NA")) { if (type == qstr("CHANNEL_PUBLIC_GROUP_NA")) {
_usernameState = UsernameState::NotAvailable; _usernameState = UsernameState::NotAvailable;
_controls.privacy->setValue(Privacy::Private); _controls.privacy->setValue(Privacy::NoUsername);
} else if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) { } else if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
_usernameState = UsernameState::TooMany; _usernameState = UsernameState::TooMany;
if (_controls.privacy->value() == Privacy::Public) { if (_controls.privacy->value() == Privacy::HasUsername) {
askUsernameRevoke(); askUsernameRevoke();
} }
} else if (initial) { } else if (initial) {
if (_controls.privacy->value() == Privacy::Public) { if (_controls.privacy->value() == Privacy::HasUsername) {
_controls.usernameResult = nullptr; _controls.usernameResult = nullptr;
setFocusUsername(); setFocusUsername();
} }
@ -446,10 +457,10 @@ void Controller::checkUsernameAvailability() {
} }
void Controller::askUsernameRevoke() { void Controller::askUsernameRevoke() {
_controls.privacy->setValue(Privacy::Private); _controls.privacy->setValue(Privacy::NoUsername);
const auto revokeCallback = crl::guard(this, [this] { const auto revokeCallback = crl::guard(this, [this] {
_usernameState = UsernameState::Normal; _usernameState = UsernameState::Normal;
_controls.privacy->setValue(Privacy::Public); _controls.privacy->setValue(Privacy::HasUsername);
checkUsernameAvailability(); checkUsernameAvailability();
}); });
Ui::show( Ui::show(
@ -683,20 +694,26 @@ void Controller::refreshCreateInviteLink() {
bool Controller::inviteLinkShown() { bool Controller::inviteLinkShown() {
return !_controls.privacy return !_controls.privacy
|| (_controls.privacy->value() == Privacy::Private) || (_controls.privacy->value() == Privacy::NoUsername)
|| _isInviteLink; || _isInviteLink;
} }
} // namespace } // namespace
EditPeerTypeBox::EditPeerTypeBox(QWidget*, not_null<PeerData*> peer)
: EditPeerTypeBox(nullptr, peer, false, {}, {}, {}, {}) {
}
EditPeerTypeBox::EditPeerTypeBox( EditPeerTypeBox::EditPeerTypeBox(
QWidget*, QWidget*,
not_null<PeerData*> peer, not_null<PeerData*> peer,
bool useLocationPhrases,
std::optional<FnMut<void(Privacy, QString)>> savedCallback, std::optional<FnMut<void(Privacy, QString)>> savedCallback,
std::optional<Privacy> privacySaved, std::optional<Privacy> privacySaved,
std::optional<QString> usernameSaved, std::optional<QString> usernameSaved,
std::optional<rpl::producer<QString>> usernameError) std::optional<rpl::producer<QString>> usernameError)
: _peer(peer) : _peer(peer)
, _useLocationPhrases(useLocationPhrases)
, _savedCallback(std::move(savedCallback)) , _savedCallback(std::move(savedCallback))
, _privacySavedValue(privacySaved) , _privacySavedValue(privacySaved)
, _usernameSavedValue(usernameSaved) , _usernameSavedValue(usernameSaved)
@ -716,6 +733,7 @@ void EditPeerTypeBox::prepare() {
this, this,
content, content,
_peer, _peer,
_useLocationPhrases,
_privacySavedValue, _privacySavedValue,
_usernameSavedValue); _usernameSavedValue);
_focusRequests.events( _focusRequests.events(
@ -735,14 +753,14 @@ void EditPeerTypeBox::prepare() {
if (!controller->isInviteLink() && _savedCallback.has_value()) { if (!controller->isInviteLink() && _savedCallback.has_value()) {
addButton(tr::lng_settings_save(), [=] { addButton(tr::lng_settings_save(), [=] {
const auto v = controller->getPrivacy(); const auto v = controller->getPrivacy();
if (!controller->isAllowSave() && (v == Privacy::Public)) { if (!controller->isAllowSave() && (v == Privacy::HasUsername)) {
controller->setFocusUsername(); controller->setFocusUsername();
return; return;
} }
auto local = std::move(*_savedCallback); auto local = std::move(*_savedCallback);
local(v, local(v,
(v == Privacy::Public) (v == Privacy::HasUsername)
? controller->getUsernameInput() ? controller->getUsernameInput()
: QString()); // We dont need username with private type. : QString()); // We dont need username with private type.
closeBox(); closeBox();

View File

@ -25,8 +25,8 @@ class Button;
} // namespace Info } // namespace Info
enum class Privacy { enum class Privacy {
Public, HasUsername,
Private, NoUsername,
}; };
enum class UsernameState { enum class UsernameState {
@ -37,12 +37,16 @@ enum class UsernameState {
class EditPeerTypeBox : public BoxContent { class EditPeerTypeBox : public BoxContent {
public: public:
// Edit just the invite link.
EditPeerTypeBox(QWidget*, not_null<PeerData*> peer);
EditPeerTypeBox( EditPeerTypeBox(
QWidget*, QWidget*,
not_null<PeerData*> p, not_null<PeerData*> peer,
std::optional<FnMut<void(Privacy, QString)>> savedCallback = {}, bool useLocationPhrases,
std::optional<Privacy> privacySaved = {}, std::optional<FnMut<void(Privacy, QString)>> savedCallback,
std::optional<QString> usernameSaved = {}, std::optional<Privacy> privacySaved,
std::optional<QString> usernameSaved,
std::optional<rpl::producer<QString>> usernameError = {}); std::optional<rpl::producer<QString>> usernameError = {});
protected: protected:
@ -51,6 +55,7 @@ protected:
private: private:
not_null<PeerData*> _peer; not_null<PeerData*> _peer;
bool _useLocationPhrases = false;
std::optional<FnMut<void(Privacy, QString)>> _savedCallback; std::optional<FnMut<void(Privacy, QString)>> _savedCallback;
std::optional<Privacy> _privacySavedValue; std::optional<Privacy> _privacySavedValue;

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_location.h"
#include "history/history.h" #include "history/history.h"
#include "observer_peer.h" #include "observer_peer.h"
#include "auth_session.h" #include "auth_session.h"
@ -32,6 +33,14 @@ void MegagroupInfo::setMigrateFromChat(ChatData *chat) {
_migratedFrom = chat; _migratedFrom = chat;
} }
const ChannelLocation *MegagroupInfo::getLocation() const {
return _location.address.isEmpty() ? nullptr : &_location;
}
void MegagroupInfo::setLocation(const ChannelLocation &location) {
_location = location;
}
ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id) ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
: PeerData(owner, id) : PeerData(owner, id)
, inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))) { , inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))) {
@ -92,6 +101,37 @@ bool ChannelData::canHaveInviteLink() const {
|| amCreator(); || amCreator();
} }
void ChannelData::setLocation(const MTPChannelLocation &data) {
if (!mgInfo) {
return;
}
const auto was = mgInfo->getLocation();
const auto wasValue = was ? *was : ChannelLocation();
data.match([&](const MTPDchannelLocation &data) {
data.vgeo_point.match([&](const MTPDgeoPoint &point) {
mgInfo->setLocation({
qs(data.vaddress),
Data::LocationPoint(point)
});
}, [&](const MTPDgeoPointEmpty &) {
mgInfo->setLocation(ChannelLocation());
});
}, [&](const MTPDchannelLocationEmpty &) {
mgInfo->setLocation(ChannelLocation());
});
const auto now = mgInfo->getLocation();
const auto nowValue = now ? *now : ChannelLocation();
if (was != now || (was && wasValue != nowValue)) {
Notify::peerUpdatedDelayed(
this,
Notify::PeerUpdate::Flag::ChannelLocation);
}
}
const ChannelLocation *ChannelData::getLocation() const {
return mgInfo ? mgInfo->getLocation() : nullptr;
}
void ChannelData::setLinkedChat(ChannelData *linked) { void ChannelData::setLinkedChat(ChannelData *linked) {
if (_linkedChat != linked) { if (_linkedChat != linked) {
_linkedChat = linked; _linkedChat = linked;
@ -600,11 +640,14 @@ void ApplyChannelUpdate(
? update.vkicked_count.v ? update.vkicked_count.v
: 0); : 0);
channel->setInviteLink(update.vexported_invite.match([&]( channel->setInviteLink(update.vexported_invite.match([&](
const MTPDchatInviteExported & data) { const MTPDchatInviteExported &data) {
return qs(data.vlink); return qs(data.vlink);
}, [&](const MTPDchatInviteEmpty &) { }, [&](const MTPDchatInviteEmpty &) {
return QString(); return QString();
})); }));
channel->setLocation(update.has_location()
? update.vlocation
: MTPChannelLocation(MTP_channelLocationEmpty()));
channel->setLinkedChat(update.has_linked_chat_id() channel->setLinkedChat(update.has_linked_chat_id()
? channel->owner().channelLoaded(update.vlinked_chat_id.v) ? channel->owner().channelLoaded(update.vlinked_chat_id.v)
: nullptr); : nullptr);

View File

@ -9,6 +9,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_pts_waiter.h" #include "data/data_pts_waiter.h"
#include "data/data_location.h"
struct ChannelLocation {
QString address;
Data::LocationPoint point;
friend inline bool operator==(
const ChannelLocation &a,
const ChannelLocation &b) {
return a.address.isEmpty()
? b.address.isEmpty()
: (a.address == b.address && a.point == b.point);
}
friend inline bool operator!=(
const ChannelLocation &a,
const ChannelLocation &b) {
return !(a == b);
}
};
class MegagroupInfo { class MegagroupInfo {
public: public:
@ -34,6 +53,9 @@ public:
ChatData *getMigrateFromChat() const; ChatData *getMigrateFromChat() const;
void setMigrateFromChat(ChatData *chat); void setMigrateFromChat(ChatData *chat);
const ChannelLocation *getLocation() const;
void setLocation(const ChannelLocation &location);
std::deque<not_null<UserData*>> lastParticipants; std::deque<not_null<UserData*>> lastParticipants;
base::flat_map<not_null<UserData*>, Admin> lastAdmins; base::flat_map<not_null<UserData*>, Admin> lastAdmins;
base::flat_map<not_null<UserData*>, Restricted> lastRestricted; base::flat_map<not_null<UserData*>, Restricted> lastRestricted;
@ -57,6 +79,7 @@ public:
private: private:
ChatData *_migratedFrom = nullptr; ChatData *_migratedFrom = nullptr;
ChannelLocation _location;
}; };
@ -79,7 +102,8 @@ public:
static constexpr auto kEssentialFullFlags = 0 static constexpr auto kEssentialFullFlags = 0
| MTPDchannelFull::Flag::f_can_view_participants | MTPDchannelFull::Flag::f_can_view_participants
| MTPDchannelFull::Flag::f_can_set_username | MTPDchannelFull::Flag::f_can_set_username
| MTPDchannelFull::Flag::f_can_set_stickers; | MTPDchannelFull::Flag::f_can_set_stickers
| MTPDchannelFull::Flag::f_location;
using FullFlags = Data::Flags< using FullFlags = Data::Flags<
MTPDchannelFull::Flags, MTPDchannelFull::Flags,
kEssentialFullFlags>; kEssentialFullFlags>;
@ -206,9 +230,15 @@ public:
bool isBroadcast() const { bool isBroadcast() const {
return flags() & MTPDchannel::Flag::f_broadcast; return flags() & MTPDchannel::Flag::f_broadcast;
} }
bool isPublic() const { bool hasUsername() const {
return flags() & MTPDchannel::Flag::f_username; return flags() & MTPDchannel::Flag::f_username;
} }
bool hasLocation() const {
return fullFlags() & MTPDchannelFull::Flag::f_location;
}
bool isPublic() const {
return hasUsername() || hasLocation();
}
bool amCreator() const { bool amCreator() const {
return flags() & MTPDchannel::Flag::f_creator; return flags() & MTPDchannel::Flag::f_creator;
} }
@ -279,6 +309,9 @@ public:
QString inviteLink() const; QString inviteLink() const;
bool canHaveInviteLink() const; bool canHaveInviteLink() const;
void setLocation(const MTPChannelLocation &data);
const ChannelLocation *getLocation() const;
void setLinkedChat(ChannelData *linked); void setLinkedChat(ChannelData *linked);
ChannelData *linkedChat() const; ChannelData *linkedChat() const;

View File

@ -0,0 +1,44 @@
/*
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/data_location.h"
#include "ui/image/image.h"
#include "data/data_file_origin.h"
namespace Data {
namespace {
GeoPointLocation ComputeLocation(const Data::LocationPoint &point) {
const auto scale = 1 + (cScale() * cIntRetinaFactor()) / 200;
const auto zoom = 13 + (scale - 1);
const auto w = st::locationSize.width() / scale;
const auto h = st::locationSize.height() / scale;
auto result = GeoPointLocation();
result.lat = point.lat();
result.lon = point.lon();
result.access = point.accessHash();
result.width = w;
result.height = h;
result.zoom = zoom;
result.scale = scale;
return result;
}
} // namespace
LocationThumbnail::LocationThumbnail(const LocationPoint &point)
: point(point)
, thumb(Images::Create(ComputeLocation(point))) {
}
void LocationThumbnail::load(FileOrigin origin) {
thumb->load(origin);
}
} // namespace Data

View File

@ -0,0 +1,99 @@
/*
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
*/
#pragma once
namespace Data {
struct FileOrigin;
class LocationPoint {
public:
LocationPoint() = default;
explicit LocationPoint(const MTPDgeoPoint &point)
: _lat(point.vlat.v)
, _lon(point.vlong.v)
, _access(point.vaccess_hash.v) {
}
QString latAsString() const {
return AsString(_lat);
}
QString lonAsString() const {
return AsString(_lon);
}
MTPGeoPoint toMTP() const {
return MTP_geoPoint(
MTP_double(_lon),
MTP_double(_lat),
MTP_long(_access));
}
float64 lat() const {
return _lat;
}
float64 lon() const {
return _lon;
}
uint64 accessHash() const {
return _access;
}
inline size_t hash() const {
#ifndef OS_MAC_OLD
return QtPrivate::QHashCombine().operator()(
std::hash<float64>()(_lat),
_lon);
#else // OS_MAC_OLD
const auto h1 = std::hash<float64>()(_lat);
const auto h2 = std::hash<float64>()(_lon);
return ((h1 << 16) | (h1 >> 16)) ^ h2;
#endif // OS_MAC_OLD
}
private:
static QString AsString(float64 value) {
constexpr auto kPrecision = 6;
return QString::number(value, 'f', kPrecision);
}
friend inline bool operator==(const LocationPoint &a, const LocationPoint &b) {
return (a._lat == b._lat) && (a._lon == b._lon);
}
friend inline bool operator<(const LocationPoint &a, const LocationPoint &b) {
return (a._lat < b._lat) || ((a._lat == b._lat) && (a._lon < b._lon));
}
float64 _lat = 0;
float64 _lon = 0;
uint64 _access = 0;
};
struct LocationThumbnail {
LocationThumbnail(const LocationPoint &point);
LocationPoint point;
ImagePtr thumb;
void load(FileOrigin origin);
};
} // namespace Data
namespace std {
template <>
struct hash<Data::LocationPoint> {
size_t operator()(const Data::LocationPoint &value) const {
return value.hash();
}
};
} // namespace std

View File

@ -168,7 +168,7 @@ const Invoice *Media::invoice() const {
return nullptr; return nullptr;
} }
LocationData *Media::location() const { LocationThumbnail *Media::location() const {
return nullptr; return nullptr;
} }
@ -861,17 +861,17 @@ std::unique_ptr<HistoryMedia> MediaContact::createView(
MediaLocation::MediaLocation( MediaLocation::MediaLocation(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
const LocationCoords &coords) const LocationPoint &point)
: MediaLocation(parent, coords, QString(), QString()) { : MediaLocation(parent, point, QString(), QString()) {
} }
MediaLocation::MediaLocation( MediaLocation::MediaLocation(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
const LocationCoords &coords, const LocationPoint &point,
const QString &title, const QString &title,
const QString &description) const QString &description)
: Media(parent) : Media(parent)
, _location(parent->history()->owner().location(coords)) , _location(parent->history()->owner().location(point))
, _title(title) , _title(title)
, _description(description) { , _description(description) {
} }
@ -879,12 +879,12 @@ MediaLocation::MediaLocation(
std::unique_ptr<Media> MediaLocation::clone(not_null<HistoryItem*> parent) { std::unique_ptr<Media> MediaLocation::clone(not_null<HistoryItem*> parent) {
return std::make_unique<MediaLocation>( return std::make_unique<MediaLocation>(
parent, parent,
_location->coords, _location->point,
_title, _title,
_description); _description);
} }
LocationData *MediaLocation::location() const { LocationThumbnail *MediaLocation::location() const {
return _location; return _location;
} }
@ -915,7 +915,7 @@ TextForMimeData MediaLocation::clipboardText() const {
if (!descriptionResult.text.isEmpty()) { if (!descriptionResult.text.isEmpty()) {
result.append(std::move(descriptionResult)); result.append(std::move(descriptionResult));
} }
result.append(LocationClickHandler(_location->coords).dragText()); result.append(LocationClickHandler(_location->point).dragText());
return result; return result;
} }

View File

@ -9,8 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class HistoryItem; class HistoryItem;
class HistoryMedia; class HistoryMedia;
class LocationCoords;
struct LocationData;
namespace base { namespace base {
template <typename Enum> template <typename Enum>
@ -29,6 +27,9 @@ class Element;
namespace Data { namespace Data {
class LocationPoint;
struct LocationThumbnail;
enum class CallFinishReason : char { enum class CallFinishReason : char {
Missed, Missed,
Busy, Busy,
@ -76,7 +77,7 @@ public:
virtual const Call *call() const; virtual const Call *call() const;
virtual GameData *game() const; virtual GameData *game() const;
virtual const Invoice *invoice() const; virtual const Invoice *invoice() const;
virtual LocationData *location() const; virtual LocationThumbnail *location() const;
virtual PollData *poll() const; virtual PollData *poll() const;
virtual bool uploading() const; virtual bool uploading() const;
@ -226,16 +227,16 @@ class MediaLocation : public Media {
public: public:
MediaLocation( MediaLocation(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
const LocationCoords &coords); const LocationPoint &point);
MediaLocation( MediaLocation(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
const LocationCoords &coords, const LocationPoint &point,
const QString &title, const QString &title,
const QString &description); const QString &description);
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override; std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
LocationData *location() const override; LocationThumbnail *location() const override;
QString chatListText() const override; QString chatListText() const override;
QString notificationText() const override; QString notificationText() const override;
QString pinnedTextSubstring() const override; QString pinnedTextSubstring() const override;
@ -248,7 +249,7 @@ public:
not_null<HistoryItem*> realParent) override; not_null<HistoryItem*> realParent) override;
private: private:
not_null<LocationData*> _location; not_null<LocationThumbnail*> _location;
QString _title; QString _title;
QString _description; QString _description;

View File

@ -2885,14 +2885,13 @@ void Session::applyUpdate(const MTPDupdateChatDefaultBannedRights &update) {
} }
} }
not_null<LocationData*> Session::location(const LocationCoords &coords) { not_null<LocationThumbnail*> Session::location(const LocationPoint &point) {
auto i = _locations.find(coords); const auto i = _locations.find(point);
if (i == _locations.cend()) { return (i != _locations.cend())
i = _locations.emplace( ? i->second.get()
coords, : _locations.emplace(
std::make_unique<LocationData>(coords)).first; point,
} std::make_unique<LocationThumbnail>(point)).first->second.get();
return i->second.get();
} }
void Session::registerPhotoItem( void Session::registerPhotoItem(

View File

@ -58,7 +58,7 @@ struct SavedCredentials;
namespace Data { namespace Data {
class Folder; class Folder;
class LocationPoint;
class WallPaper; class WallPaper;
class Session final { class Session final {
@ -521,8 +521,8 @@ public:
not_null<PollData*> processPoll(const MTPPoll &data); not_null<PollData*> processPoll(const MTPPoll &data);
not_null<PollData*> processPoll(const MTPDmessageMediaPoll &data); not_null<PollData*> processPoll(const MTPDmessageMediaPoll &data);
[[nodiscard]] not_null<LocationData*> location( [[nodiscard]] not_null<LocationThumbnail*> location(
const LocationCoords &coords); const LocationPoint &point);
void registerPhotoItem( void registerPhotoItem(
not_null<const PhotoData*> photo, not_null<const PhotoData*> photo,
@ -895,8 +895,8 @@ private:
not_null<const WebPageData*>, not_null<const WebPageData*>,
base::flat_set<not_null<ViewElement*>>> _webpageViews; base::flat_set<not_null<ViewElement*>>> _webpageViews;
std::unordered_map< std::unordered_map<
LocationCoords, LocationPoint,
std::unique_ptr<LocationData>> _locations; std::unique_ptr<LocationThumbnail>> _locations;
std::unordered_map< std::unordered_map<
PollId, PollId,
std::unique_ptr<PollData>> _polls; std::unique_ptr<PollData>> _polls;

View File

@ -667,7 +667,7 @@ void GenerateItems(
const auto address = qs(data.vaddress); const auto address = qs(data.vaddress);
const auto link = data.vgeo_point.match([&](const MTPDgeoPoint &data) { const auto link = data.vgeo_point.match([&](const MTPDgeoPoint &data) {
return textcmdLink( return textcmdLink(
LocationClickHandler::Url(LocationCoords(data)), LocationClickHandler::Url(Data::LocationPoint(data)),
address); address);
}, [&](const MTPDgeoPointEmpty &) { }, [&](const MTPDgeoPointEmpty &) {
return address; return address;

View File

@ -18,23 +18,6 @@ namespace {
constexpr auto kCoordPrecision = 8; constexpr auto kCoordPrecision = 8;
constexpr auto kMaxHttpRedirects = 5; constexpr auto kMaxHttpRedirects = 5;
GeoPointLocation ComputeLocation(const LocationCoords &coords) {
const auto scale = 1 + (cScale() * cIntRetinaFactor()) / 200;
const auto zoom = 13 + (scale - 1);
const auto w = st::locationSize.width() / scale;
const auto h = st::locationSize.height() / scale;
auto result = GeoPointLocation();
result.lat = coords.lat();
result.lon = coords.lon();
result.access = coords.accessHash();
result.width = w;
result.height = h;
result.zoom = zoom;
result.scale = scale;
return result;
}
} // namespace } // namespace
QString LocationClickHandler::copyToClipboardText() const { QString LocationClickHandler::copyToClipboardText() const {
@ -46,25 +29,16 @@ QString LocationClickHandler::copyToClipboardContextItemText() const {
} }
void LocationClickHandler::onClick(ClickContext context) const { void LocationClickHandler::onClick(ClickContext context) const {
if (!psLaunchMaps(_coords)) { if (!psLaunchMaps(_point)) {
QDesktopServices::openUrl(_text); QDesktopServices::openUrl(_text);
} }
} }
void LocationClickHandler::setup() { void LocationClickHandler::setup() {
_text = Url(_coords); _text = Url(_point);
} }
QString LocationClickHandler::Url(const LocationCoords &coords) { QString LocationClickHandler::Url(const Data::LocationPoint &point) {
const auto latlon = coords.latAsString() + ',' + coords.lonAsString(); const auto latlon = point.latAsString() + ',' + point.lonAsString();
return qsl("https://maps.google.com/maps?q=") + latlon + qsl("&ll=") + latlon + qsl("&z=16"); return qsl("https://maps.google.com/maps?q=") + latlon + qsl("&ll=") + latlon + qsl("&z=16");
} }
LocationData::LocationData(const LocationCoords &coords)
: coords(coords)
, thumb(Images::Create(ComputeLocation(coords))) {
}
void LocationData::load(Data::FileOrigin origin) {
thumb->load(origin);
}

View File

@ -7,98 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
class LocationCoords { #include "data/data_location.h"
public:
LocationCoords() = default;
explicit LocationCoords(const MTPDgeoPoint &point)
: _lat(point.vlat.v)
, _lon(point.vlong.v)
, _access(point.vaccess_hash.v) {
}
QString latAsString() const {
return asString(_lat);
}
QString lonAsString() const {
return asString(_lon);
}
MTPGeoPoint toMTP() const {
return MTP_geoPoint(
MTP_double(_lon),
MTP_double(_lat),
MTP_long(_access));
}
float64 lat() const {
return _lat;
}
float64 lon() const {
return _lon;
}
uint64 accessHash() const {
return _access;
}
inline size_t hash() const {
#ifndef OS_MAC_OLD
return QtPrivate::QHashCombine().operator()(
std::hash<float64>()(_lat),
_lon);
#else // OS_MAC_OLD
const auto h1 = std::hash<float64>()(_lat);
const auto h2 = std::hash<float64>()(_lon);
return ((h1 << 16) | (h1 >> 16)) ^ h2;
#endif // OS_MAC_OLD
}
private:
static QString asString(float64 value) {
constexpr auto kPrecision = 6;
return QString::number(value, 'f', kPrecision);
}
friend inline bool operator==(const LocationCoords &a, const LocationCoords &b) {
return (a._lat == b._lat) && (a._lon == b._lon);
}
friend inline bool operator<(const LocationCoords &a, const LocationCoords &b) {
return (a._lat < b._lat) || ((a._lat == b._lat) && (a._lon < b._lon));
}
float64 _lat = 0;
float64 _lon = 0;
uint64 _access = 0;
};
namespace std {
template <>
struct hash<LocationCoords> {
size_t operator()(const LocationCoords &value) const {
return value.hash();
}
};
} // namespace std
struct LocationData {
LocationData(const LocationCoords &coords);
LocationCoords coords;
ImagePtr thumb;
void load(Data::FileOrigin origin);
};
class LocationClickHandler : public ClickHandler { class LocationClickHandler : public ClickHandler {
public: public:
LocationClickHandler(const LocationCoords &coords) : _coords(coords) { LocationClickHandler(const Data::LocationPoint &point)
: _point(point) {
setup(); setup();
} }
static QString Url(const LocationCoords &coords); static QString Url(const Data::LocationPoint &coords);
void onClick(ClickContext context) const override; void onClick(ClickContext context) const override;
@ -116,7 +34,7 @@ public:
private: private:
void setup(); void setup();
LocationCoords _coords; Data::LocationPoint _point;
QString _text; QString _text;
}; };

View File

@ -789,7 +789,7 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
return media.vgeo.match([&](const MTPDgeoPoint &point) -> Result { return media.vgeo.match([&](const MTPDgeoPoint &point) -> Result {
return std::make_unique<Data::MediaLocation>( return std::make_unique<Data::MediaLocation>(
item, item,
LocationCoords(point)); Data::LocationPoint(point));
}, [](const MTPDgeoPointEmpty &) -> Result { }, [](const MTPDgeoPointEmpty &) -> Result {
return nullptr; return nullptr;
}); });
@ -797,7 +797,7 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
return media.vgeo.match([&](const MTPDgeoPoint &point) -> Result { return media.vgeo.match([&](const MTPDgeoPoint &point) -> Result {
return std::make_unique<Data::MediaLocation>( return std::make_unique<Data::MediaLocation>(
item, item,
LocationCoords(point)); Data::LocationPoint(point));
}, [](const MTPDgeoPointEmpty &) -> Result { }, [](const MTPDgeoPointEmpty &) -> Result {
return nullptr; return nullptr;
}); });
@ -805,7 +805,7 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
return media.vgeo.match([&](const MTPDgeoPoint &point) -> Result { return media.vgeo.match([&](const MTPDgeoPoint &point) -> Result {
return std::make_unique<Data::MediaLocation>( return std::make_unique<Data::MediaLocation>(
item, item,
LocationCoords(point), Data::LocationPoint(point),
qs(media.vtitle), qs(media.vtitle),
qs(media.vaddress)); qs(media.vaddress));
}, [](const MTPDgeoPointEmpty &data) -> Result { }, [](const MTPDgeoPointEmpty &data) -> Result {

View File

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_location.h"
#include "styles/style_history.h" #include "styles/style_history.h"
namespace { namespace {
@ -26,14 +27,14 @@ using TextState = HistoryView::TextState;
HistoryLocation::HistoryLocation( HistoryLocation::HistoryLocation(
not_null<Element*> parent, not_null<Element*> parent,
not_null<LocationData*> location, not_null<Data::LocationThumbnail*> location,
const QString &title, const QString &title,
const QString &description) const QString &description)
: HistoryMedia(parent) : HistoryMedia(parent)
, _data(location) , _data(location)
, _title(st::msgMinWidth) , _title(st::msgMinWidth)
, _description(st::msgMinWidth) , _description(st::msgMinWidth)
, _link(std::make_shared<LocationClickHandler>(_data->coords)) { , _link(std::make_shared<LocationClickHandler>(_data->point)) {
if (!title.isEmpty()) { if (!title.isEmpty()) {
_title.setText( _title.setText(
st::webPageTitleStyle, st::webPageTitleStyle,

View File

@ -9,14 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/media/history_media.h" #include "history/media/history_media.h"
class LocationCoords; namespace Data {
struct LocationData; struct LocationThumbnail;
} // namespace Data
class HistoryLocation : public HistoryMedia { class HistoryLocation : public HistoryMedia {
public: public:
HistoryLocation( HistoryLocation(
not_null<Element*> parent, not_null<Element*> parent,
not_null<LocationData*> location, not_null<Data::LocationThumbnail*> location,
const QString &title = QString(), const QString &title = QString(),
const QString &description = QString()); const QString &description = QString());
@ -58,7 +59,7 @@ private:
TextSelection toDescriptionSelection(TextSelection selection) const; TextSelection toDescriptionSelection(TextSelection selection) const;
TextSelection fromDescriptionSelection(TextSelection selection) const; TextSelection fromDescriptionSelection(TextSelection selection) const;
LocationData *_data; const not_null<Data::LocationThumbnail*> _data;
Ui::Text::String _title, _description; Ui::Text::String _title, _description;
ClickHandlerPtr _link; ClickHandlerPtr _link;

View File

@ -521,7 +521,7 @@ void CopyPostLink(FullMsgId itemId) {
const auto channel = item->history()->peer->asChannel(); const auto channel = item->history()->peer->asChannel();
Assert(channel != nullptr); Assert(channel != nullptr);
Ui::Toast::Show(channel->isPublic() Ui::Toast::Show(channel->hasUsername()
? tr::lng_channel_public_link_copied(tr::now) ? tr::lng_channel_public_link_copied(tr::now)
: tr::lng_context_about_private_link(tr::now)); : tr::lng_context_about_private_link(tr::now));
} }

View File

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper #include "ui/text/text_utilities.h" // Ui::Text::ToUpper
#include "history/history_location_manager.h" // LocationClickHandler.
#include "boxes/abstract_box.h" #include "boxes/abstract_box.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "boxes/peer_list_box.h" #include "boxes/peer_list_box.h"
@ -249,11 +250,11 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
result->setContextCopyText(contextCopyText); result->setContextCopyText(contextCopyText);
return result; return result;
}; };
if (auto user = _peer->asUser()) { if (const auto user = _peer->asUser()) {
if (Auth().supportMode()) { if (user->session().supportMode()) {
addInfoLineGeneric( addInfoLineGeneric(
Auth().supportHelper().infoLabelValue(user), user->session().supportHelper().infoLabelValue(user),
Auth().supportHelper().infoTextValue(user)); user->session().supportHelper().infoTextValue(user));
} }
addInfoOneLine( addInfoOneLine(
@ -273,26 +274,20 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
auto linkText = LinkValue( auto linkText = LinkValue(
_peer _peer
) | rpl::map([](const QString &link) { ) | rpl::map([](const QString &link) {
auto result = TextWithEntities{ link, {} }; return link.isEmpty()
if (!link.isEmpty()) { ? TextWithEntities()
auto remove = qstr("https://"); : Ui::Text::Link(
if (result.text.startsWith(remove)) { (link.startsWith(qstr("https://"))
result.text.remove(0, remove.size()); ? link.mid(qstr("https://").size())
} : link),
result.entities.push_back({ link);
EntityType::CustomUrl,
0,
result.text.size(),
link });
}
return result;
}); });
auto link = addInfoOneLine( auto link = addInfoOneLine(
tr::lng_info_link_label(), tr::lng_info_link_label(),
std::move(linkText), std::move(linkText),
QString()); QString());
link->setClickHandlerFilter([peer = _peer](auto&&...) { link->setClickHandlerFilter([peer = _peer](auto&&...) {
auto link = Core::App().createInternalLinkFull( const auto link = Core::App().createInternalLinkFull(
peer->userName()); peer->userName());
if (!link.isEmpty()) { if (!link.isEmpty()) {
QApplication::clipboard()->setText(link); QApplication::clipboard()->setText(link);
@ -300,6 +295,24 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
} }
return false; return false;
}); });
if (const auto channel = _peer->asChannel()) {
auto locationText = LocationValue(
channel
) | rpl::map([](const ChannelLocation *location) {
return location
? Ui::Text::Link(
location->address,
LocationClickHandler::Url(location->point))
: TextWithEntities();
});
addInfoOneLine(
tr::lng_info_location_label(),
std::move(locationText),
QString()
)->setLinksTrusted();
}
addInfoLine(tr::lng_info_about_label(), AboutValue(_peer)); addInfoLine(tr::lng_info_about_label(), AboutValue(_peer));
} }
if (!_peer->isSelf()) { if (!_peer->isSelf()) {

View File

@ -58,8 +58,8 @@ rpl::producer<TextWithEntities> BioValue(not_null<UserData*> user) {
auto PlainUsernameValue(not_null<PeerData*> peer) { auto PlainUsernameValue(not_null<PeerData*> peer) {
return Notify::PeerUpdateValue( return Notify::PeerUpdateValue(
peer, peer,
Notify::PeerUpdate::Flag::UsernameChanged Notify::PeerUpdate::Flag::UsernameChanged
) | rpl::map([peer] { ) | rpl::map([peer] {
return peer->userName(); return peer->userName();
}); });
@ -113,6 +113,16 @@ rpl::producer<QString> LinkValue(not_null<PeerData*> peer) {
}); });
} }
rpl::producer<const ChannelLocation*> LocationValue(
not_null<ChannelData*> channel) {
return Notify::PeerUpdateValue(
channel,
Notify::PeerUpdate::Flag::ChannelLocation
) | rpl::map([=] {
return channel->getLocation();
});
}
rpl::producer<bool> NotificationsEnabledValue(not_null<PeerData*> peer) { rpl::producer<bool> NotificationsEnabledValue(not_null<PeerData*> peer) {
return rpl::merge( return rpl::merge(
Notify::PeerUpdateValue( Notify::PeerUpdateValue(

View File

@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/map.h> #include <rpl/map.h>
#include "observer_peer.h" #include "observer_peer.h"
struct ChannelLocation;
namespace Ui { namespace Ui {
class RpWidget; class RpWidget;
template <typename Widget> template <typename Widget>
@ -36,6 +38,8 @@ rpl::producer<TextWithEntities> BioValue(not_null<UserData*> user);
rpl::producer<TextWithEntities> UsernameValue(not_null<UserData*> user); rpl::producer<TextWithEntities> UsernameValue(not_null<UserData*> user);
rpl::producer<TextWithEntities> AboutValue(not_null<PeerData*> peer); rpl::producer<TextWithEntities> AboutValue(not_null<PeerData*> peer);
rpl::producer<QString> LinkValue(not_null<PeerData*> peer); rpl::producer<QString> LinkValue(not_null<PeerData*> peer);
rpl::producer<const ChannelLocation*> LocationValue(
not_null<ChannelData*> channel);
rpl::producer<bool> NotificationsEnabledValue(not_null<PeerData*> peer); rpl::producer<bool> NotificationsEnabledValue(not_null<PeerData*> peer);
rpl::producer<bool> IsContactValue(not_null<UserData*> user); rpl::producer<bool> IsContactValue(not_null<UserData*> user);
rpl::producer<bool> CanInviteBotToGroupValue(not_null<UserData*> user); rpl::producer<bool> CanInviteBotToGroupValue(not_null<UserData*> user);

View File

@ -1025,9 +1025,10 @@ Article::Article(not_null<Context*> context, Result *result, bool withThumb) : I
, _withThumb(withThumb) , _withThumb(withThumb)
, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) , _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip)
, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) { , _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) {
LocationCoords location; if (!_link) {
if (!_link && result->getLocationCoords(&location)) { if (const auto point = result->getLocationPoint()) {
_link = std::make_shared<LocationClickHandler>(location); _link = std::make_shared<LocationClickHandler>(*point);
}
} }
_thumbLetter = getResultThumbLetter(); _thumbLetter = getResultThumbLetter();
} }

View File

@ -223,17 +223,16 @@ std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult
return nullptr; return nullptr;
} }
LocationCoords coords; if (const auto point = result->getLocationPoint()) {
if (result->getLocationCoords(&coords)) {
const auto scale = 1 + (cScale() * cIntRetinaFactor()) / 200; const auto scale = 1 + (cScale() * cIntRetinaFactor()) / 200;
const auto zoom = 15 + (scale - 1); const auto zoom = 15 + (scale - 1);
const auto w = st::inlineThumbSize / scale; const auto w = st::inlineThumbSize / scale;
const auto h = st::inlineThumbSize / scale; const auto h = st::inlineThumbSize / scale;
auto location = GeoPointLocation(); auto location = GeoPointLocation();
location.lat = coords.lat(); location.lat = point->lat();
location.lon = coords.lon(); location.lon = point->lon();
location.access = coords.accessHash(); location.access = point->accessHash();
location.width = w; location.width = w;
location.height = h; location.height = h;
location.zoom = zoom; location.zoom = zoom;
@ -329,8 +328,8 @@ QString Result::getErrorOnSend(History *history) const {
return sendData->getErrorOnSend(this, history); return sendData->getErrorOnSend(this, history);
} }
bool Result::getLocationCoords(LocationCoords *outLocation) const { std::optional<Data::LocationPoint> Result::getLocationPoint() const {
return sendData->getLocationCoords(outLocation); return sendData->getLocationPoint();
} }
QString Result::getLayoutTitle() const { QString Result::getLayoutTitle() const {

View File

@ -8,7 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
class FileLoader; class FileLoader;
class LocationCoords;
namespace Data {
class LocationPoint;
} // namespace Data
namespace InlineBots { namespace InlineBots {
@ -55,7 +58,7 @@ public:
QString getErrorOnSend(History *history) const; QString getErrorOnSend(History *history) const;
// interface for Layout:: usage // interface for Layout:: usage
bool getLocationCoords(LocationCoords *outLocation) const; std::optional<Data::LocationPoint> getLocationPoint() const;
QString getLayoutTitle() const; QString getLayoutTitle() const;
QString getLayoutDescription() const; QString getLayoutDescription() const;

View File

@ -45,8 +45,8 @@ public:
virtual bool hasLocationCoords() const { virtual bool hasLocationCoords() const {
return false; return false;
} }
virtual bool getLocationCoords(LocationCoords *outLocation) const { virtual std::optional<Data::LocationPoint> getLocationPoint() const {
return false; return std::nullopt;
} }
virtual QString getLayoutTitle(const Result *owner) const; virtual QString getLayoutTitle(const Result *owner) const;
virtual QString getLayoutDescription(const Result *owner) const; virtual QString getLayoutDescription(const Result *owner) const;
@ -121,14 +121,12 @@ public:
bool hasLocationCoords() const override { bool hasLocationCoords() const override {
return true; return true;
} }
bool getLocationCoords(LocationCoords *outLocation) const override { std::optional<Data::LocationPoint> getLocationPoint() const override {
Assert(outLocation != nullptr); return _location;
*outLocation = _location;
return true;
} }
private: private:
LocationCoords _location; Data::LocationPoint _location;
}; };
@ -153,14 +151,12 @@ public:
bool hasLocationCoords() const override { bool hasLocationCoords() const override {
return true; return true;
} }
bool getLocationCoords(LocationCoords *outLocation) const override { std::optional<Data::LocationPoint> getLocationPoint() const override {
Assert(outLocation != nullptr); return _location;
*outLocation = _location;
return true;
} }
private: private:
LocationCoords _location; Data::LocationPoint _location;
QString _venueId, _provider, _title, _address; QString _venueId, _provider, _title, _address;
}; };

View File

@ -66,6 +66,7 @@ struct PeerUpdate {
ChannelStickersChanged = (1 << 18), ChannelStickersChanged = (1 << 18),
ChannelPromotedChanged = (1 << 19), ChannelPromotedChanged = (1 << 19),
ChannelLinkedChat = (1 << 20), ChannelLinkedChat = (1 << 20),
ChannelLocation = (1 << 21),
}; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; } friend inline constexpr auto is_flag_type(Flag) { return true; }

View File

@ -478,6 +478,6 @@ bool linuxMoveFile(const char *from, const char *to) {
return true; return true;
} }
bool psLaunchMaps(const LocationCoords &coords) { bool psLaunchMaps(const Data::LocationPoint &point) {
return false; return false;
} }

View File

@ -10,7 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <execinfo.h> #include <execinfo.h>
#include <signal.h> #include <signal.h>
class LocationCoords; namespace Data {
class LocationPoint;
} // namespace Data
namespace Platform { namespace Platform {
@ -104,4 +106,4 @@ public:
bool linuxMoveFile(const char *from, const char *to); bool linuxMoveFile(const char *from, const char *to);
bool psLaunchMaps(const LocationCoords &coords); bool psLaunchMaps(const Data::LocationPoint &point);

View File

@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/mac/specific_mac_p.h" #include "platform/mac/specific_mac_p.h"
class LocationCoords; namespace Data {
class LocationPoint;
} // namespace Data
namespace Platform { namespace Platform {
@ -108,4 +110,4 @@ QString strStyleOfInterface();
QString strTitleWrapClass(); QString strTitleWrapClass();
QString strTitleClass(); QString strTitleClass();
bool psLaunchMaps(const LocationCoords &coords); bool psLaunchMaps(const Data::LocationPoint &point);

View File

@ -288,8 +288,8 @@ QByteArray psPathBookmark(const QString &path) {
return objc_pathBookmark(path); return objc_pathBookmark(path);
} }
bool psLaunchMaps(const LocationCoords &coords) { bool psLaunchMaps(const Data::LocationPoint &point) {
return QDesktopServices::openUrl(qsl("https://maps.apple.com/?q=Point&z=16&ll=%1,%2").arg(coords.latAsString()).arg(coords.lonAsString())); return QDesktopServices::openUrl(qsl("https://maps.apple.com/?q=Point&z=16&ll=%1,%2").arg(point.latAsString()).arg(point.lonAsString()));
} }
QString strNotificationAboutThemeChange() { QString strNotificationAboutThemeChange() {

View File

@ -583,6 +583,6 @@ void psWriteDump() {
#endif // TDESKTOP_DISABLE_CRASH_REPORTS #endif // TDESKTOP_DISABLE_CRASH_REPORTS
} }
bool psLaunchMaps(const LocationCoords &coords) { bool psLaunchMaps(const Data::LocationPoint &point) {
return QDesktopServices::openUrl(qsl("bingmaps:?lvl=16&collection=point.%1_%2_Point").arg(coords.latAsString()).arg(coords.lonAsString())); return QDesktopServices::openUrl(qsl("bingmaps:?lvl=16&collection=point.%1_%2_Point").arg(point.latAsString()).arg(point.lonAsString()));
} }

View File

@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/win/wrapper_windows_h.h" #include "platform/win/wrapper_windows_h.h"
class LocationCoords; namespace Data {
class LocationPoint;
} // namespace Data
namespace Platform { namespace Platform {
@ -107,4 +109,4 @@ public:
}; };
bool psLaunchMaps(const LocationCoords &coords); bool psLaunchMaps(const Data::LocationPoint &point);

View File

@ -506,7 +506,7 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) {
[channel] { Auth().api().joinChannel(channel); }); [channel] { Auth().api().joinChannel(channel); });
} }
if (_source != PeerMenuSource::ChatsList) { if (_source != PeerMenuSource::ChatsList) {
auto needReport = !channel->amCreator() const auto needReport = !channel->amCreator()
&& (!isGroup || channel->isPublic()); && (!isGroup || channel->isPublic());
if (needReport) { if (needReport) {
_addAction(tr::lng_profile_report(tr::now), [channel] { _addAction(tr::lng_profile_report(tr::now), [channel] {

View File

@ -190,6 +190,8 @@
<(src_loc)/data/data_game.h <(src_loc)/data/data_game.h
<(src_loc)/data/data_groups.cpp <(src_loc)/data/data_groups.cpp
<(src_loc)/data/data_groups.h <(src_loc)/data/data_groups.h
<(src_loc)/data/data_location.cpp
<(src_loc)/data/data_location.h
<(src_loc)/data/data_media_types.cpp <(src_loc)/data/data_media_types.cpp
<(src_loc)/data/data_media_types.h <(src_loc)/data/data_media_types.h
<(src_loc)/data/data_messages.cpp <(src_loc)/data/data_messages.cpp