Added initial support of vcard from media contacts.
This commit is contained in:
parent
1656a9c3e2
commit
47ce34e987
|
@ -5240,6 +5240,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_search_tab_no_results_retry" = "Try another hashtag.";
|
||||
"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
|
||||
|
||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||
|
|
|
@ -1224,14 +1224,17 @@ MediaContact::MediaContact(
|
|||
UserId userId,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
const QString &phoneNumber)
|
||||
: Media(parent) {
|
||||
const QString &phoneNumber,
|
||||
const SharedContact::VcardItems &vcardItems)
|
||||
: Media(parent)
|
||||
, _contact(SharedContact{
|
||||
.userId = userId,
|
||||
.firstName = firstName,
|
||||
.lastName = lastName,
|
||||
.phoneNumber = phoneNumber,
|
||||
.vcardItems = vcardItems,
|
||||
}) {
|
||||
parent->history()->owner().registerContactItem(userId, parent);
|
||||
|
||||
_contact.userId = userId;
|
||||
_contact.firstName = firstName;
|
||||
_contact.lastName = lastName;
|
||||
_contact.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
MediaContact::~MediaContact() {
|
||||
|
@ -1246,7 +1249,8 @@ std::unique_ptr<Media> MediaContact::clone(not_null<HistoryItem*> parent) {
|
|||
_contact.userId,
|
||||
_contact.firstName,
|
||||
_contact.lastName,
|
||||
_contact.phoneNumber);
|
||||
_contact.phoneNumber,
|
||||
_contact.vcardItems);
|
||||
}
|
||||
|
||||
const SharedContact *MediaContact::sharedContact() const {
|
||||
|
@ -1301,12 +1305,7 @@ std::unique_ptr<HistoryView::Media> MediaContact::createView(
|
|||
not_null<HistoryView::Element*> message,
|
||||
not_null<HistoryItem*> realParent,
|
||||
HistoryView::Element *replacing) {
|
||||
return std::make_unique<HistoryView::Contact>(
|
||||
message,
|
||||
_contact.userId,
|
||||
_contact.firstName,
|
||||
_contact.lastName,
|
||||
_contact.phoneNumber);
|
||||
return std::make_unique<HistoryView::Contact>(message, _contact);
|
||||
}
|
||||
|
||||
MediaLocation::MediaLocation(
|
||||
|
|
|
@ -47,11 +47,30 @@ enum class CallFinishReason : char {
|
|||
Hangup,
|
||||
};
|
||||
|
||||
struct SharedContact {
|
||||
struct SharedContact final {
|
||||
UserId userId = 0;
|
||||
QString firstName;
|
||||
QString lastName;
|
||||
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 {
|
||||
|
@ -308,7 +327,8 @@ public:
|
|||
UserId userId,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
const QString &phoneNumber);
|
||||
const QString &phoneNumber,
|
||||
const SharedContact::VcardItems &vcardItems);
|
||||
~MediaContact();
|
||||
|
||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||
|
|
|
@ -211,12 +211,59 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
|
|||
const MTPMessageMedia &media) {
|
||||
using Result = std::unique_ptr<Data::Media>;
|
||||
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>(
|
||||
item,
|
||||
media.vuser_id().v,
|
||||
qs(media.vfirst_name()),
|
||||
qs(media.vlast_name()),
|
||||
qs(media.vphone_number()));
|
||||
qs(media.vphone_number()),
|
||||
vcardItems);
|
||||
}, [&](const MTPDmessageMediaGeo &media) -> Result {
|
||||
return media.vgeo().match([&](const MTPDgeoPoint &point) -> Result {
|
||||
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 "core/click_handler_types.h" // ClickHandlerContext
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.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 "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/power_saving.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#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 "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
@ -103,33 +108,116 @@ ClickHandlerPtr AddContactClickHandler(not_null<HistoryItem*> item) {
|
|||
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
|
||||
|
||||
Contact::Contact(
|
||||
not_null<Element*> parent,
|
||||
UserId userId,
|
||||
const QString &first,
|
||||
const QString &last,
|
||||
const QString &phone)
|
||||
const Data::SharedContact &data)
|
||||
: Media(parent)
|
||||
, _st(st::historyPagePreview)
|
||||
, _pixh(st::contactsPhotoSize)
|
||||
, _userId(userId) {
|
||||
history()->owner().registerContactView(userId, parent);
|
||||
, _userId(data.userId)
|
||||
, _vcardBoxFactory(VcardBoxFactory(data.vcardItems)) {
|
||||
history()->owner().registerContactView(data.userId, parent);
|
||||
|
||||
_nameLine.setText(
|
||||
st::webPageTitleStyle,
|
||||
tr::lng_full_name(
|
||||
tr::now,
|
||||
lt_first_name,
|
||||
first,
|
||||
data.firstName,
|
||||
lt_last_name,
|
||||
last).trimmed(),
|
||||
data.lastName).trimmed(),
|
||||
Ui::WebpageTextTitleOptions());
|
||||
|
||||
_phoneLine.setText(
|
||||
st::webPageDescriptionStyle,
|
||||
Ui::FormatPhone(phone),
|
||||
Ui::FormatPhone(data.phoneNumber),
|
||||
Ui::WebpageTextTitleOptions());
|
||||
|
||||
#if 0 // No info.
|
||||
|
@ -188,16 +276,22 @@ QSize Contact::countOptimalSize() {
|
|||
});
|
||||
}
|
||||
_mainButton.link = _buttons.front().link;
|
||||
} else {
|
||||
#if 0 // Can't view contact.
|
||||
const auto view = tr::lng_profile_add_contact(tr::now).toUpper();
|
||||
} else if (const auto vcardBoxFactory = _vcardBoxFactory) {
|
||||
const auto view = tr::lng_contact_details_button(tr::now).toUpper();
|
||||
_buttons.push_back({
|
||||
view,
|
||||
st::semiboldFont->width(view),
|
||||
AddContactClickHandler(_parent->data()),
|
||||
});
|
||||
#endif
|
||||
_mainButton.link = nullptr;
|
||||
_mainButton.link = std::make_shared<LambdaClickHandler>([=](
|
||||
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();
|
||||
|
|
|
@ -10,8 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/media/history_view_media.h"
|
||||
#include "ui/userpic_view.h"
|
||||
|
||||
namespace Data {
|
||||
struct SharedContact;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class EmptyUserpic;
|
||||
class GenericBox;
|
||||
class RippleAnimation;
|
||||
} // namespace Ui
|
||||
|
||||
|
@ -21,10 +26,7 @@ class Contact final : public Media {
|
|||
public:
|
||||
Contact(
|
||||
not_null<Element*> parent,
|
||||
UserId userId,
|
||||
const QString &first,
|
||||
const QString &last,
|
||||
const QString &phone);
|
||||
const Data::SharedContact &data);
|
||||
~Contact();
|
||||
|
||||
void draw(Painter &p, const PaintContext &context) const override;
|
||||
|
@ -76,6 +78,8 @@ private:
|
|||
Ui::Text::String _phoneLine;
|
||||
Ui::Text::String _infoLine;
|
||||
|
||||
Fn<void(not_null<Ui::GenericBox*>)> _vcardBoxFactory;
|
||||
|
||||
struct Button {
|
||||
QString text;
|
||||
int width = 0;
|
||||
|
|
Loading…
Reference in New Issue