mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-04-01 23:00:58 +00:00
Handle located groups as public.
This commit is contained in:
parent
6537e524b8
commit
7d585ab72f
Telegram
Resources/langs
SourceFiles
apiwrap.cpp
boxes/peers
data
data_channel.cppdata_channel.hdata_location.cppdata_location.hdata_media_types.cppdata_media_types.hdata_session.cppdata_session.h
history
info/profile
inline_bots
observer_peer.hplatform
linux
mac
win
window
gyp
@ -780,6 +780,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_info_username_label" = "Username";
|
||||
"lng_info_bio_label" = "Bio";
|
||||
"lng_info_link_label" = "Link";
|
||||
"lng_info_location_label" = "Location";
|
||||
"lng_info_about_label" = "About";
|
||||
"lng_info_user_title" = "User 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_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_public_group_title" = "Public";
|
||||
"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_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_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_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" = "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_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_join" = "Join";
|
||||
|
@ -691,11 +691,11 @@ QString ApiWrap::exportDirectMessageLink(not_null<HistoryItem*> item) {
|
||||
const auto itemId = item->fullId();
|
||||
const auto channel = item->history()->peer->asChannel();
|
||||
const auto fallback = [&] {
|
||||
const auto base = channel->isPublic()
|
||||
const auto base = channel->hasUsername()
|
||||
? channel->username
|
||||
: "c/" + QString::number(channel->bareId());
|
||||
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 document = media->document()) {
|
||||
if (document->isVideoMessage()) {
|
||||
|
@ -537,6 +537,8 @@ void EditAdminBox::sendTransferRequestFrom(
|
||||
const auto problem = [&] {
|
||||
if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
|
||||
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")) {
|
||||
return (channel->isBroadcast()
|
||||
? tr::lng_error_admin_limit_channel
|
||||
|
@ -240,6 +240,7 @@ private:
|
||||
std::optional<Privacy> _privacySavedValue;
|
||||
std::optional<ChannelData*> _linkedChatSavedValue;
|
||||
ChannelData *_linkedChatOriginalValue = nullptr;
|
||||
bool _channelHasLocationOriginalValue = false;
|
||||
std::optional<HistoryVisibility> _historyVisibilitySavedValue;
|
||||
std::optional<QString> _usernameSavedValue;
|
||||
std::optional<bool> _signaturesSavedValue;
|
||||
@ -489,7 +490,8 @@ void Controller::refreshHistoryVisibility(anim::type animated) {
|
||||
return;
|
||||
}
|
||||
_controls.historyVisibilityWrap->toggle(
|
||||
(_privacySavedValue != Privacy::Public
|
||||
(_privacySavedValue != Privacy::HasUsername
|
||||
&& !_channelHasLocationOriginalValue
|
||||
&& (!_linkedChatSavedValue || !*_linkedChatSavedValue)),
|
||||
animated);
|
||||
};
|
||||
@ -506,6 +508,7 @@ void Controller::showEditPeerTypeBox(
|
||||
Ui::show(
|
||||
Box<EditPeerTypeBox>(
|
||||
_peer,
|
||||
_channelHasLocationOriginalValue,
|
||||
boxCallback,
|
||||
_privacySavedValue,
|
||||
_usernameSavedValue,
|
||||
@ -569,26 +572,34 @@ void Controller::fillPrivacyTypeButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
// Create Privacy Button.
|
||||
const auto hasLocation = _peer->isChannel()
|
||||
&& _peer->asChannel()->hasLocation();
|
||||
_privacySavedValue = (_peer->isChannel()
|
||||
&& _peer->asChannel()->isPublic())
|
||||
? Privacy::Public
|
||||
: Privacy::Private;
|
||||
&& _peer->asChannel()->hasUsername())
|
||||
? Privacy::HasUsername
|
||||
: Privacy::NoUsername;
|
||||
|
||||
const auto isGroup = (_peer->isChat() || _peer->isMegagroup());
|
||||
AddButtonWithText(
|
||||
_controls.buttonsLayout,
|
||||
(isGroup
|
||||
(hasLocation
|
||||
? tr::lng_manage_peer_link_type
|
||||
: isGroup
|
||||
? tr::lng_manage_peer_group_type
|
||||
: tr::lng_manage_peer_channel_type)(),
|
||||
_privacyTypeUpdates.events(
|
||||
) | rpl::map([=](Privacy flag) {
|
||||
return (Privacy::Public == flag
|
||||
? (isGroup
|
||||
return (flag == Privacy::HasUsername)
|
||||
? (hasLocation
|
||||
? tr::lng_manage_peer_link_permanent
|
||||
: isGroup
|
||||
? tr::lng_manage_public_group_title
|
||||
: tr::lng_manage_public_peer_title)
|
||||
: (isGroup
|
||||
: tr::lng_manage_public_peer_title)()
|
||||
: (hasLocation
|
||||
? tr::lng_manage_peer_link_invite
|
||||
: isGroup
|
||||
? tr::lng_manage_private_group_title
|
||||
: tr::lng_manage_private_peer_title))();
|
||||
: tr::lng_manage_private_peer_title)();
|
||||
}) | rpl::flatten_latest(),
|
||||
[=] { showEditPeerTypeBox(); });
|
||||
|
||||
@ -640,9 +651,7 @@ void Controller::fillInviteLinkButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
const auto buttonCallback = [=] {
|
||||
Ui::show(
|
||||
Box<EditPeerTypeBox>(_peer),
|
||||
LayerOption::KeepOther);
|
||||
Ui::show(Box<EditPeerTypeBox>(_peer), LayerOption::KeepOther);
|
||||
};
|
||||
|
||||
AddButtonWithText(
|
||||
@ -685,6 +694,7 @@ void Controller::fillHistoryVisibilityButton() {
|
||||
_historyVisibilitySavedValue = (!channel || channel->hiddenPreHistory())
|
||||
? HistoryVisibility::Hidden
|
||||
: HistoryVisibility::Visible;
|
||||
_channelHasLocationOriginalValue = channel && channel->hasLocation();
|
||||
|
||||
const auto updateHistoryVisibility =
|
||||
std::make_shared<rpl::event_stream<HistoryVisibility>>();
|
||||
@ -945,7 +955,7 @@ std::optional<Controller::Saving> Controller::validate() const {
|
||||
bool Controller::validateUsername(Saving &to) const {
|
||||
if (!_privacySavedValue) {
|
||||
return true;
|
||||
} else if (_privacySavedValue != Privacy::Public) {
|
||||
} else if (_privacySavedValue != Privacy::HasUsername) {
|
||||
to.username = QString();
|
||||
return true;
|
||||
}
|
||||
@ -994,7 +1004,8 @@ bool Controller::validateDescription(Saving &to) const {
|
||||
bool Controller::validateHistoryVisibility(Saving &to) const {
|
||||
if (!_controls.historyVisibilityWrap
|
||||
|| !_controls.historyVisibilityWrap->toggled()
|
||||
|| (_privacySavedValue == Privacy::Public)) {
|
||||
|| _channelHasLocationOriginalValue
|
||||
|| (_privacySavedValue == Privacy::HasUsername)) {
|
||||
return true;
|
||||
}
|
||||
to.hiddenPreHistory
|
||||
|
@ -51,6 +51,7 @@ public:
|
||||
Controller(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<Privacy> privacySavedValue,
|
||||
std::optional<QString> usernameSavedValue);
|
||||
|
||||
@ -129,10 +130,8 @@ private:
|
||||
void addRoundButton(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Privacy value,
|
||||
const QString &groupText,
|
||||
const QString &channelText,
|
||||
rpl::producer<QString> groupAbout,
|
||||
rpl::producer<QString> channelAbout);
|
||||
const QString &text,
|
||||
rpl::producer<QString> about);
|
||||
|
||||
bool inviteLinkShown();
|
||||
QString inviteLinkText();
|
||||
@ -141,6 +140,7 @@ private:
|
||||
std::optional<Privacy> _privacySavedValue;
|
||||
std::optional<QString> _usernameSavedValue;
|
||||
|
||||
bool _useLocationPhrases = false;
|
||||
bool _isGroup = false;
|
||||
bool _isInviteLink = false;
|
||||
bool _isAllowSave = false;
|
||||
@ -158,11 +158,13 @@ private:
|
||||
Controller::Controller(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<Privacy> privacySavedValue,
|
||||
std::optional<QString> usernameSavedValue)
|
||||
: _peer(peer)
|
||||
, _privacySavedValue(privacySavedValue)
|
||||
, _usernameSavedValue(usernameSavedValue)
|
||||
, _useLocationPhrases(useLocationPhrases)
|
||||
, _isGroup(_peer->isChat() || _peer->isMegagroup())
|
||||
, _isInviteLink(!_privacySavedValue.has_value()
|
||||
&& !_usernameSavedValue.has_value())
|
||||
@ -189,7 +191,7 @@ void Controller::createContent() {
|
||||
_wrap->add(createInviteLinkEdit());
|
||||
_wrap->add(createUsernameEdit());
|
||||
|
||||
if (_controls.privacy->value() == Privacy::Private) {
|
||||
if (_controls.privacy->value() == Privacy::NoUsername) {
|
||||
checkUsernameAvailability();
|
||||
}
|
||||
}
|
||||
@ -197,21 +199,19 @@ void Controller::createContent() {
|
||||
void Controller::addRoundButton(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Privacy value,
|
||||
const QString &groupText,
|
||||
const QString &channelText,
|
||||
rpl::producer<QString> groupAbout,
|
||||
rpl::producer<QString> channelAbout) {
|
||||
const QString &text,
|
||||
rpl::producer<QString> about) {
|
||||
container->add(object_ptr<Ui::Radioenum<Privacy>>(
|
||||
container,
|
||||
_controls.privacy,
|
||||
value,
|
||||
(_isGroup ? groupText : channelText),
|
||||
text,
|
||||
st::editPeerPrivacyBoxCheckbox));
|
||||
container->add(object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||
container,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
std::move(_isGroup ? groupAbout : channelAbout),
|
||||
std::move(about),
|
||||
st::editPeerPrivacyLabel),
|
||||
st::editPeerPrivacyLabelMargins));
|
||||
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||
@ -242,24 +242,35 @@ void Controller::fillPrivaciesButtons(
|
||||
const auto container = result->entity();
|
||||
|
||||
const auto isPublic = _peer->isChannel()
|
||||
&& _peer->asChannel()->isPublic();
|
||||
&& _peer->asChannel()->hasUsername();
|
||||
_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(
|
||||
container,
|
||||
Privacy::Public,
|
||||
tr::lng_create_public_group_title(tr::now),
|
||||
tr::lng_create_public_channel_title(tr::now),
|
||||
tr::lng_create_public_group_about(),
|
||||
tr::lng_create_public_channel_about());
|
||||
Privacy::HasUsername,
|
||||
(_useLocationPhrases
|
||||
? tr::lng_create_permanent_link_title
|
||||
: _isGroup
|
||||
? 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(
|
||||
container,
|
||||
Privacy::Private,
|
||||
tr::lng_create_private_group_title(tr::now),
|
||||
tr::lng_create_private_channel_title(tr::now),
|
||||
tr::lng_create_private_group_about(),
|
||||
tr::lng_create_private_channel_about());
|
||||
Privacy::NoUsername,
|
||||
(_useLocationPhrases
|
||||
? tr::lng_create_invite_link_title
|
||||
: _isGroup
|
||||
? 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) {
|
||||
privacyChanged(value);
|
||||
@ -343,7 +354,7 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
|
||||
&Ui::UsernameInput::changed,
|
||||
[this] { usernameChanged(); });
|
||||
|
||||
const auto shown = (_controls.privacy->value() == Privacy::Public);
|
||||
const auto shown = (_controls.privacy->value() == Privacy::HasUsername);
|
||||
result->toggle(shown, anim::type::instant);
|
||||
|
||||
return std::move(result);
|
||||
@ -352,14 +363,14 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
|
||||
void Controller::privacyChanged(Privacy value) {
|
||||
const auto toggleEditUsername = [&] {
|
||||
_controls.usernameWrap->toggle(
|
||||
(value == Privacy::Public),
|
||||
(value == Privacy::HasUsername),
|
||||
anim::type::instant);
|
||||
};
|
||||
const auto refreshVisibilities = [&] {
|
||||
// Now first we need to hide that was shown.
|
||||
// Otherwise box will change own Y position.
|
||||
|
||||
if (value == Privacy::Public) {
|
||||
if (value == Privacy::HasUsername) {
|
||||
refreshCreateInviteLink();
|
||||
refreshEditInviteLink();
|
||||
toggleEditUsername();
|
||||
@ -372,12 +383,12 @@ void Controller::privacyChanged(Privacy value) {
|
||||
refreshEditInviteLink();
|
||||
}
|
||||
};
|
||||
if (value == Privacy::Public) {
|
||||
if (value == Privacy::HasUsername) {
|
||||
if (_usernameState == UsernameState::TooMany) {
|
||||
askUsernameRevoke();
|
||||
return;
|
||||
} else if (_usernameState == UsernameState::NotAvailable) {
|
||||
_controls.privacy->setValue(Privacy::Private);
|
||||
_controls.privacy->setValue(Privacy::NoUsername);
|
||||
return;
|
||||
}
|
||||
refreshVisibilities();
|
||||
@ -394,7 +405,7 @@ void Controller::checkUsernameAvailability() {
|
||||
if (!_controls.usernameInput) {
|
||||
return;
|
||||
}
|
||||
const auto initial = (_controls.privacy->value() != Privacy::Public);
|
||||
const auto initial = (_controls.privacy->value() != Privacy::HasUsername);
|
||||
const auto checking = initial
|
||||
? qsl(".bad.")
|
||||
: getUsernameInput();
|
||||
@ -425,14 +436,14 @@ void Controller::checkUsernameAvailability() {
|
||||
_usernameState = UsernameState::Normal;
|
||||
if (type == qstr("CHANNEL_PUBLIC_GROUP_NA")) {
|
||||
_usernameState = UsernameState::NotAvailable;
|
||||
_controls.privacy->setValue(Privacy::Private);
|
||||
_controls.privacy->setValue(Privacy::NoUsername);
|
||||
} else if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
|
||||
_usernameState = UsernameState::TooMany;
|
||||
if (_controls.privacy->value() == Privacy::Public) {
|
||||
if (_controls.privacy->value() == Privacy::HasUsername) {
|
||||
askUsernameRevoke();
|
||||
}
|
||||
} else if (initial) {
|
||||
if (_controls.privacy->value() == Privacy::Public) {
|
||||
if (_controls.privacy->value() == Privacy::HasUsername) {
|
||||
_controls.usernameResult = nullptr;
|
||||
setFocusUsername();
|
||||
}
|
||||
@ -446,10 +457,10 @@ void Controller::checkUsernameAvailability() {
|
||||
}
|
||||
|
||||
void Controller::askUsernameRevoke() {
|
||||
_controls.privacy->setValue(Privacy::Private);
|
||||
_controls.privacy->setValue(Privacy::NoUsername);
|
||||
const auto revokeCallback = crl::guard(this, [this] {
|
||||
_usernameState = UsernameState::Normal;
|
||||
_controls.privacy->setValue(Privacy::Public);
|
||||
_controls.privacy->setValue(Privacy::HasUsername);
|
||||
checkUsernameAvailability();
|
||||
});
|
||||
Ui::show(
|
||||
@ -683,20 +694,26 @@ void Controller::refreshCreateInviteLink() {
|
||||
|
||||
bool Controller::inviteLinkShown() {
|
||||
return !_controls.privacy
|
||||
|| (_controls.privacy->value() == Privacy::Private)
|
||||
|| (_controls.privacy->value() == Privacy::NoUsername)
|
||||
|| _isInviteLink;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EditPeerTypeBox::EditPeerTypeBox(QWidget*, not_null<PeerData*> peer)
|
||||
: EditPeerTypeBox(nullptr, peer, false, {}, {}, {}, {}) {
|
||||
}
|
||||
|
||||
EditPeerTypeBox::EditPeerTypeBox(
|
||||
QWidget*,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<FnMut<void(Privacy, QString)>> savedCallback,
|
||||
std::optional<Privacy> privacySaved,
|
||||
std::optional<QString> usernameSaved,
|
||||
std::optional<rpl::producer<QString>> usernameError)
|
||||
: _peer(peer)
|
||||
, _useLocationPhrases(useLocationPhrases)
|
||||
, _savedCallback(std::move(savedCallback))
|
||||
, _privacySavedValue(privacySaved)
|
||||
, _usernameSavedValue(usernameSaved)
|
||||
@ -716,6 +733,7 @@ void EditPeerTypeBox::prepare() {
|
||||
this,
|
||||
content,
|
||||
_peer,
|
||||
_useLocationPhrases,
|
||||
_privacySavedValue,
|
||||
_usernameSavedValue);
|
||||
_focusRequests.events(
|
||||
@ -735,14 +753,14 @@ void EditPeerTypeBox::prepare() {
|
||||
if (!controller->isInviteLink() && _savedCallback.has_value()) {
|
||||
addButton(tr::lng_settings_save(), [=] {
|
||||
const auto v = controller->getPrivacy();
|
||||
if (!controller->isAllowSave() && (v == Privacy::Public)) {
|
||||
if (!controller->isAllowSave() && (v == Privacy::HasUsername)) {
|
||||
controller->setFocusUsername();
|
||||
return;
|
||||
}
|
||||
|
||||
auto local = std::move(*_savedCallback);
|
||||
local(v,
|
||||
(v == Privacy::Public)
|
||||
(v == Privacy::HasUsername)
|
||||
? controller->getUsernameInput()
|
||||
: QString()); // We dont need username with private type.
|
||||
closeBox();
|
||||
|
@ -25,8 +25,8 @@ class Button;
|
||||
} // namespace Info
|
||||
|
||||
enum class Privacy {
|
||||
Public,
|
||||
Private,
|
||||
HasUsername,
|
||||
NoUsername,
|
||||
};
|
||||
|
||||
enum class UsernameState {
|
||||
@ -37,12 +37,16 @@ enum class UsernameState {
|
||||
|
||||
class EditPeerTypeBox : public BoxContent {
|
||||
public:
|
||||
// Edit just the invite link.
|
||||
EditPeerTypeBox(QWidget*, not_null<PeerData*> peer);
|
||||
|
||||
EditPeerTypeBox(
|
||||
QWidget*,
|
||||
not_null<PeerData*> p,
|
||||
std::optional<FnMut<void(Privacy, QString)>> savedCallback = {},
|
||||
std::optional<Privacy> privacySaved = {},
|
||||
std::optional<QString> usernameSaved = {},
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<FnMut<void(Privacy, QString)>> savedCallback,
|
||||
std::optional<Privacy> privacySaved,
|
||||
std::optional<QString> usernameSaved,
|
||||
std::optional<rpl::producer<QString>> usernameError = {});
|
||||
|
||||
protected:
|
||||
@ -51,6 +55,7 @@ protected:
|
||||
|
||||
private:
|
||||
not_null<PeerData*> _peer;
|
||||
bool _useLocationPhrases = false;
|
||||
std::optional<FnMut<void(Privacy, QString)>> _savedCallback;
|
||||
|
||||
std::optional<Privacy> _privacySavedValue;
|
||||
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_location.h"
|
||||
#include "history/history.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
@ -32,6 +33,14 @@ void MegagroupInfo::setMigrateFromChat(ChatData *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)
|
||||
: PeerData(owner, id)
|
||||
, inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))) {
|
||||
@ -92,6 +101,37 @@ bool ChannelData::canHaveInviteLink() const {
|
||||
|| 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) {
|
||||
if (_linkedChat != linked) {
|
||||
_linkedChat = linked;
|
||||
@ -600,11 +640,14 @@ void ApplyChannelUpdate(
|
||||
? update.vkicked_count.v
|
||||
: 0);
|
||||
channel->setInviteLink(update.vexported_invite.match([&](
|
||||
const MTPDchatInviteExported & data) {
|
||||
const MTPDchatInviteExported &data) {
|
||||
return qs(data.vlink);
|
||||
}, [&](const MTPDchatInviteEmpty &) {
|
||||
return QString();
|
||||
}));
|
||||
channel->setLocation(update.has_location()
|
||||
? update.vlocation
|
||||
: MTPChannelLocation(MTP_channelLocationEmpty()));
|
||||
channel->setLinkedChat(update.has_linked_chat_id()
|
||||
? channel->owner().channelLoaded(update.vlinked_chat_id.v)
|
||||
: nullptr);
|
||||
|
@ -9,6 +9,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "data/data_peer.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 {
|
||||
public:
|
||||
@ -34,6 +53,9 @@ public:
|
||||
ChatData *getMigrateFromChat() const;
|
||||
void setMigrateFromChat(ChatData *chat);
|
||||
|
||||
const ChannelLocation *getLocation() const;
|
||||
void setLocation(const ChannelLocation &location);
|
||||
|
||||
std::deque<not_null<UserData*>> lastParticipants;
|
||||
base::flat_map<not_null<UserData*>, Admin> lastAdmins;
|
||||
base::flat_map<not_null<UserData*>, Restricted> lastRestricted;
|
||||
@ -57,6 +79,7 @@ public:
|
||||
|
||||
private:
|
||||
ChatData *_migratedFrom = nullptr;
|
||||
ChannelLocation _location;
|
||||
|
||||
};
|
||||
|
||||
@ -79,7 +102,8 @@ public:
|
||||
static constexpr auto kEssentialFullFlags = 0
|
||||
| MTPDchannelFull::Flag::f_can_view_participants
|
||||
| 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<
|
||||
MTPDchannelFull::Flags,
|
||||
kEssentialFullFlags>;
|
||||
@ -206,9 +230,15 @@ public:
|
||||
bool isBroadcast() const {
|
||||
return flags() & MTPDchannel::Flag::f_broadcast;
|
||||
}
|
||||
bool isPublic() const {
|
||||
bool hasUsername() const {
|
||||
return flags() & MTPDchannel::Flag::f_username;
|
||||
}
|
||||
bool hasLocation() const {
|
||||
return fullFlags() & MTPDchannelFull::Flag::f_location;
|
||||
}
|
||||
bool isPublic() const {
|
||||
return hasUsername() || hasLocation();
|
||||
}
|
||||
bool amCreator() const {
|
||||
return flags() & MTPDchannel::Flag::f_creator;
|
||||
}
|
||||
@ -279,6 +309,9 @@ public:
|
||||
QString inviteLink() const;
|
||||
bool canHaveInviteLink() const;
|
||||
|
||||
void setLocation(const MTPChannelLocation &data);
|
||||
const ChannelLocation *getLocation() const;
|
||||
|
||||
void setLinkedChat(ChannelData *linked);
|
||||
ChannelData *linkedChat() const;
|
||||
|
||||
|
44
Telegram/SourceFiles/data/data_location.cpp
Normal file
44
Telegram/SourceFiles/data/data_location.cpp
Normal 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
|
99
Telegram/SourceFiles/data/data_location.h
Normal file
99
Telegram/SourceFiles/data/data_location.h
Normal 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
|
@ -168,7 +168,7 @@ const Invoice *Media::invoice() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LocationData *Media::location() const {
|
||||
LocationThumbnail *Media::location() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -861,17 +861,17 @@ std::unique_ptr<HistoryMedia> MediaContact::createView(
|
||||
|
||||
MediaLocation::MediaLocation(
|
||||
not_null<HistoryItem*> parent,
|
||||
const LocationCoords &coords)
|
||||
: MediaLocation(parent, coords, QString(), QString()) {
|
||||
const LocationPoint &point)
|
||||
: MediaLocation(parent, point, QString(), QString()) {
|
||||
}
|
||||
|
||||
MediaLocation::MediaLocation(
|
||||
not_null<HistoryItem*> parent,
|
||||
const LocationCoords &coords,
|
||||
const LocationPoint &point,
|
||||
const QString &title,
|
||||
const QString &description)
|
||||
: Media(parent)
|
||||
, _location(parent->history()->owner().location(coords))
|
||||
, _location(parent->history()->owner().location(point))
|
||||
, _title(title)
|
||||
, _description(description) {
|
||||
}
|
||||
@ -879,12 +879,12 @@ MediaLocation::MediaLocation(
|
||||
std::unique_ptr<Media> MediaLocation::clone(not_null<HistoryItem*> parent) {
|
||||
return std::make_unique<MediaLocation>(
|
||||
parent,
|
||||
_location->coords,
|
||||
_location->point,
|
||||
_title,
|
||||
_description);
|
||||
}
|
||||
|
||||
LocationData *MediaLocation::location() const {
|
||||
LocationThumbnail *MediaLocation::location() const {
|
||||
return _location;
|
||||
}
|
||||
|
||||
@ -915,7 +915,7 @@ TextForMimeData MediaLocation::clipboardText() const {
|
||||
if (!descriptionResult.text.isEmpty()) {
|
||||
result.append(std::move(descriptionResult));
|
||||
}
|
||||
result.append(LocationClickHandler(_location->coords).dragText());
|
||||
result.append(LocationClickHandler(_location->point).dragText());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class HistoryItem;
|
||||
class HistoryMedia;
|
||||
class LocationCoords;
|
||||
struct LocationData;
|
||||
|
||||
namespace base {
|
||||
template <typename Enum>
|
||||
@ -29,6 +27,9 @@ class Element;
|
||||
|
||||
namespace Data {
|
||||
|
||||
class LocationPoint;
|
||||
struct LocationThumbnail;
|
||||
|
||||
enum class CallFinishReason : char {
|
||||
Missed,
|
||||
Busy,
|
||||
@ -76,7 +77,7 @@ public:
|
||||
virtual const Call *call() const;
|
||||
virtual GameData *game() const;
|
||||
virtual const Invoice *invoice() const;
|
||||
virtual LocationData *location() const;
|
||||
virtual LocationThumbnail *location() const;
|
||||
virtual PollData *poll() const;
|
||||
|
||||
virtual bool uploading() const;
|
||||
@ -226,16 +227,16 @@ class MediaLocation : public Media {
|
||||
public:
|
||||
MediaLocation(
|
||||
not_null<HistoryItem*> parent,
|
||||
const LocationCoords &coords);
|
||||
const LocationPoint &point);
|
||||
MediaLocation(
|
||||
not_null<HistoryItem*> parent,
|
||||
const LocationCoords &coords,
|
||||
const LocationPoint &point,
|
||||
const QString &title,
|
||||
const QString &description);
|
||||
|
||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||
|
||||
LocationData *location() const override;
|
||||
LocationThumbnail *location() const override;
|
||||
QString chatListText() const override;
|
||||
QString notificationText() const override;
|
||||
QString pinnedTextSubstring() const override;
|
||||
@ -248,7 +249,7 @@ public:
|
||||
not_null<HistoryItem*> realParent) override;
|
||||
|
||||
private:
|
||||
not_null<LocationData*> _location;
|
||||
not_null<LocationThumbnail*> _location;
|
||||
QString _title;
|
||||
QString _description;
|
||||
|
||||
|
@ -2885,14 +2885,13 @@ void Session::applyUpdate(const MTPDupdateChatDefaultBannedRights &update) {
|
||||
}
|
||||
}
|
||||
|
||||
not_null<LocationData*> Session::location(const LocationCoords &coords) {
|
||||
auto i = _locations.find(coords);
|
||||
if (i == _locations.cend()) {
|
||||
i = _locations.emplace(
|
||||
coords,
|
||||
std::make_unique<LocationData>(coords)).first;
|
||||
}
|
||||
return i->second.get();
|
||||
not_null<LocationThumbnail*> Session::location(const LocationPoint &point) {
|
||||
const auto i = _locations.find(point);
|
||||
return (i != _locations.cend())
|
||||
? i->second.get()
|
||||
: _locations.emplace(
|
||||
point,
|
||||
std::make_unique<LocationThumbnail>(point)).first->second.get();
|
||||
}
|
||||
|
||||
void Session::registerPhotoItem(
|
||||
|
@ -58,7 +58,7 @@ struct SavedCredentials;
|
||||
namespace Data {
|
||||
|
||||
class Folder;
|
||||
|
||||
class LocationPoint;
|
||||
class WallPaper;
|
||||
|
||||
class Session final {
|
||||
@ -521,8 +521,8 @@ public:
|
||||
not_null<PollData*> processPoll(const MTPPoll &data);
|
||||
not_null<PollData*> processPoll(const MTPDmessageMediaPoll &data);
|
||||
|
||||
[[nodiscard]] not_null<LocationData*> location(
|
||||
const LocationCoords &coords);
|
||||
[[nodiscard]] not_null<LocationThumbnail*> location(
|
||||
const LocationPoint &point);
|
||||
|
||||
void registerPhotoItem(
|
||||
not_null<const PhotoData*> photo,
|
||||
@ -895,8 +895,8 @@ private:
|
||||
not_null<const WebPageData*>,
|
||||
base::flat_set<not_null<ViewElement*>>> _webpageViews;
|
||||
std::unordered_map<
|
||||
LocationCoords,
|
||||
std::unique_ptr<LocationData>> _locations;
|
||||
LocationPoint,
|
||||
std::unique_ptr<LocationThumbnail>> _locations;
|
||||
std::unordered_map<
|
||||
PollId,
|
||||
std::unique_ptr<PollData>> _polls;
|
||||
|
@ -667,7 +667,7 @@ void GenerateItems(
|
||||
const auto address = qs(data.vaddress);
|
||||
const auto link = data.vgeo_point.match([&](const MTPDgeoPoint &data) {
|
||||
return textcmdLink(
|
||||
LocationClickHandler::Url(LocationCoords(data)),
|
||||
LocationClickHandler::Url(Data::LocationPoint(data)),
|
||||
address);
|
||||
}, [&](const MTPDgeoPointEmpty &) {
|
||||
return address;
|
||||
|
@ -18,23 +18,6 @@ namespace {
|
||||
constexpr auto kCoordPrecision = 8;
|
||||
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
|
||||
|
||||
QString LocationClickHandler::copyToClipboardText() const {
|
||||
@ -46,25 +29,16 @@ QString LocationClickHandler::copyToClipboardContextItemText() const {
|
||||
}
|
||||
|
||||
void LocationClickHandler::onClick(ClickContext context) const {
|
||||
if (!psLaunchMaps(_coords)) {
|
||||
if (!psLaunchMaps(_point)) {
|
||||
QDesktopServices::openUrl(_text);
|
||||
}
|
||||
}
|
||||
|
||||
void LocationClickHandler::setup() {
|
||||
_text = Url(_coords);
|
||||
_text = Url(_point);
|
||||
}
|
||||
|
||||
QString LocationClickHandler::Url(const LocationCoords &coords) {
|
||||
const auto latlon = coords.latAsString() + ',' + coords.lonAsString();
|
||||
QString LocationClickHandler::Url(const Data::LocationPoint &point) {
|
||||
const auto latlon = point.latAsString() + ',' + point.lonAsString();
|
||||
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);
|
||||
}
|
||||
|
@ -7,98 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class LocationCoords {
|
||||
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);
|
||||
|
||||
};
|
||||
#include "data/data_location.h"
|
||||
|
||||
class LocationClickHandler : public ClickHandler {
|
||||
public:
|
||||
LocationClickHandler(const LocationCoords &coords) : _coords(coords) {
|
||||
LocationClickHandler(const Data::LocationPoint &point)
|
||||
: _point(point) {
|
||||
setup();
|
||||
}
|
||||
|
||||
static QString Url(const LocationCoords &coords);
|
||||
static QString Url(const Data::LocationPoint &coords);
|
||||
|
||||
void onClick(ClickContext context) const override;
|
||||
|
||||
@ -116,7 +34,7 @@ public:
|
||||
private:
|
||||
|
||||
void setup();
|
||||
LocationCoords _coords;
|
||||
Data::LocationPoint _point;
|
||||
QString _text;
|
||||
|
||||
};
|
||||
|
@ -789,7 +789,7 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
|
||||
return media.vgeo.match([&](const MTPDgeoPoint &point) -> Result {
|
||||
return std::make_unique<Data::MediaLocation>(
|
||||
item,
|
||||
LocationCoords(point));
|
||||
Data::LocationPoint(point));
|
||||
}, [](const MTPDgeoPointEmpty &) -> Result {
|
||||
return nullptr;
|
||||
});
|
||||
@ -797,7 +797,7 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
|
||||
return media.vgeo.match([&](const MTPDgeoPoint &point) -> Result {
|
||||
return std::make_unique<Data::MediaLocation>(
|
||||
item,
|
||||
LocationCoords(point));
|
||||
Data::LocationPoint(point));
|
||||
}, [](const MTPDgeoPointEmpty &) -> Result {
|
||||
return nullptr;
|
||||
});
|
||||
@ -805,7 +805,7 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
|
||||
return media.vgeo.match([&](const MTPDgeoPoint &point) -> Result {
|
||||
return std::make_unique<Data::MediaLocation>(
|
||||
item,
|
||||
LocationCoords(point),
|
||||
Data::LocationPoint(point),
|
||||
qs(media.vtitle),
|
||||
qs(media.vaddress));
|
||||
}, [](const MTPDgeoPointEmpty &data) -> Result {
|
||||
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_location.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
namespace {
|
||||
@ -26,14 +27,14 @@ using TextState = HistoryView::TextState;
|
||||
|
||||
HistoryLocation::HistoryLocation(
|
||||
not_null<Element*> parent,
|
||||
not_null<LocationData*> location,
|
||||
not_null<Data::LocationThumbnail*> location,
|
||||
const QString &title,
|
||||
const QString &description)
|
||||
: HistoryMedia(parent)
|
||||
, _data(location)
|
||||
, _title(st::msgMinWidth)
|
||||
, _description(st::msgMinWidth)
|
||||
, _link(std::make_shared<LocationClickHandler>(_data->coords)) {
|
||||
, _link(std::make_shared<LocationClickHandler>(_data->point)) {
|
||||
if (!title.isEmpty()) {
|
||||
_title.setText(
|
||||
st::webPageTitleStyle,
|
||||
|
@ -9,14 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "history/media/history_media.h"
|
||||
|
||||
class LocationCoords;
|
||||
struct LocationData;
|
||||
namespace Data {
|
||||
struct LocationThumbnail;
|
||||
} // namespace Data
|
||||
|
||||
class HistoryLocation : public HistoryMedia {
|
||||
public:
|
||||
HistoryLocation(
|
||||
not_null<Element*> parent,
|
||||
not_null<LocationData*> location,
|
||||
not_null<Data::LocationThumbnail*> location,
|
||||
const QString &title = QString(),
|
||||
const QString &description = QString());
|
||||
|
||||
@ -58,7 +59,7 @@ private:
|
||||
TextSelection toDescriptionSelection(TextSelection selection) const;
|
||||
TextSelection fromDescriptionSelection(TextSelection selection) const;
|
||||
|
||||
LocationData *_data;
|
||||
const not_null<Data::LocationThumbnail*> _data;
|
||||
Ui::Text::String _title, _description;
|
||||
ClickHandlerPtr _link;
|
||||
|
||||
|
@ -521,7 +521,7 @@ void CopyPostLink(FullMsgId itemId) {
|
||||
const auto channel = item->history()->peer->asChannel();
|
||||
Assert(channel != nullptr);
|
||||
|
||||
Ui::Toast::Show(channel->isPublic()
|
||||
Ui::Toast::Show(channel->hasUsername()
|
||||
? tr::lng_channel_public_link_copied(tr::now)
|
||||
: tr::lng_context_about_private_link(tr::now));
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
|
||||
#include "history/history_location_manager.h" // LocationClickHandler.
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
@ -249,11 +250,11 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
||||
result->setContextCopyText(contextCopyText);
|
||||
return result;
|
||||
};
|
||||
if (auto user = _peer->asUser()) {
|
||||
if (Auth().supportMode()) {
|
||||
if (const auto user = _peer->asUser()) {
|
||||
if (user->session().supportMode()) {
|
||||
addInfoLineGeneric(
|
||||
Auth().supportHelper().infoLabelValue(user),
|
||||
Auth().supportHelper().infoTextValue(user));
|
||||
user->session().supportHelper().infoLabelValue(user),
|
||||
user->session().supportHelper().infoTextValue(user));
|
||||
}
|
||||
|
||||
addInfoOneLine(
|
||||
@ -273,26 +274,20 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
||||
auto linkText = LinkValue(
|
||||
_peer
|
||||
) | rpl::map([](const QString &link) {
|
||||
auto result = TextWithEntities{ link, {} };
|
||||
if (!link.isEmpty()) {
|
||||
auto remove = qstr("https://");
|
||||
if (result.text.startsWith(remove)) {
|
||||
result.text.remove(0, remove.size());
|
||||
}
|
||||
result.entities.push_back({
|
||||
EntityType::CustomUrl,
|
||||
0,
|
||||
result.text.size(),
|
||||
link });
|
||||
}
|
||||
return result;
|
||||
return link.isEmpty()
|
||||
? TextWithEntities()
|
||||
: Ui::Text::Link(
|
||||
(link.startsWith(qstr("https://"))
|
||||
? link.mid(qstr("https://").size())
|
||||
: link),
|
||||
link);
|
||||
});
|
||||
auto link = addInfoOneLine(
|
||||
tr::lng_info_link_label(),
|
||||
std::move(linkText),
|
||||
QString());
|
||||
link->setClickHandlerFilter([peer = _peer](auto&&...) {
|
||||
auto link = Core::App().createInternalLinkFull(
|
||||
const auto link = Core::App().createInternalLinkFull(
|
||||
peer->userName());
|
||||
if (!link.isEmpty()) {
|
||||
QApplication::clipboard()->setText(link);
|
||||
@ -300,6 +295,24 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
||||
}
|
||||
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));
|
||||
}
|
||||
if (!_peer->isSelf()) {
|
||||
|
@ -58,8 +58,8 @@ rpl::producer<TextWithEntities> BioValue(not_null<UserData*> user) {
|
||||
|
||||
auto PlainUsernameValue(not_null<PeerData*> peer) {
|
||||
return Notify::PeerUpdateValue(
|
||||
peer,
|
||||
Notify::PeerUpdate::Flag::UsernameChanged
|
||||
peer,
|
||||
Notify::PeerUpdate::Flag::UsernameChanged
|
||||
) | rpl::map([peer] {
|
||||
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) {
|
||||
return rpl::merge(
|
||||
Notify::PeerUpdateValue(
|
||||
|
@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <rpl/map.h>
|
||||
#include "observer_peer.h"
|
||||
|
||||
struct ChannelLocation;
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
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> AboutValue(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> IsContactValue(not_null<UserData*> user);
|
||||
rpl::producer<bool> CanInviteBotToGroupValue(not_null<UserData*> user);
|
||||
|
@ -1025,9 +1025,10 @@ Article::Article(not_null<Context*> context, Result *result, bool withThumb) : I
|
||||
, _withThumb(withThumb)
|
||||
, _title(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 && result->getLocationCoords(&location)) {
|
||||
_link = std::make_shared<LocationClickHandler>(location);
|
||||
if (!_link) {
|
||||
if (const auto point = result->getLocationPoint()) {
|
||||
_link = std::make_shared<LocationClickHandler>(*point);
|
||||
}
|
||||
}
|
||||
_thumbLetter = getResultThumbLetter();
|
||||
}
|
||||
|
@ -223,17 +223,16 @@ std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LocationCoords coords;
|
||||
if (result->getLocationCoords(&coords)) {
|
||||
if (const auto point = result->getLocationPoint()) {
|
||||
const auto scale = 1 + (cScale() * cIntRetinaFactor()) / 200;
|
||||
const auto zoom = 15 + (scale - 1);
|
||||
const auto w = st::inlineThumbSize / scale;
|
||||
const auto h = st::inlineThumbSize / scale;
|
||||
|
||||
auto location = GeoPointLocation();
|
||||
location.lat = coords.lat();
|
||||
location.lon = coords.lon();
|
||||
location.access = coords.accessHash();
|
||||
location.lat = point->lat();
|
||||
location.lon = point->lon();
|
||||
location.access = point->accessHash();
|
||||
location.width = w;
|
||||
location.height = h;
|
||||
location.zoom = zoom;
|
||||
@ -329,8 +328,8 @@ QString Result::getErrorOnSend(History *history) const {
|
||||
return sendData->getErrorOnSend(this, history);
|
||||
}
|
||||
|
||||
bool Result::getLocationCoords(LocationCoords *outLocation) const {
|
||||
return sendData->getLocationCoords(outLocation);
|
||||
std::optional<Data::LocationPoint> Result::getLocationPoint() const {
|
||||
return sendData->getLocationPoint();
|
||||
}
|
||||
|
||||
QString Result::getLayoutTitle() const {
|
||||
|
@ -8,7 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
class FileLoader;
|
||||
class LocationCoords;
|
||||
|
||||
namespace Data {
|
||||
class LocationPoint;
|
||||
} // namespace Data
|
||||
|
||||
namespace InlineBots {
|
||||
|
||||
@ -55,7 +58,7 @@ public:
|
||||
QString getErrorOnSend(History *history) const;
|
||||
|
||||
// interface for Layout:: usage
|
||||
bool getLocationCoords(LocationCoords *outLocation) const;
|
||||
std::optional<Data::LocationPoint> getLocationPoint() const;
|
||||
QString getLayoutTitle() const;
|
||||
QString getLayoutDescription() const;
|
||||
|
||||
|
@ -45,8 +45,8 @@ public:
|
||||
virtual bool hasLocationCoords() const {
|
||||
return false;
|
||||
}
|
||||
virtual bool getLocationCoords(LocationCoords *outLocation) const {
|
||||
return false;
|
||||
virtual std::optional<Data::LocationPoint> getLocationPoint() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
virtual QString getLayoutTitle(const Result *owner) const;
|
||||
virtual QString getLayoutDescription(const Result *owner) const;
|
||||
@ -121,14 +121,12 @@ public:
|
||||
bool hasLocationCoords() const override {
|
||||
return true;
|
||||
}
|
||||
bool getLocationCoords(LocationCoords *outLocation) const override {
|
||||
Assert(outLocation != nullptr);
|
||||
*outLocation = _location;
|
||||
return true;
|
||||
std::optional<Data::LocationPoint> getLocationPoint() const override {
|
||||
return _location;
|
||||
}
|
||||
|
||||
private:
|
||||
LocationCoords _location;
|
||||
Data::LocationPoint _location;
|
||||
|
||||
};
|
||||
|
||||
@ -153,14 +151,12 @@ public:
|
||||
bool hasLocationCoords() const override {
|
||||
return true;
|
||||
}
|
||||
bool getLocationCoords(LocationCoords *outLocation) const override {
|
||||
Assert(outLocation != nullptr);
|
||||
*outLocation = _location;
|
||||
return true;
|
||||
std::optional<Data::LocationPoint> getLocationPoint() const override {
|
||||
return _location;
|
||||
}
|
||||
|
||||
private:
|
||||
LocationCoords _location;
|
||||
Data::LocationPoint _location;
|
||||
QString _venueId, _provider, _title, _address;
|
||||
|
||||
};
|
||||
|
@ -66,6 +66,7 @@ struct PeerUpdate {
|
||||
ChannelStickersChanged = (1 << 18),
|
||||
ChannelPromotedChanged = (1 << 19),
|
||||
ChannelLinkedChat = (1 << 20),
|
||||
ChannelLocation = (1 << 21),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
@ -478,6 +478,6 @@ bool linuxMoveFile(const char *from, const char *to) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool psLaunchMaps(const LocationCoords &coords) {
|
||||
bool psLaunchMaps(const Data::LocationPoint &point) {
|
||||
return false;
|
||||
}
|
||||
|
@ -10,7 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
|
||||
class LocationCoords;
|
||||
namespace Data {
|
||||
class LocationPoint;
|
||||
} // namespace Data
|
||||
|
||||
namespace Platform {
|
||||
|
||||
@ -104,4 +106,4 @@ public:
|
||||
|
||||
bool linuxMoveFile(const char *from, const char *to);
|
||||
|
||||
bool psLaunchMaps(const LocationCoords &coords);
|
||||
bool psLaunchMaps(const Data::LocationPoint &point);
|
||||
|
@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "platform/mac/specific_mac_p.h"
|
||||
|
||||
class LocationCoords;
|
||||
namespace Data {
|
||||
class LocationPoint;
|
||||
} // namespace Data
|
||||
|
||||
namespace Platform {
|
||||
|
||||
@ -108,4 +110,4 @@ QString strStyleOfInterface();
|
||||
QString strTitleWrapClass();
|
||||
QString strTitleClass();
|
||||
|
||||
bool psLaunchMaps(const LocationCoords &coords);
|
||||
bool psLaunchMaps(const Data::LocationPoint &point);
|
||||
|
@ -288,8 +288,8 @@ QByteArray psPathBookmark(const QString &path) {
|
||||
return objc_pathBookmark(path);
|
||||
}
|
||||
|
||||
bool psLaunchMaps(const LocationCoords &coords) {
|
||||
return QDesktopServices::openUrl(qsl("https://maps.apple.com/?q=Point&z=16&ll=%1,%2").arg(coords.latAsString()).arg(coords.lonAsString()));
|
||||
bool psLaunchMaps(const Data::LocationPoint &point) {
|
||||
return QDesktopServices::openUrl(qsl("https://maps.apple.com/?q=Point&z=16&ll=%1,%2").arg(point.latAsString()).arg(point.lonAsString()));
|
||||
}
|
||||
|
||||
QString strNotificationAboutThemeChange() {
|
||||
|
@ -583,6 +583,6 @@ void psWriteDump() {
|
||||
#endif // TDESKTOP_DISABLE_CRASH_REPORTS
|
||||
}
|
||||
|
||||
bool psLaunchMaps(const LocationCoords &coords) {
|
||||
return QDesktopServices::openUrl(qsl("bingmaps:?lvl=16&collection=point.%1_%2_Point").arg(coords.latAsString()).arg(coords.lonAsString()));
|
||||
bool psLaunchMaps(const Data::LocationPoint &point) {
|
||||
return QDesktopServices::openUrl(qsl("bingmaps:?lvl=16&collection=point.%1_%2_Point").arg(point.latAsString()).arg(point.lonAsString()));
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "platform/win/wrapper_windows_h.h"
|
||||
|
||||
class LocationCoords;
|
||||
namespace Data {
|
||||
class LocationPoint;
|
||||
} // namespace Data
|
||||
|
||||
namespace Platform {
|
||||
|
||||
@ -107,4 +109,4 @@ public:
|
||||
|
||||
};
|
||||
|
||||
bool psLaunchMaps(const LocationCoords &coords);
|
||||
bool psLaunchMaps(const Data::LocationPoint &point);
|
||||
|
@ -506,7 +506,7 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) {
|
||||
[channel] { Auth().api().joinChannel(channel); });
|
||||
}
|
||||
if (_source != PeerMenuSource::ChatsList) {
|
||||
auto needReport = !channel->amCreator()
|
||||
const auto needReport = !channel->amCreator()
|
||||
&& (!isGroup || channel->isPublic());
|
||||
if (needReport) {
|
||||
_addAction(tr::lng_profile_report(tr::now), [channel] {
|
||||
|
@ -190,6 +190,8 @@
|
||||
<(src_loc)/data/data_game.h
|
||||
<(src_loc)/data/data_groups.cpp
|
||||
<(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.h
|
||||
<(src_loc)/data/data_messages.cpp
|
||||
|
Loading…
Reference in New Issue
Block a user