From 41c8df029a66c1a04f17369cff233ad78f6def86 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 31 May 2016 22:27:11 +0300 Subject: [PATCH] New profile blocks started. Info block fully ready. All block widgets added (currently empty). About text and phone number PeerUpdateFlag added for observers. --- Telegram/Resources/langs/lang.strings | 10 +- Telegram/SourceFiles/apiwrap.cpp | 4 +- Telegram/SourceFiles/app.cpp | 23 +- Telegram/SourceFiles/boxes/aboutbox.cpp | 6 +- Telegram/SourceFiles/boxes/addcontactbox.cpp | 33 ++- Telegram/SourceFiles/boxes/addcontactbox.h | 6 +- Telegram/SourceFiles/boxes/confirmbox.cpp | 7 +- Telegram/SourceFiles/boxes/confirmbox.h | 2 +- Telegram/SourceFiles/core/click_handler.h | 1 + .../SourceFiles/core/click_handler_types.cpp | 25 +- .../SourceFiles/core/click_handler_types.h | 18 ++ Telegram/SourceFiles/historywidget.cpp | 2 +- Telegram/SourceFiles/intro/introphone.cpp | 2 +- Telegram/SourceFiles/intro/introstart.cpp | 2 +- Telegram/SourceFiles/localstorage.cpp | 5 +- Telegram/SourceFiles/mainwidget.cpp | 13 +- Telegram/SourceFiles/observer_peer.h | 2 + Telegram/SourceFiles/profile/profile.style | 28 ++- .../profile/profile_actions_widget.cpp | 40 ++++ .../profile/profile_actions_widget.h | 37 +++ .../profile/profile_block_widget.cpp | 8 +- .../profile/profile_block_widget.h | 11 + .../SourceFiles/profile/profile_cover.cpp | 97 ++++---- Telegram/SourceFiles/profile/profile_cover.h | 14 +- .../SourceFiles/profile/profile_fixed_bar.cpp | 4 +- .../profile/profile_info_widget.cpp | 217 ++++++++++++++++++ .../SourceFiles/profile/profile_info_widget.h | 72 ++++++ .../profile/profile_inner_widget.cpp | 168 +++++++++++++- .../profile/profile_inner_widget.h | 35 ++- .../profile/profile_invite_link_widget.cpp | 40 ++++ .../profile/profile_invite_link_widget.h | 37 +++ .../profile/profile_members_widget.cpp | 51 ++++ .../profile/profile_members_widget.h | 47 ++++ .../profile/profile_settings_widget.cpp | 40 ++++ .../profile/profile_settings_widget.h | 37 +++ .../profile/profile_shared_media_widget.cpp | 40 ++++ .../profile/profile_shared_media_widget.h | 37 +++ Telegram/SourceFiles/profilewidget.cpp | 26 +-- Telegram/SourceFiles/settingswidget.cpp | 2 +- Telegram/SourceFiles/structs.cpp | 26 ++- Telegram/SourceFiles/structs.h | 23 +- Telegram/SourceFiles/ui/flatinput.cpp | 4 +- Telegram/SourceFiles/ui/flatlabel.cpp | 87 +++++-- Telegram/SourceFiles/ui/flatlabel.h | 24 +- Telegram/SourceFiles/ui/twidget.h | 14 ++ .../SourceFiles/window/top_bar_widget.cpp | 2 +- Telegram/Telegram.vcxproj | 18 +- Telegram/Telegram.vcxproj.filters | 60 ++++- 48 files changed, 1342 insertions(+), 165 deletions(-) create mode 100644 Telegram/SourceFiles/profile/profile_actions_widget.cpp create mode 100644 Telegram/SourceFiles/profile/profile_actions_widget.h create mode 100644 Telegram/SourceFiles/profile/profile_info_widget.cpp create mode 100644 Telegram/SourceFiles/profile/profile_info_widget.h create mode 100644 Telegram/SourceFiles/profile/profile_invite_link_widget.cpp create mode 100644 Telegram/SourceFiles/profile/profile_invite_link_widget.h create mode 100644 Telegram/SourceFiles/profile/profile_members_widget.cpp create mode 100644 Telegram/SourceFiles/profile/profile_members_widget.h create mode 100644 Telegram/SourceFiles/profile/profile_settings_widget.cpp create mode 100644 Telegram/SourceFiles/profile/profile_settings_widget.h create mode 100644 Telegram/SourceFiles/profile/profile_shared_media_widget.cpp create mode 100644 Telegram/SourceFiles/profile/profile_shared_media_widget.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7b9adcfee..041fd7827 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -405,12 +405,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_profile_actions_section" = "Actions"; "lng_profile_bot_settings" = "Settings"; "lng_profile_bot_help" = "Help"; +"lng_profile_invite_link_section" = "Invite link"; "lng_profile_create_public_link" = "Create public link"; "lng_profile_edit_public_link" = "Edit public link"; "lng_profile_participants_section" = "Members"; -"lng_profile_info" = "Contact info"; -"lng_profile_group_info" = "Group info"; -"lng_profile_channel_info" = "Channel info"; +"lng_profile_info_section" = "Info"; +"lng_profile_mobile_number" = "Mobile:"; +"lng_profile_username" = "Username:"; +"lng_profile_link" = "Link:"; "lng_profile_add_contact" = "Add Contact"; "lng_profile_edit_contact" = "Edit"; "lng_profile_enable_notifications" = "Notifications"; @@ -912,6 +914,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org // Not used "lng_topbar_info" = "Info"; +"lng_profile_group_info" = "Group info"; +"lng_profile_channel_info" = "Channel info"; // Wnd specific diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 751757c63..11f67b836 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -276,7 +276,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt } break; } } - channel->about = qs(f.vabout); + channel->setAbout(qs(f.vabout)); int32 newCount = f.has_participants_count() ? f.vparticipants_count.v : 0; if (newCount != channel->count) { if (channel->isMegagroup() && !channel->mgInfo->lastParticipants.isEmpty()) { @@ -343,7 +343,7 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestI peer->asUser()->setBotInfoVersion(-1); } peer->asUser()->blocked = d.is_blocked() ? UserIsBlocked : UserIsNotBlocked; - peer->asUser()->about = d.has_about() ? qs(d.vabout) : QString(); + peer->asUser()->setAbout(d.has_about() ? qs(d.vabout) : QString()); if (req) { QMap::iterator i = _fullPeerRequests.find(peer); diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index be22abcf3..c157dddc8 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -428,7 +428,10 @@ namespace { } } if (d.is_deleted()) { - data->setPhone(QString()); + if (!data->phone().isEmpty()) { + data->setPhone(QString()); + update.flags |= UpdateFlag::UserPhoneChanged; + } data->setNameDelayed(lang(lng_deleted), QString(), QString(), QString()); data->setPhoto(MTP_userProfilePhotoEmpty()); data->access = UserNoAccess; @@ -440,12 +443,14 @@ namespace { QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? textOneLine(qs(d.vfirst_name)) : QString()) : data->firstName; QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString()) : data->lastName; - QString phone = minimal ? data->phone : (d.has_phone() ? qs(d.vphone) : QString()); + QString phone = minimal ? data->phone() : (d.has_phone() ? qs(d.vphone) : QString()); QString uname = minimal ? data->username : (d.has_username() ? textOneLine(qs(d.vusername)) : QString()); - bool phoneChanged = (data->phone != phone); - if (phoneChanged) data->setPhone(phone); - + bool phoneChanged = (data->phone() != phone); + if (phoneChanged) { + data->setPhone(phone); + update.flags |= UpdateFlag::UserPhoneChanged; + } bool nameChanged = (data->firstName != fname) || (data->lastName != lname); bool showPhone = !isServiceUser(data->id) && !d.is_self() && !d.is_contact() && !d.is_mutual_contact(); @@ -480,7 +485,7 @@ namespace { } else { data->setBotInfoVersion(-1); } - data->contact = (d.is_contact() || d.is_mutual_contact()) ? 1 : (data->phone.isEmpty() ? -1 : 0); + data->contact = (d.is_contact() || d.is_mutual_contact()) ? 1 : (data->phone().isEmpty() ? -1 : 0); if (data->contact == 1 && cReportSpamStatuses().value(data->id, dbiprsHidden) != dbiprsHidden) { cRefReportSpamStatuses().insert(data->id, dbiprsHidden); Local::writeReportSpamStatuses(); @@ -522,7 +527,7 @@ namespace { case mtpc_userStatusOnline: data->onlineTill = status->c_userStatusOnline().vexpires.v; break; } - if (data->contact < 0 && !data->phone.isEmpty() && peerToUser(data->id) != MTP::authedId()) { + if (data->contact < 0 && !data->phone().isEmpty() && peerToUser(data->id) != MTP::authedId()) { data->contact = 0; } if (App::main()) { @@ -1259,7 +1264,7 @@ namespace { break; } if (user->contact < 1) { - if (user->contact < 0 && !user->phone.isEmpty() && peerToUser(user->id) != MTP::authedId()) { + if (user->contact < 0 && !user->phone().isEmpty() && peerToUser(user->id) != MTP::authedId()) { user->contact = 0; } } @@ -1276,7 +1281,7 @@ namespace { bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact; bool showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone)); if (showPhoneChanged) { - user->setNameDelayed(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone) : QString(), textOneLine(user->username)); + user->setNameDelayed(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone()) : QString(), textOneLine(user->username)); } markPeerUpdated(user); } diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp index c5bc8cdf5..7e64bd52d 100644 --- a/Telegram/SourceFiles/boxes/aboutbox.cpp +++ b/Telegram/SourceFiles/boxes/aboutbox.cpp @@ -32,9 +32,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org AboutBox::AboutBox() : AbstractBox(st::aboutWidth) , _version(this, lng_about_version(lt_version, QString::fromLatin1(AppVersionStr.c_str()) + (cAlphaVersion() ? " alpha" : "") + (cBetaVersion() ? qsl(" beta %1").arg(cBetaVersion()) : QString())), st::aboutVersionLink) -, _text1(this, lang(lng_about_text_1), st::aboutLabel, st::aboutTextStyle) -, _text2(this, lang(lng_about_text_2), st::aboutLabel, st::aboutTextStyle) -, _text3(this, QString(), st::aboutLabel, st::aboutTextStyle) +, _text1(this, lang(lng_about_text_1), FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle) +, _text2(this, lang(lng_about_text_2), FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle) +, _text3(this,st::aboutLabel, st::aboutTextStyle) , _done(this, lang(lng_close), st::defaultBoxButton) { _text3.setRichText(lng_about_text_3(lt_faq_open, qsl("[a href=\"%1\"]").arg(telegramFaqLink()), lt_faq_close, qsl("[/a]"))); diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index f1bea515d..abe636529 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -33,16 +33,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "observer_peer.h" AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : AbstractBox(st::boxWidth) -, _user(0) , _save(this, lang(lng_add_contact), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _retry(this, lang(lng_try_other_contact), st::defaultBoxButton) , _first(this, st::defaultInputField, lang(lng_signup_firstname), fname) , _last(this, st::defaultInputField, lang(lng_signup_lastname), lname) , _phone(this, st::defaultInputField, lang(lng_contact_phone), phone) -, _invertOrder(langFirstNameGoesSecond()) -, _contactId(0) -, _addRequest(0) { +, _invertOrder(langFirstNameGoesSecond()) { if (!phone.isEmpty()) { _phone.setDisabled(true); } @@ -57,10 +54,8 @@ AddContactBox::AddContactBox(UserData *user) : AbstractBox(st::boxWidth) , _retry(this, lang(lng_try_other_contact), st::defaultBoxButton) , _first(this, st::defaultInputField, lang(lng_signup_firstname), user->firstName) , _last(this, st::defaultInputField, lang(lng_signup_lastname), user->lastName) -, _phone(this, st::defaultInputField, lang(lng_contact_phone), user->phone) -, _invertOrder(langFirstNameGoesSecond()) -, _contactId(0) -, _addRequest(0) { +, _phone(this, st::defaultInputField, lang(lng_contact_phone), user->phone()) +, _invertOrder(langFirstNameGoesSecond()) { _phone.setDisabled(true); initBox(); } @@ -191,7 +186,7 @@ void AddContactBox::onSave() { _sentName = firstName; if (_user) { _contactId = rand_value(); - QVector v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(_user->phone), MTP_string(firstName), MTP_string(lastName))); + QVector v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(_user->phone()), MTP_string(firstName), MTP_string(lastName))); _addRequest = MTP::send(MTPcontacts_ImportContacts(MTP_vector(v), MTP_bool(false)), rpcDone(&AddContactBox::onSaveUserDone), rpcFail(&AddContactBox::onSaveUserFail)); } else { _contactId = rand_value(); @@ -1181,7 +1176,7 @@ EditChannelBox::EditChannelBox(ChannelData *channel) : AbstractBox() , _save(this, lang(lng_settings_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _title(this, st::defaultInputField, lang(lng_dlg_new_channel_name), _channel->name) -, _description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about) +, _description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about()) , _sign(this, lang(lng_edit_sign_messages), channel->addsSignature()) , _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::defaultBoxLinkButton) , _saveTitleRequestId(0) @@ -1322,7 +1317,7 @@ void EditChannelBox::onPublicLink() { } void EditChannelBox::saveDescription() { - if (_sentDescription == _channel->about) { + if (_sentDescription == _channel->about()) { saveSign(); } else { _saveDescriptionRequestId = MTP::send(MTPchannels_EditAbout(_channel->inputChannel, MTP_string(_sentDescription)), rpcDone(&EditChannelBox::onSaveDescriptionDone), rpcFail(&EditChannelBox::onSaveFail)); @@ -1357,9 +1352,11 @@ bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) { } else if (req == _saveDescriptionRequestId) { _saveDescriptionRequestId = 0; if (err == qstr("CHAT_ABOUT_NOT_MODIFIED")) { - _channel->about = _sentDescription; - if (App::api()) { - emit App::api()->fullPeerUpdated(_channel); + if (_channel->setAbout(_sentDescription)) { + if (App::api()) { + emit App::api()->fullPeerUpdated(_channel); + } + Notify::peerUpdatedSendDelayed(); } saveSign(); return true; @@ -1386,9 +1383,11 @@ void EditChannelBox::onSaveTitleDone(const MTPUpdates &updates) { void EditChannelBox::onSaveDescriptionDone(const MTPBool &result) { _saveDescriptionRequestId = 0; - _channel->about = _sentDescription; - if (App::api()) { - emit App::api()->fullPeerUpdated(_channel); + if (_channel->setAbout(_sentDescription)) { + if (App::api()) { + emit App::api()->fullPeerUpdated(_channel); + } + Notify::peerUpdatedSendDelayed(); } saveSign(); } diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index 90e55311c..e70b1ac93 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -57,7 +57,7 @@ private: void initBox(); - UserData *_user; + UserData *_user = nullptr; QString _boxTitle; BoxButton _save, _cancel, _retry; @@ -66,9 +66,9 @@ private: bool _invertOrder; - uint64 _contactId; + uint64 _contactId = 0; - mtpRequestId _addRequest; + mtpRequestId _addRequest = 0; QString _sentName; }; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index fcd6b5e1d..f4c044e53 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -381,11 +381,10 @@ void ConvertToSupergroupBox::resizeEvent(QResizeEvent *e) { PinMessageBox::PinMessageBox(ChannelData *channel, MsgId msgId) : AbstractBox(st::boxWidth) , _channel(channel) , _msgId(msgId) -, _text(this, lang(lng_pinned_pin_sure), st::boxLabel) +, _text(this, lang(lng_pinned_pin_sure), FlatLabel::InitType::Simple, st::boxLabel) , _notify(this, lang(lng_pinned_notify), true) , _pin(this, lang(lng_pinned_pin), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _requestId(0) { +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { _text.resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()); setMaxHeight(st::boxPadding.top() + _text.height() + st::boxMediumSkip + _notify.height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _pin.height() + st::boxButtonPadding.bottom()); @@ -441,7 +440,7 @@ RichDeleteMessageBox::RichDeleteMessageBox(ChannelData *channel, UserData *from, , _channel(channel) , _from(from) , _msgId(msgId) -, _text(this, lang(lng_selected_delete_sure_this), st::boxLabel) +, _text(this, lang(lng_selected_delete_sure_this), FlatLabel::InitType::Simple, st::boxLabel) , _banUser(this, lang(lng_ban_user), false) , _reportSpam(this, lang(lng_report_spam), false) , _deleteAll(this, lang(lng_delete_all_from), false) diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index 4af3c49af..e7af61470 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -217,7 +217,7 @@ private: BoxButton _pin, _cancel; - mtpRequestId _requestId; + mtpRequestId _requestId = 0; }; diff --git a/Telegram/SourceFiles/core/click_handler.h b/Telegram/SourceFiles/core/click_handler.h index c0199fac9..8ceadfb81 100644 --- a/Telegram/SourceFiles/core/click_handler.h +++ b/Telegram/SourceFiles/core/click_handler.h @@ -27,6 +27,7 @@ enum ExpandLinksMode { ExpandLinksNone, ExpandLinksShortened, ExpandLinksAll, + ExpandLinksUrlOnly, // For custom urls leaves only url instead of text. }; class ClickHandlerHost { diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 05043abf2..23b34fb76 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -110,15 +110,22 @@ QString HiddenUrlClickHandler::getExpandedLinkText(ExpandLinksMode mode, const Q QString result; if (mode == ExpandLinksAll) { result = textPart.toString() + qsl(" (") + url() + ')'; + } else if (mode == ExpandLinksUrlOnly) { + result = url(); } return result; } TextWithEntities HiddenUrlClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const { TextWithEntities result; - result.entities.push_back({ EntityInTextCustomUrl, entityOffset, textPart.size(), url() }); - if (mode == ExpandLinksAll) { - result.text = textPart.toString() + qsl(" (") + url() + ')'; + if (mode == ExpandLinksUrlOnly) { + result.text = url(); + result.entities.push_back({ EntityInTextUrl, entityOffset, result.text.size() }); + } else { + result.entities.push_back({ EntityInTextCustomUrl, entityOffset, textPart.size(), url() }); + if (mode == ExpandLinksAll) { + result.text = textPart.toString() + qsl(" (") + url() + ')'; + } } return result; } @@ -174,9 +181,19 @@ TextWithEntities HashtagClickHandler::getExpandedLinkTextWithEntities(ExpandLink return simpleTextWithEntity({ EntityInTextHashtag, entityOffset, textPart.size() }); } +PeerData *BotCommandClickHandler::_peer = nullptr; +UserData *BotCommandClickHandler::_bot = nullptr; void BotCommandClickHandler::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton || button == Qt::MiddleButton) { - if (PeerData *peer = Ui::getPeerForMouseAction()) { + if (auto peer = peerForCommand()) { + if (auto bot = peer->isUser() ? peer->asUser() : botForCommand()) { + Ui::showPeerHistory(peer, ShowAtTheEndMsgId); + App::sendBotCommand(peer, bot, _cmd); + return; + } + } + + if (auto peer = Ui::getPeerForMouseAction()) { // old way UserData *bot = peer->isUser() ? peer->asUser() : nullptr; if (auto item = App::hoveredLinkItem()) { if (!bot) { diff --git a/Telegram/SourceFiles/core/click_handler_types.h b/Telegram/SourceFiles/core/click_handler_types.h index d25078278..84deafe77 100644 --- a/Telegram/SourceFiles/core/click_handler_types.h +++ b/Telegram/SourceFiles/core/click_handler_types.h @@ -193,6 +193,8 @@ private: }; +class PeerData; +class UserData; class BotCommandClickHandler : public TextClickHandler { public: BotCommandClickHandler(const QString &cmd) : _cmd(cmd) { @@ -204,14 +206,30 @@ public: return _cmd; } + static void setPeerForCommand(PeerData *peer) { + _peer = peer; + } + static void setBotForCommand(UserData *bot) { + _bot = bot; + } + TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override; protected: QString url() const override { return _cmd; } + static PeerData *peerForCommand() { + return _peer; + } + static UserData *botForCommand() { + return _bot; + } private: QString _cmd; + static PeerData *_peer; + static UserData *_bot; + }; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 4375ede39..3e41bac0a 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -5034,7 +5034,7 @@ void HistoryWidget::onBroadcastSilentChange() { } void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) { - auto phone = contact->phone; + auto phone = contact->phone(); if (phone.isEmpty()) phone = App::phoneFromSharedContact(peerToUser(contact->id)); if (!contact || phone.isEmpty()) return; diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index e5fcb4956..48cc83f8b 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -50,7 +50,7 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent) , country(this, st::introCountry) , phone(this, st::inpIntroPhone) , code(this, st::inpIntroCountryCode) -, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), st::introErrLabel, st::introErrLabelTextStyle) +, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), FlatLabel::InitType::Rich, st::introErrLabel, st::introErrLabelTextStyle) , _showSignup(false) , sentRequest(0) { setVisible(false); diff --git a/Telegram/SourceFiles/intro/introstart.cpp b/Telegram/SourceFiles/intro/introstart.cpp index 8753300fc..bc3c520ff 100644 --- a/Telegram/SourceFiles/intro/introstart.cpp +++ b/Telegram/SourceFiles/intro/introstart.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "langloaderplain.h" IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent) -, _intro(this, lang(lng_intro), st::introLabel, st::introLabelTextStyle) +, _intro(this, lang(lng_intro), FlatLabel::InitType::Rich, st::introLabel, st::introLabelTextStyle) , _changeLang(this, QString()) , _next(this, lang(lng_start_msgs), st::btnIntroNext) { _changeLang.hide(); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index a94231f4a..9127b6abb 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -3407,7 +3407,7 @@ namespace Local { UserData *user = peer->asUser(); // first + last + phone + username + access - result += Serialize::stringSize(user->firstName) + Serialize::stringSize(user->lastName) + Serialize::stringSize(user->phone) + Serialize::stringSize(user->username) + sizeof(quint64); + result += Serialize::stringSize(user->firstName) + Serialize::stringSize(user->lastName) + Serialize::stringSize(user->phone()) + Serialize::stringSize(user->username) + sizeof(quint64); // flags if (AppVersion >= 9012) { @@ -3436,7 +3436,7 @@ namespace Local { if (peer->isUser()) { UserData *user = peer->asUser(); - stream << user->firstName << user->lastName << user->phone << user->username << quint64(user->access); + stream << user->firstName << user->lastName << user->phone() << user->username << quint64(user->access); if (AppVersion >= 9012) { stream << qint32(user->flags); } @@ -3490,6 +3490,7 @@ namespace Local { QString pname = (showPhone && !phone.isEmpty()) ? App::formatPhone(phone) : QString(); if (!wasLoaded) { + user->setPhone(phone); user->setNameDelayed(first, last, pname, username); user->access = access; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 3a60bfb44..b3c12ede1 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -4389,9 +4389,16 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { case mtpc_updateUserPhone: { auto &d(update.c_updateUserPhone()); if (auto user = App::userLoaded(d.vuser_id.v)) { - user->setPhone(qs(d.vphone)); - user->setNameDelayed(user->firstName, user->lastName, (user->contact || isServiceUser(user->id) || user->isSelf() || user->phone.isEmpty()) ? QString() : App::formatPhone(user->phone), user->username); - App::markPeerUpdated(user); + auto newPhone = qs(d.vphone); + if (newPhone != user->phone()) { + user->setPhone(newPhone); + user->setNameDelayed(user->firstName, user->lastName, (user->contact || isServiceUser(user->id) || user->isSelf() || user->phone().isEmpty()) ? QString() : App::formatPhone(user->phone()), user->username); + App::markPeerUpdated(user); + + Notify::PeerUpdate update(user); + update.flags |= Notify::PeerUpdateFlag::UserPhoneChanged; + Notify::peerUpdatedDelayed(update); + } } } break; diff --git a/Telegram/SourceFiles/observer_peer.h b/Telegram/SourceFiles/observer_peer.h index 06f07b73a..797dd562d 100644 --- a/Telegram/SourceFiles/observer_peer.h +++ b/Telegram/SourceFiles/observer_peer.h @@ -33,9 +33,11 @@ enum class PeerUpdateFlag { NameChanged = 0x00000001U, UsernameChanged = 0x00000002U, PhotoChanged = 0x00000004U, + AboutChanged = 0x00000008U, UserCanShareContact = 0x00010000U, UserIsContact = 0x00020000U, + UserPhoneChanged = 0x00040000U, ChatCanEdit = 0x00010000U, diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index 76fc87208..cd0577934 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -37,7 +37,8 @@ profileFixedBarButton: flatButton(topBarButton) { profileMarginTop: 13px; profilePhotoSize: 112px; -profilePhotoLeft: 35px; +profilePhotoLeftMin: 18px; +profilePhotoLeftMax: 45px; profilePhotoDuration: 500; profileNameLeft: 26px; profileNameTop: 9px; @@ -106,7 +107,28 @@ profileDividerFill: icon { }; profileBlocksTop: 7px; +profileBlocksBottom: 20px; +profileBlockLeftMin: 8px; +profileBlockLeftMax: 25px; +profileBlockNarrowWidthMin: 220px; +profileBlockWideWidthMin: 300px; +profileBlockWideWidthMax: 340px; profileBlockMarginTop: 21px; -profileBlockTitleFont: semiboldFont; +profileBlockMarginRight: 10px; +profileBlockMarginBottom: 4px; +profileBlockTitleHeight: 22px; +profileBlockTitleFont: font(14px semibold); profileBlockTitleFg: black; -profileBlockTitlePosition: point(16px, profileBlockMarginTop); \ No newline at end of file +profileBlockTitlePosition: point(24px, -7px); +profileBlockLabel: flatLabel(labelDefFlat) { + textFg: windowSubTextFg; +} +profileBlockTextPart: flatLabel(labelDefFlat) { + width: 180px; + margin: margins(5px, 5px, 5px, 5px); +} +profileBlockOneLineTextPart: flatLabel(profileBlockTextPart) { + width: 0px; // No need to set minWidth in one-line text. + maxHeight: 20px; +} +profileBlockOneLineSkip: 9px; diff --git a/Telegram/SourceFiles/profile/profile_actions_widget.cpp b/Telegram/SourceFiles/profile/profile_actions_widget.cpp new file mode 100644 index 000000000..aec40e5c8 --- /dev/null +++ b/Telegram/SourceFiles/profile/profile_actions_widget.cpp @@ -0,0 +1,40 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "profile/profile_actions_widget.h" + +#include "styles/style_profile.h" +#include "lang.h" + +namespace Profile { + +ActionsWidget::ActionsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_actions_section)) +{ + show(); +} + +int ActionsWidget::resizeGetHeight(int newWidth) { + int newHeight = contentTop(); + + return newHeight; +} + +} // namespace Profile diff --git a/Telegram/SourceFiles/profile/profile_actions_widget.h b/Telegram/SourceFiles/profile/profile_actions_widget.h new file mode 100644 index 000000000..118c2d0d5 --- /dev/null +++ b/Telegram/SourceFiles/profile/profile_actions_widget.h @@ -0,0 +1,37 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "profile/profile_block_widget.h" + +namespace Profile { + +class ActionsWidget : public BlockWidget { +public: + ActionsWidget(QWidget *parent, PeerData *peer); + +protected: + // Resizes content and counts natural widget height for the desired width. + int resizeGetHeight(int newWidth) override; + +}; + +} // namespace Profile diff --git a/Telegram/SourceFiles/profile/profile_block_widget.cpp b/Telegram/SourceFiles/profile/profile_block_widget.cpp index abfe8453b..6548fbb61 100644 --- a/Telegram/SourceFiles/profile/profile_block_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_block_widget.cpp @@ -34,12 +34,18 @@ void BlockWidget::resizeToWidth(int newWidth) { resize(newWidth, resizeGetHeight(newWidth)); } +int BlockWidget::contentTop() const { + return st::profileBlockMarginTop + st::profileBlockTitleHeight; +} + void BlockWidget::paintEvent(QPaintEvent *e) { Painter p(this); p.setFont(st::profileBlockTitleFont); p.setPen(st::profileBlockTitleFg); - p.drawText(st::profileBlockTitlePosition, _title); + int titleLeft = st::profileBlockTitlePosition.x(); + int titleTop = st::profileBlockMarginTop + st::profileBlockTitlePosition.y(); + p.drawTextLeft(titleLeft, titleTop, width(), _title); paintContents(p); } diff --git a/Telegram/SourceFiles/profile/profile_block_widget.h b/Telegram/SourceFiles/profile/profile_block_widget.h index 313e0d747..8607aa0b5 100644 --- a/Telegram/SourceFiles/profile/profile_block_widget.h +++ b/Telegram/SourceFiles/profile/profile_block_widget.h @@ -35,14 +35,25 @@ public: virtual void setVisibleTopBottom(int visibleTop, int visibleBottom) { } +signals: + void heightUpdated(); + protected: void paintEvent(QPaintEvent *e) override; virtual void paintContents(Painter &p) { } + // Where does the block content start (after the title). + int contentTop() const; + // Resizes content and counts natural widget height for the desired width. virtual int resizeGetHeight(int newWidth) = 0; + void contentSizeUpdated() { + resizeToWidth(width()); + emit heightUpdated(); + } + PeerData *peer() const { return _peer; } diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp index fd8937987..8ed491b80 100644 --- a/Telegram/SourceFiles/profile/profile_cover.cpp +++ b/Telegram/SourceFiles/profile/profile_cover.cpp @@ -80,7 +80,7 @@ CoverWidget::CoverWidget(QWidget *parent, PeerData *peer) : TWidget(parent) , _peerChannel(peer->asChannel()) , _peerMegagroup(peer->isMegagroup() ? _peerChannel : nullptr) , _userpicButton(this, peer) -, _name(this, QString(), st::profileNameLabel) { +, _name(this, st::profileNameLabel) { setAttribute(Qt::WA_OpaquePaintEvent); setAcceptDrops(true); @@ -109,20 +109,23 @@ void CoverWidget::onPhotoShow() { } } +int CoverWidget::countPhotoLeft(int newWidth) const { + int result = st::profilePhotoLeftMin; + result += (newWidth - st::wndMinWidth) / 2; + return qMin(result, st::profilePhotoLeftMax); +} + void CoverWidget::resizeToWidth(int newWidth) { int newHeight = 0; newHeight += st::profileMarginTop; - _userpicButton->moveToLeft(st::profilePhotoLeft, newHeight); + + _photoLeft = countPhotoLeft(newWidth); + _userpicButton->moveToLeft(_photoLeft, newHeight); + + refreshNameGeometry(newWidth); int infoLeft = _userpicButton->x() + _userpicButton->width(); - int nameLeft = infoLeft + st::profileNameLeft - st::profileNameLabel.margin.left(); - int nameTop = _userpicButton->y() + st::profileNameTop - st::profileNameLabel.margin.top(); - _name.moveToLeft(nameLeft, nameTop); - int nameWidth = newWidth - infoLeft - st::profileNameLeft - st::profileButtonSkip; - nameWidth += st::profileNameLabel.margin.left() + st::profileNameLabel.margin.right(); - _name.resizeToWidth(nameWidth); - _statusPosition = QPoint(infoLeft + st::profileStatusLeft, _userpicButton->y() + st::profileStatusTop); moveAndToggleButtons(newWidth); @@ -140,29 +143,43 @@ void CoverWidget::resizeToWidth(int newWidth) { update(); } +void CoverWidget::refreshNameGeometry(int newWidth) { + int infoLeft = _userpicButton->x() + _userpicButton->width(); + int nameLeft = infoLeft + st::profileNameLeft - st::profileNameLabel.margin.left(); + int nameTop = _userpicButton->y() + st::profileNameTop - st::profileNameLabel.margin.top(); + int nameWidth = newWidth - infoLeft - st::profileNameLeft - st::profileButtonSkip; + int marginsAdd = st::profileNameLabel.margin.left() + st::profileNameLabel.margin.right(); + _name.resizeToWidth(qMin(nameWidth, _name.naturalWidth()) + marginsAdd); + _name.moveToLeft(nameLeft, nameTop); +} + // A more generic solution would be allowing an optional icon button // for each text button. But currently we use only one, so it is done easily: // There can be primary + secondary + icon buttons. If primary + secondary fit, // then icon is hidden, otherwise secondary is hidden and icon is shown. void CoverWidget::moveAndToggleButtons(int newWiddth) { - bool showNextButton = true; - int buttonLeft = st::profilePhotoLeft + _userpicButton->width() + st::profileButtonLeft; + int buttonLeft = _userpicButton->x() + _userpicButton->width() + st::profileButtonLeft; int buttonsRight = newWiddth - st::profileButtonSkip; for (int i = 0, count = _buttons.size(); i < count; ++i) { - auto button = _buttons.at(i); - button->moveToLeft(buttonLeft, st::profileButtonTop); - if (i == 1) { - // If second button is not fitting. - if (buttonLeft + button->width() > buttonsRight) { - button->hide(); + auto &button = _buttons.at(i); + button.widget->moveToLeft(buttonLeft, st::profileButtonTop); + if (button.replacement) { + button.replacement->moveToLeft(buttonLeft, st::profileButtonTop); + if (buttonLeft + button.widget->width() > buttonsRight) { + button.widget->hide(); + button.replacement->show(); + buttonLeft += button.replacement->width() + st::profileButtonSkip; } else { - button->show(); - buttonLeft += button->width() + st::profileButtonSkip; - showNextButton = false; + button.widget->show(); + button.replacement->hide(); + buttonLeft += button.widget->width() + st::profileButtonSkip; } + } else if (i == 1 && (buttonLeft + button.widget->width() > buttonsRight)) { + // If second button is not fitting. + button.widget->hide(); } else { - button->setVisible(showNextButton); - buttonLeft += button->width() + st::profileButtonSkip; + button.widget->show(); + buttonLeft += button.widget->width() + st::profileButtonSkip; } } } @@ -172,7 +189,7 @@ void CoverWidget::showFinished() { } bool CoverWidget::shareContactButtonShown() const { - return _peerUser && (_buttons.size() > 1) && !(_buttons.at(1)->isHidden()); + return _peerUser && (_buttons.size() > 1) && !(_buttons.at(1).widget->isHidden()); } void CoverWidget::paintEvent(QPaintEvent *e) { @@ -195,8 +212,7 @@ void CoverWidget::resizeDropArea() { void CoverWidget::dropAreaHidden(CoverDropArea *dropArea) { if (_dropArea == dropArea) { - _dropArea->deleteLater(); - _dropArea = nullptr; + _dropArea.destroyDelayed(); } } @@ -314,7 +330,7 @@ void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) { void CoverWidget::refreshNameText() { _name.setText(App::peerName(_peer)); - update(); + refreshNameGeometry(width()); } void CoverWidget::refreshStatusText() { @@ -394,8 +410,7 @@ void CoverWidget::setUserButtons() { void CoverWidget::setChatButtons() { if (_peerChat->canEdit()) { addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto())); - addButton(lang(lng_profile_add_participant), SLOT(onAddMember())); - addButton(st::profileAddMemberButton, SLOT(onAddMember())); + addButton(lang(lng_profile_add_participant), SLOT(onAddMember()), &st::profileAddMemberButton); } } @@ -404,8 +419,7 @@ void CoverWidget::setMegagroupButtons() { addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto())); } if (_peerMegagroup->canAddParticipants()) { - addButton(lang(lng_profile_add_participant), SLOT(onAddMember())); - addButton(st::profileAddMemberButton, SLOT(onAddMember())); + addButton(lang(lng_profile_add_participant), SLOT(onAddMember()), &st::profileAddMemberButton); } } @@ -422,21 +436,24 @@ void CoverWidget::setChannelButtons() { void CoverWidget::clearButtons() { auto buttons = createAndSwap(_buttons); for_const (auto button, buttons) { - delete button; + delete button.widget; + delete button.replacement; } } -void CoverWidget::addButton(const QString &text, const char *slot) { +void CoverWidget::addButton(const QString &text, const char *slot, const style::BoxButton *replacementStyle) { auto &buttonStyle = _buttons.isEmpty() ? st::profilePrimaryButton : st::profileSecondaryButton; - _buttons.push_back(new Ui::RoundButton(this, text, buttonStyle)); - connect(_buttons.back(), SIGNAL(clicked()), this, slot); - _buttons.back()->show(); -} + auto button = new Ui::RoundButton(this, text, buttonStyle); + connect(button, SIGNAL(clicked()), this, slot); + button->show(); -void CoverWidget::addButton(const style::BoxButton &buttonStyle, const char *slot) { - _buttons.push_back(new Ui::RoundButton(this, QString(), buttonStyle)); - connect(_buttons.back(), SIGNAL(clicked()), this, slot); - _buttons.back()->hide(); + auto replacement = replacementStyle ? new Ui::RoundButton(this, QString(), *replacementStyle) : nullptr; + if (replacement) { + connect(replacement, SIGNAL(clicked()), this, slot); + replacement->hide(); + } + + _buttons.push_back({ button, replacement }); } void CoverWidget::onSendMessage() { diff --git a/Telegram/SourceFiles/profile/profile_cover.h b/Telegram/SourceFiles/profile/profile_cover.h index 47975485b..2a53e31ec 100644 --- a/Telegram/SourceFiles/profile/profile_cover.h +++ b/Telegram/SourceFiles/profile/profile_cover.h @@ -75,6 +75,10 @@ private: void notifyPeerUpdated(const Notify::PeerUpdate &update); void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update); + // Counts userpic button left offset for a new widget width. + int countPhotoLeft(int newWidth) const; + + void refreshNameGeometry(int newWidth); void moveAndToggleButtons(int newWiddth); void refreshNameText(); void refreshStatusText(); @@ -87,8 +91,7 @@ private: void setChannelButtons(); void clearButtons(); - void addButton(const QString &text, const char *slot); - void addButton(const style::BoxButton &buttonStyle, const char *slot); + void addButton(const QString &text, const char *slot, const style::BoxButton *replacementStyle = nullptr); void paintDivider(Painter &p); @@ -112,8 +115,13 @@ private: QPoint _statusPosition; QString _statusText; - QList _buttons; + struct Button { + Ui::RoundButton *widget; + Ui::RoundButton *replacement; + }; + QList