diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 07cb596cb..699ffb09f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -271,10 +271,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_username_link" = "This link opens a chat with you:"; "lng_username_copied" = "Link copied to clipboard."; +"lng_bio_title" = "Edit your bio"; +"lng_bio_placeholder" = "Bio"; +"lng_bio_about" = "You can add a few lines about yourself. Anyone who opens your profile will see this text."; + "lng_settings_section_info" = "Info"; "lng_settings_phone_number" = "Phone number:"; "lng_settings_username" = "Username:"; "lng_settings_choose_username" = "Choose username"; +"lng_settings_empty_bio" = "None"; "lng_settings_section_notify" = "Notifications"; "lng_settings_desktop_notify" = "Desktop notifications"; @@ -570,6 +575,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_profile_mobile_number" = "Mobile:"; "lng_profile_username" = "Username:"; "lng_profile_link" = "Link:"; +"lng_profile_bio" = "Bio:"; "lng_profile_add_contact" = "Add Contact"; "lng_profile_edit_contact" = "Edit"; "lng_profile_enable_notifications" = "Notifications"; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 441184ebb..0f61b356d 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -41,6 +41,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "observer_peer.h" #include "auth_session.h" +namespace { + +constexpr auto kMaxGroupChannelTitle = 255; +constexpr auto kMaxChannelDescription = 255; +constexpr auto kMaxBioLength = 70; + +style::InputField CreateBioFieldStyle() { + auto result = st::newGroupDescription; + result.textMargins.setRight(st::boxTextFont->spacew + st::boxTextFont->width(QString::number(kMaxBioLength))); + return result; +} + +} // namespace + class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender { public: Inner(QWidget *parent, base::lambda revokeCallback); @@ -290,12 +304,12 @@ GroupInfoBox::GroupInfoBox(QWidget*, CreatingGroupType creating, bool fromTypeCh void GroupInfoBox::prepare() { setMouseTracking(true); - _title->setMaxLength(MaxGroupChannelTitle); + _title->setMaxLength(kMaxGroupChannelTitle); if (_creating == CreatingGroupChannel) { _description.create(this, st::newGroupDescription, langFactory(lng_create_group_description)); _description->show(); - _description->setMaxLength(MaxChannelDescription); + _description->setMaxLength(kMaxChannelDescription); connect(_description, SIGNAL(resized()), this, SLOT(onDescriptionResized())); connect(_description, SIGNAL(submitted(bool)), this, SLOT(onNext())); @@ -828,8 +842,8 @@ void EditNameTitleBox::prepare() { if (_invertOrder) { setTabOrder(_last, _first); } - _first->setMaxLength(MaxGroupChannelTitle); - _last->setMaxLength(MaxGroupChannelTitle); + _first->setMaxLength(kMaxGroupChannelTitle); + _last->setMaxLength(kMaxGroupChannelTitle); connect(_first, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); connect(_last, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); @@ -960,6 +974,76 @@ void EditNameTitleBox::onSaveChatDone(const MTPUpdates &updates) { closeBox(); } +EditBioBox::EditBioBox(QWidget*, gsl::not_null self) : BoxContent() +, _dynamicFieldStyle(CreateBioFieldStyle()) +, _self(self) +, _bio(this, _dynamicFieldStyle, langFactory(lng_bio_placeholder), _self->about()) +, _countdown(this, QString(), Ui::FlatLabel::InitType::Simple, st::editBioCountdownLabel) +, _about(this, lang(lng_bio_about), Ui::FlatLabel::InitType::Simple, st::aboutRevokePublicLabel) { +} + +void EditBioBox::prepare() { + setTitle(langFactory(lng_bio_title)); + + addButton(langFactory(lng_settings_save), [this] { save(); }); + addButton(langFactory(lng_cancel), [this] { closeBox(); }); + _bio->setMaxLength(kMaxBioLength); + _bio->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both); + auto cursor = _bio->textCursor(); + cursor.setPosition(_bio->getLastText().size()); + _bio->setTextCursor(cursor); + connect(_bio, &Ui::InputArea::submitted, this, [this](bool ctrlShiftEnter) { save(); }); + connect(_bio, &Ui::InputArea::resized, this, [this] { updateMaxHeight(); }); + connect(_bio, &Ui::InputArea::changed, this, [this] { handleBioUpdated(); }); + handleBioUpdated(); + updateMaxHeight(); +} + +void EditBioBox::updateMaxHeight() { + auto newHeight = st::contactPadding.top() + _bio->height() + st::boxLittleSkip + _about->height() + st::boxPadding.bottom() + st::contactPadding.bottom(); + setDimensions(st::boxWideWidth, newHeight); +} + +void EditBioBox::handleBioUpdated() { + auto text = _bio->getLastText(); + if (text.indexOf('\n') >= 0) { + auto position = _bio->textCursor().position(); + _bio->setText(text.replace('\n', ' ')); + auto cursor = _bio->textCursor(); + cursor.setPosition(position); + _bio->setTextCursor(cursor); + } + auto countLeft = qMax(kMaxBioLength - text.size(), 0); + _countdown->setText(QString::number(countLeft)); +} + +void EditBioBox::setInnerFocus() { + _bio->setFocusFast(); +} + +void EditBioBox::resizeEvent(QResizeEvent *e) { + BoxContent::resizeEvent(e); + + _bio->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _bio->height()); + _bio->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::contactPadding.top()); + _countdown->moveToRight(st::boxPadding.right(), _bio->y() + _dynamicFieldStyle.textMargins.top()); + _about->moveToLeft(st::boxPadding.left(), _bio->y() + _bio->height() + st::boxLittleSkip); +} + +void EditBioBox::save() { + if (_requestId) return; + + auto text = TextUtilities::PrepareForSending(_bio->getLastText()); + _sentBio = text; + + auto flags = MTPaccount_UpdateProfile::Flag::f_about; + _requestId = request(MTPaccount_UpdateProfile(MTP_flags(flags), MTPstring(), MTPstring(), MTP_string(text))).done([this](const MTPUser &result) { + App::feedUsers(MTP_vector(1, result)); + _self->setAbout(_sentBio); + closeBox(); + }).send(); +} + EditChannelBox::EditChannelBox(QWidget*, gsl::not_null channel) : _channel(channel) , _title(this, st::defaultInputField, langFactory(_channel->isMegagroup() ? lng_dlg_new_group_name : lng_dlg_new_channel_name), _channel->name) @@ -981,8 +1065,8 @@ void EditChannelBox::prepare() { setMouseTracking(true); - _title->setMaxLength(MaxGroupChannelTitle); - _description->setMaxLength(MaxChannelDescription); + _title->setMaxLength(kMaxGroupChannelTitle); + _description->setMaxLength(kMaxChannelDescription); connect(_description, SIGNAL(resized()), this, SLOT(onDescriptionResized())); connect(_description, SIGNAL(submitted(bool)), this, SLOT(onSave())); diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index d11ecadae..190b65aaa 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "boxes/abstract_box.h" +#include "mtproto/sender.h" class ConfirmBox; @@ -230,6 +231,32 @@ private: }; +class EditBioBox : public BoxContent, private MTP::Sender { +public: + EditBioBox(QWidget*, gsl::not_null self); + +protected: + void setInnerFocus() override; + void prepare() override; + + void resizeEvent(QResizeEvent *e) override; + +private: + void updateMaxHeight(); + void handleBioUpdated(); + void save(); + + style::InputField _dynamicFieldStyle; + gsl::not_null _self; + + object_ptr _bio; + object_ptr _countdown; + object_ptr _about; + mtpRequestId _requestId = 0; + QString _sentBio; + +}; + class EditChannelBox : public BoxContent, public RPCSender { Q_OBJECT diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 162f3fd50..cd7ddc1d5 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -202,6 +202,10 @@ aboutRevokePublicLabel: FlatLabel(defaultFlatLabel) { align: align(topleft); width: 320px; } +editBioCountdownLabel: FlatLabel(defaultFlatLabel) { + style: boxTextStyle; + textFg: windowSubTextFg; +} contactUserIcon: icon {{ "add_contact_user", menuIconFg }}; contactPhoneIcon: icon {{ "add_contact_phone", menuIconFg }}; @@ -563,11 +567,7 @@ passcodeTextStyle: TextStyle(defaultTextStyle) { usernamePadding: margins(23px, 6px, 21px, 12px); usernameSkip: 49px; -usernameTextStyle: TextStyle(passcodeTextStyle) { - font: boxTextFont; - linkFont: boxTextFont; - linkFontOver: font(boxFontSize underline); -} +usernameTextStyle: boxTextStyle; usernameDefaultFg: windowSubTextFg; downloadPathSkip: 10px; diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 6a075905c..5341d6c51 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -112,8 +112,6 @@ enum { MaxUsernameLength = 32, UsernameCheckTimeout = 200, - MaxChannelDescription = 255, - MaxGroupChannelTitle = 255, MaxPhotoCaption = 200, MaxMessageSize = 4096, diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 1219a0b07..0c47420ee 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -30,10 +30,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/tooltip.h" #include "core/file_utilities.h" -QString UrlClickHandler::copyToClipboardContextItemText() const { - return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link); -} - namespace { QString tryConvertUrlToLocal(QString url) { @@ -80,6 +76,24 @@ bool UrlRequiresConfirmation(const QUrl &url) { } // namespace +QString UrlClickHandler::copyToClipboardContextItemText() const { + return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link); +} + +QString UrlClickHandler::url() const { + if (isEmail()) { + return _originalUrl; + } + + QUrl u(_originalUrl), good(u.isValid() ? u.toEncoded() : QString()); + QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _originalUrl); + + if (!result.isEmpty() && !QRegularExpression(qsl("^[a-zA-Z]+:")).match(result).hasMatch()) { // no protocol + return qsl("http://") + result; + } + return result; +} + void UrlClickHandler::doOpen(QString url) { Ui::Tooltip::Hide(); diff --git a/Telegram/SourceFiles/core/click_handler_types.h b/Telegram/SourceFiles/core/click_handler_types.h index 5497229fe..04f7ddb8f 100644 --- a/Telegram/SourceFiles/core/click_handler_types.h +++ b/Telegram/SourceFiles/core/click_handler_types.h @@ -80,19 +80,7 @@ public: } protected: - QString url() const override { - if (isEmail()) { - return _originalUrl; - } - - QUrl u(_originalUrl), good(u.isValid() ? u.toEncoded() : QString()); - QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _originalUrl); - - if (!QRegularExpression(qsl("^[a-zA-Z]+:")).match(result).hasMatch()) { // no protocol - return qsl("http://") + result; - } - return result; - } + QString url() const override; QString readable() const override { return _readable; } @@ -115,6 +103,9 @@ class HiddenUrlClickHandler : public UrlClickHandler { public: HiddenUrlClickHandler(QString url) : UrlClickHandler(url, false) { } + QString copyToClipboardContextItemText() const override { + return url().isEmpty() ? QString() : UrlClickHandler::copyToClipboardContextItemText(); + } static void doOpen(QString url); void onClick(Qt::MouseButton button) const override { diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index e5009a2d4..fca48c3c2 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -29,6 +29,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "intro/introsignup.h" #include "intro/intropwdcheck.h" #include "mainwidget.h" +#include "apiwrap.h" #include "mainwindow.h" #include "messenger.h" #include "application.h" @@ -455,6 +456,9 @@ void Widget::Step::finish(const MTPUser &user, QImage photo) { App::wnd()->setupMain(&user); // "this" is already deleted here by creating the main widget. + if (auto user = App::self()) { + App::api()->requestFullPeer(user); + } if (!photo.isNull()) { App::app()->uploadProfilePhoto(photo, AuthSession::CurrentUserId()); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 35d75570f..34245cc91 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3995,7 +3995,7 @@ void MainWidget::mtpPing() { void MainWidget::start(const MTPUser *self) { if (!self) { - MTP::send(MTPusers_GetUsers(MTP_vector(1, MTP_inputUserSelf())), rpcDone(&MainWidget::startWithSelf)); + MTP::send(MTPusers_GetFullUser(MTP_inputUserSelf()), rpcDone(&MainWidget::startWithSelf)); return; } if (!AuthSession::Current().validateSelf(*self)) { @@ -4227,13 +4227,13 @@ bool MainWidget::inviteImportFail(const RPCError &error) { return true; } -void MainWidget::startWithSelf(const MTPVector &users) { - auto &v = users.v; - if (v.isEmpty()) { - LOG(("Auth Error: self user not received.")); - return App::logOutDelayed(); +void MainWidget::startWithSelf(const MTPUserFull &result) { + Expects(result.type() == mtpc_userFull); + auto &d = result.c_userFull(); + start(&d.vuser); + if (auto user = App::self()) { + App::api()->processFullPeer(user, result); } - start(&v[0]); } void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *h) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 12c78400b..a95174394 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -535,7 +535,7 @@ private: Window::SectionSlideParams prepareOverviewAnimation(); Window::SectionSlideParams prepareDialogsAnimation(); - void startWithSelf(const MTPVector &users); + void startWithSelf(const MTPUserFull &user); void saveSectionInStack(); diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 9aff7db36..03f321d84 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -102,6 +102,10 @@ settingsBlockOneLineTextPart: FlatLabel(settingsPrimaryLabel) { margin: margins(5px, 5px, 5px, 5px); maxHeight: 20px; } +settingsBioValue: FlatLabel(settingsBlockOneLineTextPart) { + width: 120px; + maxHeight: 0px; +} settingsSubSkip: 4px; settingsSmallSkip: 10px; settingsSkip: 14px; diff --git a/Telegram/SourceFiles/settings/settings_info_widget.cpp b/Telegram/SourceFiles/settings/settings_info_widget.cpp index 587dbdb8c..488d56fc9 100644 --- a/Telegram/SourceFiles/settings/settings_info_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_info_widget.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/labels.h" #include "ui/effects/widget_slide_wrap.h" #include "boxes/username_box.h" +#include "boxes/add_contact_box.h" #include "boxes/change_phone_box.h" #include "observer_peer.h" #include "messenger.h" @@ -34,7 +35,7 @@ namespace Settings { using UpdateFlag = Notify::PeerUpdate::Flag; InfoWidget::InfoWidget(QWidget *parent, UserData *self) : BlockWidget(parent, self, lang(lng_settings_section_info)) { - auto observeEvents = UpdateFlag::UsernameChanged | UpdateFlag::UserPhoneChanged; + auto observeEvents = UpdateFlag::UsernameChanged | UpdateFlag::UserPhoneChanged | UpdateFlag::AboutChanged; subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) { notifyPeerUpdated(update); })); @@ -45,12 +46,13 @@ InfoWidget::InfoWidget(QWidget *parent, UserData *self) : BlockWidget(parent, se void InfoWidget::createControls() { style::margins margin(0, -st::settingsBlockOneLineTextPart.margin.top(), 0, st::settingsSmallSkip - st::settingsBlockOneLineTextPart.margin.bottom()); style::margins slidedPadding(0, st::settingsSmallSkip / 2, 0, st::settingsSmallSkip - (st::settingsSmallSkip / 2)); - addChildRow(_mobileNumber, margin, slidedPadding); - addChildRow(_username, margin, slidedPadding); - addChildRow(_link, margin, slidedPadding); + addChildRow(_mobileNumber, margin, slidedPadding, st::settingsBlockOneLineTextPart); + addChildRow(_username, margin, slidedPadding, st::settingsBlockOneLineTextPart); + addChildRow(_link, margin, slidedPadding, st::settingsBlockOneLineTextPart); if (self()->username.isEmpty()) { _link->hideFast(); } + addChildRow(_bio, margin, slidedPadding, st::settingsBioValue); refreshControls(); } @@ -58,6 +60,7 @@ void InfoWidget::refreshControls() { refreshMobileNumber(); refreshUsername(); refreshLink(); + refreshBio(); } void InfoWidget::refreshMobileNumber() { @@ -122,6 +125,24 @@ void InfoWidget::refreshLink() { } } +void InfoWidget::refreshBio() { + TextWithEntities bioText; + auto aboutText = self()->about(); + if (self()->about().isEmpty()) { + bioText.text = lang(lng_settings_empty_bio); + } else { + bioText.text = aboutText; + } + bioText.entities.push_back(EntityInText(EntityInTextCustomUrl, 0, bioText.text.size(), QString())); + setLabeledText(_bio, lang(lng_profile_bio), bioText, TextWithEntities(), QString()); + if (auto text = _bio->entity()->textLabel()) { + text->setClickHandlerHook([](const ClickHandlerPtr &handler, Qt::MouseButton button) { + Ui::show(Box(App::self())); + return false; + }); + } +} + void InfoWidget::setLabeledText(object_ptr &row, const QString &label, const TextWithEntities &textWithEntities, const TextWithEntities &shortTextWithEntities, const QString ©Text) { auto nonEmptyText = !textWithEntities.text.isEmpty(); if (nonEmptyText) { @@ -130,7 +151,8 @@ void InfoWidget::setLabeledText(object_ptr &row, const QString &lab row->toggleAnimated(nonEmptyText); } -InfoWidget::LabeledWidget::LabeledWidget(QWidget *parent) : TWidget(parent) { +InfoWidget::LabeledWidget::LabeledWidget(QWidget *parent, const style::FlatLabel &valueSt) : TWidget(parent) +, _valueSt(valueSt) { } void InfoWidget::LabeledWidget::setLabeledText(const QString &label, const TextWithEntities &textWithEntities, const TextWithEntities &shortTextWithEntities, const QString ©Text) { @@ -158,7 +180,7 @@ void InfoWidget::LabeledWidget::setLabelText(object_ptr &text, co text.destroy(); if (textWithEntities.text.isEmpty()) return; - text.create(this, QString(), Ui::FlatLabel::InitType::Simple, st::settingsBlockOneLineTextPart); + text.create(this, QString(), Ui::FlatLabel::InitType::Simple, _valueSt); text->show(); text->setMarkedText(textWithEntities); text->setContextCopyText(copyText); @@ -178,6 +200,9 @@ void InfoWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) { if (update.flags & (UpdateFlag::UserPhoneChanged)) { refreshMobileNumber(); } + if (update.flags & UpdateFlag::AboutChanged) { + refreshBio(); + } contentSizeUpdated(); } @@ -221,7 +246,7 @@ int InfoWidget::LabeledWidget::resizeGetHeight(int newWidth) { _text->show(); } } - return st::settingsBlockOneLineTextPart.margin.top() + _label->height() + st::settingsBlockOneLineTextPart.margin.bottom(); + return st::settingsBlockOneLineTextPart.margin.top() + qMax(_label->height(), _text->height() - st::settingsBlockOneLineTextPart.margin.top() - st::settingsBlockOneLineTextPart.margin.bottom()) + st::settingsBlockOneLineTextPart.margin.bottom(); } } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_info_widget.h b/Telegram/SourceFiles/settings/settings_info_widget.h index 0146592fb..6d6adce95 100644 --- a/Telegram/SourceFiles/settings/settings_info_widget.h +++ b/Telegram/SourceFiles/settings/settings_info_widget.h @@ -45,10 +45,11 @@ private: void refreshMobileNumber(); void refreshUsername(); void refreshLink(); + void refreshBio(); class LabeledWidget : public TWidget { public: - LabeledWidget(QWidget *parent); + LabeledWidget(QWidget *parent, const style::FlatLabel &valueSt); void setLabeledText(const QString &label, const TextWithEntities &textWithEntities, const TextWithEntities &shortTextWithEntities, const QString ©Text); @@ -63,6 +64,7 @@ private: private: void setLabelText(object_ptr &text, const TextWithEntities &textWithEntities, const QString ©Text); + const style::FlatLabel &_valueSt; object_ptr _label = { nullptr }; object_ptr _text = { nullptr }; object_ptr _shortText = { nullptr }; @@ -75,6 +77,7 @@ private: object_ptr _mobileNumber = { nullptr }; object_ptr _username = { nullptr }; object_ptr _link = { nullptr }; + object_ptr _bio = { nullptr }; };