mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Added initial support of vcard from media contacts.
This commit is contained in:
parent
1656a9c3e2
commit
47ce34e987
6 changed files with 216 additions and 37 deletions
|
@ -5240,6 +5240,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_search_tab_no_results_retry" = "Try another hashtag.";
|
"lng_search_tab_no_results_retry" = "Try another hashtag.";
|
||||||
"lng_search_tab_by_hashtag" = "Enter a hashtag to find messages containing it.";
|
"lng_search_tab_by_hashtag" = "Enter a hashtag to find messages containing it.";
|
||||||
|
|
||||||
|
"lng_contact_details_button" = "View Contact";
|
||||||
|
"lng_contact_details_title" = "Contact details";
|
||||||
|
"lng_contact_details_phone" = "Phone";
|
||||||
|
"lng_contact_details_phone_main" = "Main Phone";
|
||||||
|
"lng_contact_details_phone_home" = "Home Phone";
|
||||||
|
"lng_contact_details_phone_mobile" = "Mobile Phone";
|
||||||
|
"lng_contact_details_phone_work" = "Work Phone";
|
||||||
|
"lng_contact_details_phone_other" = "Other Phone";
|
||||||
|
"lng_contact_details_email" = "Email";
|
||||||
|
"lng_contact_details_address" = "Address";
|
||||||
|
"lng_contact_details_url" = "URL";
|
||||||
|
"lng_contact_details_note" = "Note";
|
||||||
|
"lng_contact_details_birthday" = "Birthday";
|
||||||
|
"lng_contact_details_organization" = "Organization";
|
||||||
|
|
||||||
// Wnd specific
|
// Wnd specific
|
||||||
|
|
||||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||||
|
|
|
@ -1224,14 +1224,17 @@ MediaContact::MediaContact(
|
||||||
UserId userId,
|
UserId userId,
|
||||||
const QString &firstName,
|
const QString &firstName,
|
||||||
const QString &lastName,
|
const QString &lastName,
|
||||||
const QString &phoneNumber)
|
const QString &phoneNumber,
|
||||||
: Media(parent) {
|
const SharedContact::VcardItems &vcardItems)
|
||||||
|
: Media(parent)
|
||||||
|
, _contact(SharedContact{
|
||||||
|
.userId = userId,
|
||||||
|
.firstName = firstName,
|
||||||
|
.lastName = lastName,
|
||||||
|
.phoneNumber = phoneNumber,
|
||||||
|
.vcardItems = vcardItems,
|
||||||
|
}) {
|
||||||
parent->history()->owner().registerContactItem(userId, parent);
|
parent->history()->owner().registerContactItem(userId, parent);
|
||||||
|
|
||||||
_contact.userId = userId;
|
|
||||||
_contact.firstName = firstName;
|
|
||||||
_contact.lastName = lastName;
|
|
||||||
_contact.phoneNumber = phoneNumber;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaContact::~MediaContact() {
|
MediaContact::~MediaContact() {
|
||||||
|
@ -1246,7 +1249,8 @@ std::unique_ptr<Media> MediaContact::clone(not_null<HistoryItem*> parent) {
|
||||||
_contact.userId,
|
_contact.userId,
|
||||||
_contact.firstName,
|
_contact.firstName,
|
||||||
_contact.lastName,
|
_contact.lastName,
|
||||||
_contact.phoneNumber);
|
_contact.phoneNumber,
|
||||||
|
_contact.vcardItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
const SharedContact *MediaContact::sharedContact() const {
|
const SharedContact *MediaContact::sharedContact() const {
|
||||||
|
@ -1301,12 +1305,7 @@ std::unique_ptr<HistoryView::Media> MediaContact::createView(
|
||||||
not_null<HistoryView::Element*> message,
|
not_null<HistoryView::Element*> message,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
HistoryView::Element *replacing) {
|
HistoryView::Element *replacing) {
|
||||||
return std::make_unique<HistoryView::Contact>(
|
return std::make_unique<HistoryView::Contact>(message, _contact);
|
||||||
message,
|
|
||||||
_contact.userId,
|
|
||||||
_contact.firstName,
|
|
||||||
_contact.lastName,
|
|
||||||
_contact.phoneNumber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaLocation::MediaLocation(
|
MediaLocation::MediaLocation(
|
||||||
|
|
|
@ -47,11 +47,30 @@ enum class CallFinishReason : char {
|
||||||
Hangup,
|
Hangup,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SharedContact {
|
struct SharedContact final {
|
||||||
UserId userId = 0;
|
UserId userId = 0;
|
||||||
QString firstName;
|
QString firstName;
|
||||||
QString lastName;
|
QString lastName;
|
||||||
QString phoneNumber;
|
QString phoneNumber;
|
||||||
|
|
||||||
|
enum class VcardItemType {
|
||||||
|
Phone,
|
||||||
|
PhoneMain,
|
||||||
|
PhoneHome,
|
||||||
|
PhoneMobile,
|
||||||
|
PhoneWork,
|
||||||
|
PhoneOther,
|
||||||
|
Email,
|
||||||
|
Address,
|
||||||
|
Url,
|
||||||
|
Note,
|
||||||
|
Birthday,
|
||||||
|
Organization,
|
||||||
|
Name,
|
||||||
|
};
|
||||||
|
|
||||||
|
using VcardItems = base::flat_map<VcardItemType, QString>;
|
||||||
|
VcardItems vcardItems;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Call {
|
struct Call {
|
||||||
|
@ -308,7 +327,8 @@ public:
|
||||||
UserId userId,
|
UserId userId,
|
||||||
const QString &firstName,
|
const QString &firstName,
|
||||||
const QString &lastName,
|
const QString &lastName,
|
||||||
const QString &phoneNumber);
|
const QString &phoneNumber,
|
||||||
|
const SharedContact::VcardItems &vcardItems);
|
||||||
~MediaContact();
|
~MediaContact();
|
||||||
|
|
||||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||||
|
|
|
@ -211,12 +211,59 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
|
||||||
const MTPMessageMedia &media) {
|
const MTPMessageMedia &media) {
|
||||||
using Result = std::unique_ptr<Data::Media>;
|
using Result = std::unique_ptr<Data::Media>;
|
||||||
return media.match([&](const MTPDmessageMediaContact &media) -> Result {
|
return media.match([&](const MTPDmessageMediaContact &media) -> Result {
|
||||||
|
|
||||||
|
const auto vcardItems = [&] {
|
||||||
|
using Type = Data::SharedContact::VcardItemType;
|
||||||
|
auto items = Data::SharedContact::VcardItems();
|
||||||
|
for (const auto &item : qs(media.vvcard()).split('\n')) {
|
||||||
|
const auto parts = item.split(':');
|
||||||
|
if (parts.size() == 2) {
|
||||||
|
const auto &type = parts.front();
|
||||||
|
const auto &value = parts[1];
|
||||||
|
|
||||||
|
if (type.startsWith("TEL")) {
|
||||||
|
const auto telType = type.contains("PREF")
|
||||||
|
? Type::PhoneMain
|
||||||
|
: type.contains("HOME")
|
||||||
|
? Type::PhoneHome
|
||||||
|
: type.contains("WORK")
|
||||||
|
? Type::PhoneWork
|
||||||
|
: (type.contains("CELL")
|
||||||
|
|| type.contains("MOBILE"))
|
||||||
|
? Type::PhoneMobile
|
||||||
|
: type.contains("OTHER")
|
||||||
|
? Type::PhoneOther
|
||||||
|
: Type::Phone;
|
||||||
|
items[telType] = value;
|
||||||
|
} else if (type.startsWith("EMAIL")) {
|
||||||
|
items[Type::Email] = value;
|
||||||
|
} else if (type.startsWith("URL")) {
|
||||||
|
items[Type::Url] = value;
|
||||||
|
} else if (type.startsWith("NOTE")) {
|
||||||
|
items[Type::Note] = value;
|
||||||
|
} else if (type.startsWith("ORG")) {
|
||||||
|
items[Type::Organization] = value;
|
||||||
|
items[Type::Organization].replace(';', ' ');
|
||||||
|
} else if (type.startsWith("ADR")) {
|
||||||
|
items[Type::Address] = value;
|
||||||
|
} else if (type.startsWith("BDAY")) {
|
||||||
|
items[Type::Birthday] = value;
|
||||||
|
} else if (type.startsWith("N")) {
|
||||||
|
items[Type::Birthday] = value;
|
||||||
|
items[Type::Birthday].replace(';', ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}();
|
||||||
|
|
||||||
return std::make_unique<Data::MediaContact>(
|
return std::make_unique<Data::MediaContact>(
|
||||||
item,
|
item,
|
||||||
media.vuser_id().v,
|
media.vuser_id().v,
|
||||||
qs(media.vfirst_name()),
|
qs(media.vfirst_name()),
|
||||||
qs(media.vlast_name()),
|
qs(media.vlast_name()),
|
||||||
qs(media.vphone_number()));
|
qs(media.vphone_number()),
|
||||||
|
vcardItems);
|
||||||
}, [&](const MTPDmessageMediaGeo &media) -> Result {
|
}, [&](const MTPDmessageMediaGeo &media) -> Result {
|
||||||
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>(
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "boxes/add_contact_box.h"
|
#include "boxes/add_contact_box.h"
|
||||||
#include "core/click_handler_types.h" // ClickHandlerContext
|
#include "core/click_handler_types.h" // ClickHandlerContext
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
|
@ -18,16 +19,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/media/history_view_media_common.h"
|
#include "history/view/media/history_view_media_common.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "styles/style_boxes.h"
|
|
||||||
#include "styles/style_chat.h"
|
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "ui/empty_userpic.h"
|
#include "ui/empty_userpic.h"
|
||||||
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/power_saving.h"
|
#include "ui/power_saving.h"
|
||||||
#include "ui/rect.h"
|
#include "ui/rect.h"
|
||||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
|
#include "ui/text/text_utilities.h" // Ui::Text::Wrapped.
|
||||||
|
#include "ui/vertical_list.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -103,33 +108,116 @@ ClickHandlerPtr AddContactClickHandler(not_null<HistoryItem*> item) {
|
||||||
return clickHandlerPtr;
|
return clickHandlerPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Fn<void(not_null<Ui::GenericBox*>)> VcardBoxFactory(
|
||||||
|
const Data::SharedContact::VcardItems &vcardItems) {
|
||||||
|
if (vcardItems.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return [=](not_null<Ui::GenericBox*> box) {
|
||||||
|
box->setTitle(tr::lng_contact_details_title());
|
||||||
|
const auto &stL = st::proxyApplyBoxLabel;
|
||||||
|
const auto &stSubL = st::boxDividerLabel;
|
||||||
|
const auto add = [&](const QString &s, tr::phrase<> phrase) {
|
||||||
|
if (!s.isEmpty()) {
|
||||||
|
const auto label = box->addRow(
|
||||||
|
object_ptr<Ui::FlatLabel>(box, s, stL));
|
||||||
|
box->addRow(object_ptr<Ui::FlatLabel>(box, phrase(), stSubL));
|
||||||
|
Ui::AddSkip(box->verticalLayout());
|
||||||
|
Ui::AddSkip(box->verticalLayout());
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
return (Ui::FlatLabel*)(nullptr);
|
||||||
|
};
|
||||||
|
for (const auto &[type, value] : vcardItems) {
|
||||||
|
using Type = Data::SharedContact::VcardItemType;
|
||||||
|
const auto isPhoneType = (type == Type::Phone)
|
||||||
|
|| (type == Type::PhoneMain)
|
||||||
|
|| (type == Type::PhoneHome)
|
||||||
|
|| (type == Type::PhoneMobile)
|
||||||
|
|| (type == Type::PhoneWork)
|
||||||
|
|| (type == Type::PhoneOther);
|
||||||
|
const auto typePhrase = (type == Type::Phone)
|
||||||
|
? tr::lng_contact_details_phone
|
||||||
|
: (type == Type::PhoneMain)
|
||||||
|
? tr::lng_contact_details_phone_main
|
||||||
|
: (type == Type::PhoneHome)
|
||||||
|
? tr::lng_contact_details_phone_home
|
||||||
|
: (type == Type::PhoneMobile)
|
||||||
|
? tr::lng_contact_details_phone_mobile
|
||||||
|
: (type == Type::PhoneWork)
|
||||||
|
? tr::lng_contact_details_phone_work
|
||||||
|
: (type == Type::PhoneOther)
|
||||||
|
? tr::lng_contact_details_phone_other
|
||||||
|
: (type == Type::Email)
|
||||||
|
? tr::lng_contact_details_email
|
||||||
|
: (type == Type::Address)
|
||||||
|
? tr::lng_contact_details_address
|
||||||
|
: (type == Type::Url)
|
||||||
|
? tr::lng_contact_details_url
|
||||||
|
: (type == Type::Note)
|
||||||
|
? tr::lng_contact_details_note
|
||||||
|
: (type == Type::Birthday)
|
||||||
|
? tr::lng_contact_details_birthday
|
||||||
|
: (type == Type::Organization)
|
||||||
|
? tr::lng_contact_details_organization
|
||||||
|
: tr::lng_payments_info_name;
|
||||||
|
if (const auto label = add(value, typePhrase)) {
|
||||||
|
const auto copyText = isPhoneType
|
||||||
|
? tr::lng_profile_copy_phone
|
||||||
|
: (type == Type::Email)
|
||||||
|
? tr::lng_context_copy_email
|
||||||
|
: (type == Type::Url)
|
||||||
|
? tr::lng_context_copy_link
|
||||||
|
: (type == Type::Name)
|
||||||
|
? tr::lng_profile_copy_fullname
|
||||||
|
: tr::lng_context_copy_text;
|
||||||
|
label->setContextCopyText(copyText(tr::now));
|
||||||
|
if (type == Type::Email) {
|
||||||
|
label->setMarkedText(
|
||||||
|
Ui::Text::Wrapped({ value }, EntityType::Email));
|
||||||
|
} else if (type == Type::Url) {
|
||||||
|
label->setMarkedText(
|
||||||
|
Ui::Text::Wrapped({ value }, EntityType::Url));
|
||||||
|
} else if (isPhoneType) {
|
||||||
|
label->setText(Ui::FormatPhone(value));
|
||||||
|
}
|
||||||
|
using Request = Ui::FlatLabel::ContextMenuRequest;
|
||||||
|
label->setContextMenuHook([=](Request r) {
|
||||||
|
label->fillContextMenu(r.link
|
||||||
|
? r
|
||||||
|
: Request{ .menu = r.menu, .fullSelection = true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Contact::Contact(
|
Contact::Contact(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
UserId userId,
|
const Data::SharedContact &data)
|
||||||
const QString &first,
|
|
||||||
const QString &last,
|
|
||||||
const QString &phone)
|
|
||||||
: Media(parent)
|
: Media(parent)
|
||||||
, _st(st::historyPagePreview)
|
, _st(st::historyPagePreview)
|
||||||
, _pixh(st::contactsPhotoSize)
|
, _pixh(st::contactsPhotoSize)
|
||||||
, _userId(userId) {
|
, _userId(data.userId)
|
||||||
history()->owner().registerContactView(userId, parent);
|
, _vcardBoxFactory(VcardBoxFactory(data.vcardItems)) {
|
||||||
|
history()->owner().registerContactView(data.userId, parent);
|
||||||
|
|
||||||
_nameLine.setText(
|
_nameLine.setText(
|
||||||
st::webPageTitleStyle,
|
st::webPageTitleStyle,
|
||||||
tr::lng_full_name(
|
tr::lng_full_name(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_first_name,
|
lt_first_name,
|
||||||
first,
|
data.firstName,
|
||||||
lt_last_name,
|
lt_last_name,
|
||||||
last).trimmed(),
|
data.lastName).trimmed(),
|
||||||
Ui::WebpageTextTitleOptions());
|
Ui::WebpageTextTitleOptions());
|
||||||
|
|
||||||
_phoneLine.setText(
|
_phoneLine.setText(
|
||||||
st::webPageDescriptionStyle,
|
st::webPageDescriptionStyle,
|
||||||
Ui::FormatPhone(phone),
|
Ui::FormatPhone(data.phoneNumber),
|
||||||
Ui::WebpageTextTitleOptions());
|
Ui::WebpageTextTitleOptions());
|
||||||
|
|
||||||
#if 0 // No info.
|
#if 0 // No info.
|
||||||
|
@ -188,16 +276,22 @@ QSize Contact::countOptimalSize() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_mainButton.link = _buttons.front().link;
|
_mainButton.link = _buttons.front().link;
|
||||||
} else {
|
} else if (const auto vcardBoxFactory = _vcardBoxFactory) {
|
||||||
#if 0 // Can't view contact.
|
const auto view = tr::lng_contact_details_button(tr::now).toUpper();
|
||||||
const auto view = tr::lng_profile_add_contact(tr::now).toUpper();
|
|
||||||
_buttons.push_back({
|
_buttons.push_back({
|
||||||
view,
|
view,
|
||||||
st::semiboldFont->width(view),
|
st::semiboldFont->width(view),
|
||||||
AddContactClickHandler(_parent->data()),
|
AddContactClickHandler(_parent->data()),
|
||||||
});
|
});
|
||||||
#endif
|
_mainButton.link = std::make_shared<LambdaClickHandler>([=](
|
||||||
_mainButton.link = nullptr;
|
const ClickContext &context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
if (const auto controller = my.sessionWindow.get()) {
|
||||||
|
controller->uiShow()->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
vcardBoxFactory(box);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto padding = inBubblePadding() + innerMargin();
|
const auto padding = inBubblePadding() + innerMargin();
|
||||||
|
|
|
@ -10,8 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/media/history_view_media.h"
|
#include "history/view/media/history_view_media.h"
|
||||||
#include "ui/userpic_view.h"
|
#include "ui/userpic_view.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct SharedContact;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class EmptyUserpic;
|
class EmptyUserpic;
|
||||||
|
class GenericBox;
|
||||||
class RippleAnimation;
|
class RippleAnimation;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
@ -21,10 +26,7 @@ class Contact final : public Media {
|
||||||
public:
|
public:
|
||||||
Contact(
|
Contact(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
UserId userId,
|
const Data::SharedContact &data);
|
||||||
const QString &first,
|
|
||||||
const QString &last,
|
|
||||||
const QString &phone);
|
|
||||||
~Contact();
|
~Contact();
|
||||||
|
|
||||||
void draw(Painter &p, const PaintContext &context) const override;
|
void draw(Painter &p, const PaintContext &context) const override;
|
||||||
|
@ -76,6 +78,8 @@ private:
|
||||||
Ui::Text::String _phoneLine;
|
Ui::Text::String _phoneLine;
|
||||||
Ui::Text::String _infoLine;
|
Ui::Text::String _infoLine;
|
||||||
|
|
||||||
|
Fn<void(not_null<Ui::GenericBox*>)> _vcardBoxFactory;
|
||||||
|
|
||||||
struct Button {
|
struct Button {
|
||||||
QString text;
|
QString text;
|
||||||
int width = 0;
|
int width = 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue