mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 13:47:05 +02:00
Added initial api support of text phone entity in messages.
This commit is contained in:
parent
50ce847b31
commit
ba611d0f2d
9 changed files with 373 additions and 2 deletions
|
@ -445,6 +445,8 @@ PRIVATE
|
|||
core/launcher.h
|
||||
core/local_url_handlers.cpp
|
||||
core/local_url_handlers.h
|
||||
core/phone_click_handler.cpp
|
||||
core/phone_click_handler.h
|
||||
core/sandbox.cpp
|
||||
core/sandbox.h
|
||||
core/shortcuts.cpp
|
||||
|
|
|
@ -3427,6 +3427,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_add_contact" = "Create";
|
||||
"lng_add_contact_button" = "New contact";
|
||||
"lng_contacts_header" = "Contacts";
|
||||
"lng_menu_not_contact" = "This number is not on Telegram";
|
||||
"lng_contacts_hidden_stories" = "Hidden Stories";
|
||||
"lng_contacts_stories_status#one" = "{count} story";
|
||||
"lng_contacts_stories_status#other" = "{count} stories";
|
||||
|
|
|
@ -178,7 +178,11 @@ EntitiesInText EntitiesFromMTP(
|
|||
});
|
||||
}
|
||||
}, [&](const MTPDmessageEntityPhone &d) {
|
||||
// Skipping phones.
|
||||
result.push_back({
|
||||
EntityType::Phone,
|
||||
d.voffset().v,
|
||||
d.vlength().v,
|
||||
});
|
||||
}, [&](const MTPDmessageEntityCashtag &d) {
|
||||
result.push_back({
|
||||
EntityType::Cashtag,
|
||||
|
@ -266,6 +270,9 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
|
|||
case EntityType::Email: {
|
||||
v.push_back(MTP_messageEntityEmail(offset, length));
|
||||
} break;
|
||||
case EntityType::Phone: {
|
||||
v.push_back(MTP_messageEntityPhone(offset, length));
|
||||
} break;
|
||||
case EntityType::Hashtag: {
|
||||
v.push_back(MTP_messageEntityHashtag(offset, length));
|
||||
} break;
|
||||
|
|
|
@ -52,6 +52,8 @@ struct ClickHandlerContext {
|
|||
};
|
||||
Q_DECLARE_METATYPE(ClickHandlerContext);
|
||||
|
||||
class PhoneClickHandler;
|
||||
|
||||
class HiddenUrlClickHandler : public UrlClickHandler {
|
||||
public:
|
||||
HiddenUrlClickHandler(QString url) : UrlClickHandler(url, false) {
|
||||
|
|
325
Telegram/SourceFiles/core/phone_click_handler.cpp
Normal file
325
Telegram/SourceFiles/core/phone_click_handler.cpp
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
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 "core/phone_click_handler.h"
|
||||
|
||||
#include "core/click_handler_types.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/widgets/menu/menu_item_base.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_calls.h"
|
||||
#include "styles/style_chat.h" // popupMenuExpandedSeparator.
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString Trim(QString text) {
|
||||
return text
|
||||
.replace('+', QString())
|
||||
.replace(' ', QString())
|
||||
.replace('-', QString());
|
||||
}
|
||||
|
||||
class ResolvePhoneAction final : public Ui::Menu::ItemBase {
|
||||
public:
|
||||
ResolvePhoneAction(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::Menu &st,
|
||||
const QString &phone,
|
||||
not_null<Window::SessionController*> controller);
|
||||
|
||||
bool isEnabled() const override;
|
||||
not_null<QAction*> action() const override;
|
||||
|
||||
void handleKeyPress(not_null<QKeyEvent*> e) override;
|
||||
|
||||
protected:
|
||||
QPoint prepareRippleStartPosition() const override;
|
||||
QImage prepareRippleMask() const override;
|
||||
|
||||
int contentHeight() const override;
|
||||
|
||||
private:
|
||||
void prepare();
|
||||
void paint(Painter &p);
|
||||
|
||||
const not_null<QAction*> _dummyAction;
|
||||
const style::Menu &_st;
|
||||
rpl::variable<PeerData*> _peer;
|
||||
rpl::variable<bool> _loaded;
|
||||
Ui::PeerUserpicView _userpicView;
|
||||
|
||||
MTP::Sender _api;
|
||||
|
||||
Ui::Text::String _above;
|
||||
Ui::Text::String _below;
|
||||
int _aboveWidth = 0;
|
||||
int _belowWidth = 0;
|
||||
const int _height = 0;
|
||||
|
||||
};
|
||||
|
||||
ResolvePhoneAction::ResolvePhoneAction(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const style::Menu &st,
|
||||
const QString &phone,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: ItemBase(parent, st)
|
||||
, _dummyAction(new QAction(parent))
|
||||
, _st(st)
|
||||
, _api(&controller->session().mtp())
|
||||
, _height(rect::m::sum::v(st::groupCallJoinAsPadding)
|
||||
+ st::groupCallJoinAsPhotoSize) {
|
||||
setAcceptBoth(true);
|
||||
initResizeHook(parent->sizeValue());
|
||||
setClickedCallback([=] {
|
||||
if (const auto peer = _peer.current()) {
|
||||
controller->showPeerInfo(peer);
|
||||
}
|
||||
});
|
||||
|
||||
const auto formattedPhone = Trim(phone);
|
||||
|
||||
const auto owner = &controller->session().data();
|
||||
|
||||
if (const auto peer = owner->userByPhone(formattedPhone)) {
|
||||
_peer = peer;
|
||||
_loaded.force_assign(true);
|
||||
} else {
|
||||
_api.request(MTPcontacts_ResolvePhone(
|
||||
MTP_string(phone)
|
||||
)).done([=](const MTPcontacts_ResolvedPeer &result) {
|
||||
result.match([&](const MTPDcontacts_resolvedPeer &data) {
|
||||
owner->processUsers(data.vusers());
|
||||
owner->processChats(data.vchats());
|
||||
if (const auto peerId = peerFromMTP(data.vpeer())) {
|
||||
_peer = owner->peer(peerId);
|
||||
}
|
||||
_loaded.force_assign(true);
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.code() == 400) {
|
||||
_peer.force_assign(nullptr);
|
||||
_loaded.force_assign(true);
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
Painter p(this);
|
||||
paint(p);
|
||||
}, lifetime());
|
||||
|
||||
enableMouseSelecting();
|
||||
prepare();
|
||||
}
|
||||
|
||||
void ResolvePhoneAction::paint(Painter &p) {
|
||||
const auto selected = isSelected() && _peer.current();
|
||||
const auto height = contentHeight();
|
||||
if (selected && _st.itemBgOver->c.alpha() < 255) {
|
||||
p.fillRect(0, 0, width(), height, _st.itemBg);
|
||||
}
|
||||
p.fillRect(0, 0, width(), height, selected ? _st.itemBgOver : _st.itemBg);
|
||||
if (isEnabled()) {
|
||||
paintRipple(p, 0, 0);
|
||||
}
|
||||
|
||||
const auto &padding = st::groupCallJoinAsPadding;
|
||||
const auto textLeft = padding.left()
|
||||
+ st::groupCallJoinAsPhotoSize
|
||||
+ padding.left();
|
||||
if (const auto peer = _peer.current()) {
|
||||
peer->paintUserpic(
|
||||
p,
|
||||
_userpicView,
|
||||
padding.left(),
|
||||
padding.top(),
|
||||
st::groupCallJoinAsPhotoSize);
|
||||
p.setPen(selected ? _st.itemFgOver : _st.itemFg);
|
||||
_above.drawLeftElided(
|
||||
p,
|
||||
textLeft,
|
||||
st::groupCallJoinAsTextTop,
|
||||
width() - textLeft - padding.right(),
|
||||
width());
|
||||
p.setPen(selected ? _st.itemFgShortcutOver : _st.itemFgShortcut);
|
||||
_below.drawLeftElided(
|
||||
p,
|
||||
textLeft,
|
||||
st::groupCallJoinAsNameTop,
|
||||
_belowWidth,
|
||||
width());
|
||||
} else {
|
||||
p.setPen(selected ? _st.itemFgShortcutOver : _st.itemFgShortcut);
|
||||
p.drawText(rect() - padding, _below.toString(), style::al_center);
|
||||
}
|
||||
}
|
||||
|
||||
void ResolvePhoneAction::prepare() {
|
||||
rpl::combine(
|
||||
tr::lng_context_view_profile(),
|
||||
_peer.value(
|
||||
) | rpl::map([](PeerData *peer) {
|
||||
return peer
|
||||
? Info::Profile::NameValue(peer)
|
||||
: rpl::single(QString());
|
||||
}) | rpl::flatten_latest(),
|
||||
tr::lng_menu_not_contact(),
|
||||
_loaded.value(
|
||||
) | rpl::map([](bool loaded) {
|
||||
return loaded
|
||||
? rpl::single(QString())
|
||||
: tr::lng_contacts_loading();
|
||||
}) | rpl::flatten_latest()
|
||||
) | rpl::start_with_next([=](
|
||||
QString text,
|
||||
QString name,
|
||||
QString no,
|
||||
QString loading) {
|
||||
const auto &padding = st::groupCallJoinAsPadding;
|
||||
QWidget::setAttribute(
|
||||
Qt::WA_TransparentForMouseEvents,
|
||||
!_peer.current());
|
||||
const auto above = name;
|
||||
const auto below = !loading.isEmpty()
|
||||
? loading
|
||||
: name.isEmpty()
|
||||
? no
|
||||
: text;
|
||||
const auto options = kDefaultTextOptions;
|
||||
const auto tempWidth = [&] {
|
||||
_below.setMarkedText(_st.itemStyle, { text }, options);
|
||||
return _below.maxWidth();
|
||||
}();
|
||||
_above.setMarkedText(_st.itemStyle, { above }, options);
|
||||
_below.setMarkedText(_st.itemStyle, { below }, options);
|
||||
const auto textWidth = _above.maxWidth();
|
||||
const auto nameWidth = _below.maxWidth();
|
||||
const auto textLeft = padding.left()
|
||||
+ st::groupCallJoinAsPhotoSize
|
||||
+ padding.left();
|
||||
const auto w = std::clamp(
|
||||
(textLeft + tempWidth + padding.right()),
|
||||
_st.widthMin,
|
||||
_st.widthMax);
|
||||
setMinWidth(w);
|
||||
_aboveWidth = w - textLeft - padding.right();
|
||||
_belowWidth = w
|
||||
- ((loading.isEmpty() && name.isEmpty()) ? 0 : textLeft)
|
||||
- padding.right();
|
||||
update();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
bool ResolvePhoneAction::isEnabled() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
not_null<QAction*> ResolvePhoneAction::action() const {
|
||||
return _dummyAction;
|
||||
}
|
||||
|
||||
QPoint ResolvePhoneAction::prepareRippleStartPosition() const {
|
||||
return mapFromGlobal(QCursor::pos());
|
||||
}
|
||||
|
||||
QImage ResolvePhoneAction::prepareRippleMask() const {
|
||||
return Ui::RippleAnimation::RectMask(size());
|
||||
}
|
||||
|
||||
int ResolvePhoneAction::contentHeight() const {
|
||||
return _height;
|
||||
}
|
||||
|
||||
void ResolvePhoneAction::handleKeyPress(not_null<QKeyEvent*> e) {
|
||||
if (!isSelected() || !_peer.current()) {
|
||||
return;
|
||||
}
|
||||
const auto key = e->key();
|
||||
if (key == Qt::Key_Enter || key == Qt::Key_Return) {
|
||||
setClicked(Ui::Menu::TriggeredSource::Keyboard);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PhoneClickHandler::PhoneClickHandler(
|
||||
not_null<Main::Session*> session,
|
||||
QString text)
|
||||
: _session(session)
|
||||
, _text(text) {
|
||||
}
|
||||
|
||||
void PhoneClickHandler::onClick(ClickContext context) const {
|
||||
if (context.button != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
const auto controller = my.sessionWindow.get();
|
||||
const auto pos = QCursor::pos();
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
const auto menu = Ui::CreateChild<Ui::PopupMenu>(
|
||||
controller->content(),
|
||||
st::popupMenuWithIcons);
|
||||
|
||||
const auto phone = _text;
|
||||
|
||||
#if 0
|
||||
const auto maybeContact = [&]() -> PeerData* {
|
||||
const auto &chats = controller->session().data().contactsList();
|
||||
for (const auto &row : chats->all()) {
|
||||
if (const auto history = row->history()) {
|
||||
if (const auto user = history->peer->asUser()) {
|
||||
if (Trim(user->phone()) == Trim(phone)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
#endif
|
||||
|
||||
menu->addAction(tr::lng_profile_copy_phone(tr::now), [=] {
|
||||
TextUtilities::SetClipboardText(
|
||||
TextForMimeData::Simple(phone.trimmed()));
|
||||
}, &st::menuIconCopy);
|
||||
|
||||
menu->addSeparator(&st::popupMenuExpandedSeparator.menu.separator);
|
||||
|
||||
menu->addAction(
|
||||
base::make_unique_q<ResolvePhoneAction>(
|
||||
menu,
|
||||
menu->st().menu,
|
||||
phone,
|
||||
controller));
|
||||
|
||||
menu->popup(pos);
|
||||
}
|
||||
|
||||
auto PhoneClickHandler::getTextEntity() const -> TextEntity {
|
||||
return { EntityType::Phone };
|
||||
}
|
||||
|
||||
QString PhoneClickHandler::tooltip() const {
|
||||
return _text;
|
||||
}
|
30
Telegram/SourceFiles/core/phone_click_handler.h
Normal file
30
Telegram/SourceFiles/core/phone_click_handler.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "ui/basic_click_handlers.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
class PhoneClickHandler : public ClickHandler {
|
||||
public:
|
||||
PhoneClickHandler(not_null<Main::Session*> session, QString text);
|
||||
|
||||
void onClick(ClickContext context) const override;
|
||||
|
||||
TextEntity getTextEntity() const override;
|
||||
|
||||
QString tooltip() const override;
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
QString _text;
|
||||
|
||||
};
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "boxes/url_auth_box.h"
|
||||
#include "core/phone_click_handler.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_app_config.h"
|
||||
|
@ -217,6 +218,8 @@ std::shared_ptr<ClickHandler> UiIntegration::createLinkHandler(
|
|||
return std::make_shared<MonospaceClickHandler>(data.text, data.type);
|
||||
case EntityType::Pre:
|
||||
return std::make_shared<MonospaceClickHandler>(data.text, data.type);
|
||||
case EntityType::Phone:
|
||||
return std::make_shared<PhoneClickHandler>(my->session, data.text);
|
||||
}
|
||||
return Integration::createLinkHandler(data, context);
|
||||
}
|
||||
|
|
|
@ -3127,6 +3127,7 @@ void HistoryItem::setText(const TextWithEntities &textWithEntities) {
|
|||
auto type = entity.type();
|
||||
if (type == EntityType::Url
|
||||
|| type == EntityType::CustomUrl
|
||||
|| type == EntityType::Phone
|
||||
|| type == EntityType::Email) {
|
||||
_flags |= MessageFlag::HasTextLinks;
|
||||
break;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d0514b2b022043b3777b06d6068232aa4cda7e80
|
||||
Subproject commit 4440037244bd0175752b82ee1177c676a5340f5c
|
Loading…
Add table
Reference in a new issue