mirror of https://github.com/procxx/kepka.git
Implement information settings section.
This commit is contained in:
parent
633ff4b60e
commit
bbe6d2d13b
|
@ -344,6 +344,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_settings_sessions_about" = "Control your sessions on other devices.";
|
||||
"lng_settings_passcode_disable" = "Disable passcode";
|
||||
"lng_settings_password_disable" = "Disable cloud password";
|
||||
"lng_settings_about_bio" = "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco";
|
||||
"lng_settings_name_label" = "Name";
|
||||
"lng_settings_username_label" = "Username";
|
||||
"lng_settings_phone_label" = "Phone number";
|
||||
"lng_settings_username_add" = "Add username";
|
||||
|
||||
"lng_backgrounds_header" = "Choose your new chat background";
|
||||
"lng_theme_sure_keep" = "Keep this theme?";
|
||||
|
|
|
@ -4946,6 +4946,37 @@ auto ApiWrap::passwordStateCurrent() const
|
|||
: base::none;
|
||||
}
|
||||
|
||||
void ApiWrap::saveSelfBio(const QString &text, FnMut<void()> done) {
|
||||
if (_saveBioRequestId) {
|
||||
if (text != _saveBioText) {
|
||||
request(_saveBioRequestId).cancel();
|
||||
} else {
|
||||
if (done) {
|
||||
_saveBioDone = std::move(done);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
_saveBioText = text;
|
||||
_saveBioDone = std::move(done);
|
||||
_saveBioRequestId = request(MTPaccount_UpdateProfile(
|
||||
MTP_flags(MTPaccount_UpdateProfile::Flag::f_about),
|
||||
MTPstring(),
|
||||
MTPstring(),
|
||||
MTP_string(text)
|
||||
)).done([=](const MTPUser &result) {
|
||||
_saveBioRequestId = 0;
|
||||
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, result));
|
||||
App::self()->setAbout(_saveBioText);
|
||||
if (_saveBioDone) {
|
||||
_saveBioDone();
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
_saveBioRequestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::readServerHistory(not_null<History*> history) {
|
||||
if (history->unreadCount()) {
|
||||
readServerHistoryForce(history);
|
||||
|
|
|
@ -336,6 +336,8 @@ public:
|
|||
rpl::producer<Core::CloudPasswordState> passwordState() const;
|
||||
base::optional<Core::CloudPasswordState> passwordStateCurrent() const;
|
||||
|
||||
void saveSelfBio(const QString &text, FnMut<void()> done);
|
||||
|
||||
~ApiWrap();
|
||||
|
||||
private:
|
||||
|
@ -672,4 +674,8 @@ private:
|
|||
std::unique_ptr<Core::CloudPasswordState> _passwordState;
|
||||
rpl::event_stream<Core::CloudPasswordState> _passwordStateChanges;
|
||||
|
||||
mtpRequestId _saveBioRequestId = 0;
|
||||
FnMut<void()> _saveBioDone;
|
||||
QString _saveBioText;
|
||||
|
||||
};
|
||||
|
|
|
@ -34,17 +34,18 @@ namespace {
|
|||
|
||||
constexpr auto kMaxGroupChannelTitle = 255; // See also edit_peer_info_box.
|
||||
constexpr auto kMaxChannelDescription = 255; // See also edit_peer_info_box.
|
||||
constexpr auto kMaxBioLength = 70;
|
||||
constexpr auto kMinUsernameLength = 5;
|
||||
|
||||
} // namespace
|
||||
|
||||
style::InputField CreateBioFieldStyle() {
|
||||
auto result = st::newGroupDescription;
|
||||
result.textMargins.setRight(st::boxTextFont->spacew + st::boxTextFont->width(QString::number(kMaxBioLength)));
|
||||
result.textMargins.setRight(
|
||||
st::boxTextFont->spacew
|
||||
+ st::boxTextFont->width(QString::number(kMaxBioLength)));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QString PeerFloodErrorText(PeerFloodType type) {
|
||||
auto link = textcmdLink(
|
||||
Messenger::Instance().createInternalLinkFull(qsl("spambot")),
|
||||
|
|
|
@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
class ConfirmBox;
|
||||
class PeerListBox;
|
||||
|
||||
constexpr auto kMaxBioLength = 70;
|
||||
|
||||
style::InputField CreateBioFieldStyle();
|
||||
|
||||
namespace Ui {
|
||||
class FlatLabel;
|
||||
class InputField;
|
||||
|
|
|
@ -155,6 +155,10 @@ infoTopBarCall: IconButton(infoTopBarNotifications) {
|
|||
icon: icon {{ "top_bar_call", boxTitleCloseFg }};
|
||||
iconOver: icon {{ "top_bar_call", boxTitleCloseFgOver }};
|
||||
}
|
||||
infoTopBarSave: IconButton(infoTopBarNotifications) {
|
||||
icon: icon {{ "passport_ready", windowActiveTextFg }};
|
||||
iconOver: icon {{ "passport_ready", windowActiveTextFg}};
|
||||
}
|
||||
infoTopBarForward: IconButton(infoTopBarBack) {
|
||||
width: 46px;
|
||||
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
|
||||
|
@ -222,6 +226,10 @@ infoLayerTopBarCall: IconButton(infoLayerTopBarNotifications) {
|
|||
icon: icon {{ "top_bar_call", boxTitleCloseFg }};
|
||||
iconOver: icon {{ "top_bar_call", boxTitleCloseFgOver }};
|
||||
}
|
||||
infoLayerTopBarSave: IconButton(infoLayerTopBarNotifications) {
|
||||
icon: icon {{ "passport_ready", windowActiveTextFg }};
|
||||
iconOver: icon {{ "passport_ready", windowActiveTextFg }};
|
||||
}
|
||||
infoLayerTopBarForward: IconButton(infoLayerTopBarBack) {
|
||||
width: 45px;
|
||||
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
|
||||
|
|
|
@ -222,6 +222,14 @@ rpl::producer<SelectedItems> ContentWidget::selectedListValue() const {
|
|||
return rpl::single(SelectedItems(Storage::SharedMediaType::Photo));
|
||||
}
|
||||
|
||||
rpl::producer<bool> ContentWidget::canSaveChanges() const {
|
||||
return rpl::single(false);
|
||||
}
|
||||
|
||||
void ContentWidget::saveChanges(FnMut<void()> done) {
|
||||
done();
|
||||
}
|
||||
|
||||
void ContentWidget::refreshSearchField(bool shown) {
|
||||
auto search = _controller->searchFieldController();
|
||||
if (search && shown) {
|
||||
|
|
|
@ -73,6 +73,9 @@ public:
|
|||
virtual void cancelSelection() {
|
||||
}
|
||||
|
||||
virtual rpl::producer<bool> canSaveChanges() const;
|
||||
virtual void saveChanges(FnMut<void()> done);
|
||||
|
||||
protected:
|
||||
template <typename Widget>
|
||||
Widget *setInnerWidget(object_ptr<Widget> inner) {
|
||||
|
|
|
@ -269,6 +269,14 @@ rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(
|
|||
limitAfter);
|
||||
}
|
||||
|
||||
void Controller::setCanSaveChanges(rpl::producer<bool> can) {
|
||||
_canSaveChanges = std::move(can);
|
||||
}
|
||||
|
||||
rpl::producer<bool> Controller::canSaveChanges() const {
|
||||
return _canSaveChanges.value();
|
||||
}
|
||||
|
||||
Controller::~Controller() = default;
|
||||
|
||||
} // namespace Info
|
||||
|
|
|
@ -188,6 +188,9 @@ public:
|
|||
return base::take(_searchStartsFocused);
|
||||
}
|
||||
|
||||
void setCanSaveChanges(rpl::producer<bool> can);
|
||||
rpl::producer<bool> canSaveChanges() const;
|
||||
|
||||
void saveSearchState(not_null<ContentMemento*> memento);
|
||||
|
||||
void showSection(
|
||||
|
@ -218,6 +221,7 @@ private:
|
|||
std::unique_ptr<Ui::SearchFieldController> _searchFieldController;
|
||||
std::unique_ptr<Api::DelayedSearchController> _searchController;
|
||||
rpl::variable<bool> _seachEnabledByContent = false;
|
||||
rpl::variable<bool> _canSaveChanges = false;
|
||||
bool _searchStartsFocused = false;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
|
|
@ -152,6 +152,13 @@ Ui::FadeWrap<Ui::RpWidget> *TopBar::pushButton(
|
|||
return weak;
|
||||
}
|
||||
|
||||
void TopBar::forceButtonVisibility(
|
||||
Ui::FadeWrap<Ui::RpWidget> *button,
|
||||
rpl::producer<bool> shown) {
|
||||
_updateControlCallbacks.erase(button);
|
||||
button->toggleOn(std::move(shown));
|
||||
}
|
||||
|
||||
void TopBar::setSearchField(
|
||||
base::unique_qptr<Ui::InputField> field,
|
||||
rpl::producer<bool> &&shown,
|
||||
|
|
|
@ -56,6 +56,17 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
template <typename ButtonWidget>
|
||||
ButtonWidget *addButtonWithVisibility(
|
||||
base::unique_qptr<ButtonWidget> button,
|
||||
rpl::producer<bool> shown) {
|
||||
auto result = button.get();
|
||||
forceButtonVisibility(
|
||||
pushButton(std::move(button)),
|
||||
std::move(shown));
|
||||
return result;
|
||||
}
|
||||
|
||||
void createSearchView(
|
||||
not_null<Ui::SearchFieldController*> controller,
|
||||
rpl::producer<bool> &&shown,
|
||||
|
@ -79,7 +90,11 @@ private:
|
|||
void updateControlsGeometry(int newWidth);
|
||||
void updateDefaultControlsGeometry(int newWidth);
|
||||
void updateSelectionControlsGeometry(int newWidth);
|
||||
Ui::FadeWrap<Ui::RpWidget> *pushButton(base::unique_qptr<Ui::RpWidget> button);
|
||||
Ui::FadeWrap<Ui::RpWidget> *pushButton(
|
||||
base::unique_qptr<Ui::RpWidget> button);
|
||||
void forceButtonVisibility(
|
||||
Ui::FadeWrap<Ui::RpWidget> *button,
|
||||
rpl::producer<bool> shown);
|
||||
void removeButton(not_null<Ui::RpWidget*> button);
|
||||
void startHighlightAnimation();
|
||||
void updateControlsVisibility(anim::type animated);
|
||||
|
|
|
@ -387,6 +387,9 @@ void WrapWidget::createTopBar() {
|
|||
} else if (section.type() == Section::Type::Settings
|
||||
&& section.settingsType() == Section::SettingsType::Main) {
|
||||
addTopBarMenuButton();
|
||||
} else if (section.type() == Section::Type::Settings
|
||||
&& section.settingsType() == Section::SettingsType::Information) {
|
||||
addContentSaveButton();
|
||||
}
|
||||
|
||||
_topBar->lower();
|
||||
|
@ -409,6 +412,23 @@ void WrapWidget::addTopBarMenuButton() {
|
|||
});
|
||||
}
|
||||
|
||||
void WrapWidget::addContentSaveButton() {
|
||||
Expects(_topBar != nullptr);
|
||||
|
||||
_topBar->addButtonWithVisibility(
|
||||
base::make_unique_q<Ui::IconButton>(
|
||||
_topBar,
|
||||
(wrap() == Wrap::Layer
|
||||
? st::infoLayerTopBarSave
|
||||
: st::infoTopBarSave)),
|
||||
_controller->canSaveChanges()
|
||||
)->addClickHandler([=] {
|
||||
_content->saveChanges(crl::guard(_content.data(), [=] {
|
||||
_controller->showBackFromStack();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
void WrapWidget::addProfileCallsButton() {
|
||||
Expects(_topBar != nullptr);
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@ private:
|
|||
bool requireTopBarSearch() const;
|
||||
|
||||
void addTopBarMenuButton();
|
||||
void addContentSaveButton();
|
||||
void addProfileCallsButton();
|
||||
void addProfileNotificationsButton();
|
||||
void showTopBarMenu();
|
||||
|
|
|
@ -274,12 +274,13 @@ Cover *Cover::setOnlineCount(rpl::producer<int> &&count) {
|
|||
|
||||
void Cover::initViewers() {
|
||||
using Flag = Notify::PeerUpdate::Flag;
|
||||
Notify::PeerUpdateValue(
|
||||
_peer,
|
||||
Flag::NameChanged
|
||||
) | rpl::start_with_next(
|
||||
[this] { refreshNameText(); },
|
||||
lifetime());
|
||||
NameValue(
|
||||
_peer
|
||||
) | rpl::start_with_next([=](const TextWithEntities &name) {
|
||||
_name->setText(name.text);
|
||||
refreshNameGeometry(width());
|
||||
}, lifetime());
|
||||
|
||||
Notify::PeerUpdateValue(
|
||||
_peer,
|
||||
Flag::UserOnlineChanged | Flag::MembersChanged
|
||||
|
@ -331,11 +332,6 @@ void Cover::setVerified(bool verified) {
|
|||
refreshNameGeometry(width());
|
||||
}
|
||||
|
||||
void Cover::refreshNameText() {
|
||||
_name->setText(App::peerName(_peer));
|
||||
refreshNameGeometry(width());
|
||||
}
|
||||
|
||||
void Cover::refreshStatusText() {
|
||||
auto hasMembersLink = [&] {
|
||||
if (auto megagroup = _peer->asMegagroup()) {
|
||||
|
|
|
@ -76,7 +76,6 @@ public:
|
|||
private:
|
||||
void setupChildGeometry();
|
||||
void initViewers();
|
||||
void refreshNameText();
|
||||
void refreshStatusText();
|
||||
void refreshNameGeometry(int newWidth);
|
||||
void refreshStatusGeometry(int newWidth);
|
||||
|
|
|
@ -23,6 +23,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Info {
|
||||
namespace Profile {
|
||||
|
||||
rpl::producer<TextWithEntities> NameValue(
|
||||
not_null<PeerData*> peer) {
|
||||
return Notify::PeerUpdateValue(
|
||||
peer,
|
||||
Notify::PeerUpdate::Flag::NameChanged
|
||||
) | rpl::map([=] {
|
||||
return App::peerName(peer);
|
||||
}) | WithEmptyEntities();
|
||||
}
|
||||
|
||||
rpl::producer<TextWithEntities> PhoneValue(
|
||||
not_null<UserData*> user) {
|
||||
return Notify::PeerUpdateValue(
|
||||
|
|
|
@ -42,6 +42,8 @@ inline auto ToUpperValue() {
|
|||
});
|
||||
}
|
||||
|
||||
rpl::producer<TextWithEntities> NameValue(
|
||||
not_null<PeerData*> peer);
|
||||
rpl::producer<TextWithEntities> PhoneValue(
|
||||
not_null<UserData*> user);
|
||||
rpl::producer<TextWithEntities> BioValue(
|
||||
|
|
|
@ -41,16 +41,18 @@ Widget::Widget(
|
|||
not_null<Controller*> controller)
|
||||
: ContentWidget(parent, controller)
|
||||
, _self(controller->key().settingsSelf())
|
||||
, _type(controller->section().settingsType()) {
|
||||
const auto inner = setInnerWidget(::Settings::CreateSection(
|
||||
, _type(controller->section().settingsType())
|
||||
, _inner(setInnerWidget(::Settings::CreateSection(
|
||||
_type,
|
||||
this,
|
||||
controller->parentController(),
|
||||
_self));
|
||||
inner->sectionShowOther(
|
||||
_self))) {
|
||||
_inner->sectionShowOther(
|
||||
) | rpl::start_with_next([=](Type type) {
|
||||
this->controller()->showSettings(type);
|
||||
}, inner->lifetime());
|
||||
}, _inner->lifetime());
|
||||
|
||||
controller->setCanSaveChanges(_inner->sectionCanSaveChanges());
|
||||
}
|
||||
|
||||
not_null<UserData*> Widget::self() const {
|
||||
|
@ -76,6 +78,14 @@ void Widget::setInternalState(
|
|||
restoreState(memento);
|
||||
}
|
||||
|
||||
rpl::producer<bool> Widget::canSaveChanges() const {
|
||||
return _inner->sectionCanSaveChanges();
|
||||
}
|
||||
|
||||
void Widget::saveChanges(FnMut<void()> done) {
|
||||
_inner->sectionSaveChanges(std::move(done));
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_unique<Memento>(self(), _type);
|
||||
saveState(result.get());
|
||||
|
|
|
@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "info/info_content_widget.h"
|
||||
#include "info/info_controller.h"
|
||||
|
||||
namespace Settings {
|
||||
class Section;
|
||||
} // namespace Settings
|
||||
|
||||
namespace Info {
|
||||
namespace Settings {
|
||||
|
||||
|
@ -59,6 +63,9 @@ public:
|
|||
const QRect &geometry,
|
||||
not_null<Memento*> memento);
|
||||
|
||||
rpl::producer<bool> canSaveChanges() const override;
|
||||
void saveChanges(FnMut<void()> done) override;
|
||||
|
||||
private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
@ -68,6 +75,8 @@ private:
|
|||
not_null<UserData*> _self;
|
||||
Type _type = Type();
|
||||
|
||||
not_null<::Settings::Section*> _inner;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Settings
|
||||
|
|
|
@ -114,6 +114,7 @@ public:
|
|||
| start_with_next([this](auto &&data) {
|
||||
assign(std::forward<decltype(data)>(data));
|
||||
}, _lifetime);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Type current() const {
|
||||
|
|
|
@ -76,3 +76,27 @@ settingsCloudPasswordLabel: FlatLabel(defaultFlatLabel) {
|
|||
maxHeight: 20px;
|
||||
}
|
||||
settingsCloudPasswordLabelPadding: margins(22px, 8px, 10px, 8px);
|
||||
|
||||
settingsInfoRowHeight: 62px;
|
||||
settingsInfoIconPosition: point(24px, 16px);
|
||||
settingsInfoValue: FlatLabel(defaultFlatLabel) {
|
||||
textFg: windowFg;
|
||||
style: boxTextStyle;
|
||||
maxHeight: 20px;
|
||||
}
|
||||
settingsInfoValuePosition: point(78px, 14px);
|
||||
settingsInfoAbout: FlatLabel(settingsInfoValue) {
|
||||
textFg: windowSubTextFg;
|
||||
style: defaultTextStyle;
|
||||
}
|
||||
settingsInfoAboutPosition: point(78px, 33px);
|
||||
settingsInfoRightSkip: 60px;
|
||||
settingsInfoEditPosition: point(12px, 12px);
|
||||
settingsInfoEdit: IconButton(defaultIconButton) {
|
||||
}
|
||||
|
||||
settingsBioMargins: margins(20px, 2px, 20px, 2px);
|
||||
settingsBioCountdown: FlatLabel(defaultFlatLabel) {
|
||||
style: boxTextStyle;
|
||||
textFg: windowSubTextFg;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,12 @@ public:
|
|||
virtual rpl::producer<Type> sectionShowOther() {
|
||||
return rpl::never<Type>();
|
||||
}
|
||||
virtual rpl::producer<bool> sectionCanSaveChanges() {
|
||||
return rpl::single(false);
|
||||
}
|
||||
virtual void sectionSaveChanges(FnMut<void()> done) {
|
||||
done();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -8,12 +8,253 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "settings/settings_information.h"
|
||||
|
||||
#include "settings/settings_common.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/change_phone_box.h"
|
||||
#include "boxes/username_box.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "auth_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "styles/style_old_settings.h"
|
||||
|
||||
namespace Settings {
|
||||
namespace {
|
||||
|
||||
void AddRow(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<QString> label,
|
||||
rpl::producer<TextWithEntities> value,
|
||||
const QString ©Text,
|
||||
const style::IconButton &editSt,
|
||||
Fn<void()> edit,
|
||||
const style::icon &icon) {
|
||||
const auto wrap = container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||
container,
|
||||
st::settingsInfoRowHeight));
|
||||
|
||||
wrap->paintRequest(
|
||||
) | rpl::start_with_next([=, &icon] {
|
||||
Painter p(wrap);
|
||||
icon.paint(p, st::settingsInfoIconPosition, wrap->width());
|
||||
}, wrap->lifetime());
|
||||
|
||||
auto existing = base::duplicate(
|
||||
value
|
||||
) | rpl::map([](const TextWithEntities &text) {
|
||||
return text.entities.isEmpty();
|
||||
});
|
||||
const auto text = Ui::CreateChild<Ui::FlatLabel>(
|
||||
wrap,
|
||||
std::move(value),
|
||||
st::settingsInfoValue);
|
||||
text->setClickHandlerFilter([=](auto&&...) {
|
||||
edit();
|
||||
return false;
|
||||
});
|
||||
base::duplicate(
|
||||
existing
|
||||
) | rpl::start_with_next([=](bool existing) {
|
||||
text->setSelectable(existing);
|
||||
text->setDoubleClickSelectsParagraph(existing);
|
||||
text->setContextCopyText(existing ? copyText : QString());
|
||||
}, text->lifetime());
|
||||
|
||||
const auto about = Ui::CreateChild<Ui::FlatLabel>(
|
||||
wrap,
|
||||
std::move(label),
|
||||
st::settingsInfoAbout);
|
||||
|
||||
const auto button = Ui::CreateChild<Ui::IconButton>(
|
||||
wrap,
|
||||
editSt);
|
||||
button->addClickHandler(edit);
|
||||
button->showOn(std::move(existing));
|
||||
|
||||
wrap->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
text->resizeToWidth(width
|
||||
- st::settingsInfoValuePosition.x()
|
||||
- st::settingsInfoRightSkip);
|
||||
text->moveToLeft(
|
||||
st::settingsInfoValuePosition.x(),
|
||||
st::settingsInfoValuePosition.y(),
|
||||
width);
|
||||
about->resizeToWidth(width
|
||||
- st::settingsInfoAboutPosition.x()
|
||||
- st::settingsInfoRightSkip);
|
||||
about->moveToLeft(
|
||||
st::settingsInfoAboutPosition.x(),
|
||||
st::settingsInfoAboutPosition.y(),
|
||||
width);
|
||||
button->moveToRight(
|
||||
st::settingsInfoEditPosition.x(),
|
||||
st::settingsInfoEditPosition.y(),
|
||||
width);
|
||||
}, wrap->lifetime());
|
||||
}
|
||||
|
||||
void SetupRows(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<UserData*> self) {
|
||||
AddDivider(container);
|
||||
AddSkip(container);
|
||||
|
||||
AddRow(
|
||||
container,
|
||||
Lang::Viewer(lng_settings_name_label),
|
||||
Info::Profile::NameValue(self),
|
||||
lang(lng_profile_copy_fullname),
|
||||
st::settingsEditButton,
|
||||
[=] { Ui::show(Box<EditNameBox>(self)); },
|
||||
st::settingsEditButton.icon);
|
||||
|
||||
AddRow(
|
||||
container,
|
||||
Lang::Viewer(lng_settings_phone_label),
|
||||
Info::Profile::PhoneValue(self),
|
||||
lang(lng_profile_copy_phone),
|
||||
st::settingsEditButton,
|
||||
[] { Ui::show(Box<ChangePhoneBox>()); },
|
||||
st::settingsEditButton.icon);
|
||||
|
||||
auto username = Info::Profile::UsernameValue(self);
|
||||
auto empty = base::duplicate(
|
||||
username
|
||||
) | rpl::map([](const TextWithEntities &username) {
|
||||
return username.text.isEmpty();
|
||||
});
|
||||
auto label = rpl::combine(
|
||||
Lang::Viewer(lng_settings_username_label),
|
||||
std::move(empty)
|
||||
) | rpl::map([](const QString &label, bool empty) {
|
||||
return empty ? "t.me/username" : label;
|
||||
});
|
||||
auto value = rpl::combine(
|
||||
std::move(username),
|
||||
Lang::Viewer(lng_settings_username_add)
|
||||
) | rpl::map([](const TextWithEntities &username, const QString &add) {
|
||||
if (!username.text.isEmpty()) {
|
||||
return username;
|
||||
}
|
||||
auto result = TextWithEntities{ add };
|
||||
result.entities.push_back(EntityInText(
|
||||
EntityInTextCustomUrl,
|
||||
0,
|
||||
add.size(),
|
||||
"internal:edit_username"));
|
||||
return result;
|
||||
});
|
||||
AddRow(
|
||||
container,
|
||||
std::move(label),
|
||||
std::move(value),
|
||||
lang(lng_context_copy_mention),
|
||||
st::settingsEditButton,
|
||||
[=] { Ui::show(Box<UsernameBox>()); },
|
||||
st::settingsEditButton.icon);
|
||||
|
||||
AddSkip(container);
|
||||
}
|
||||
|
||||
struct BioManager {
|
||||
rpl::producer<bool> canSave;
|
||||
Fn<void(FnMut<void()> done)> save;
|
||||
};
|
||||
|
||||
BioManager SetupBio(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<UserData*> self) {
|
||||
AddDivider(container);
|
||||
AddSkip(container);
|
||||
|
||||
const auto bioStyle = [] {
|
||||
auto result = CreateBioFieldStyle();
|
||||
return result;
|
||||
};
|
||||
const auto style = Ui::AttachAsChild(container, bioStyle());
|
||||
const auto current = Ui::AttachAsChild(container, self->about());
|
||||
const auto changed = Ui::AttachAsChild(
|
||||
container,
|
||||
rpl::event_stream<bool>());
|
||||
const auto bio = container->add(
|
||||
object_ptr<Ui::InputField>(
|
||||
container,
|
||||
*style,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_bio_placeholder),
|
||||
*current),
|
||||
st::settingsBioMargins);
|
||||
|
||||
const auto countdown = Ui::CreateChild<Ui::FlatLabel>(
|
||||
container.get(),
|
||||
QString(),
|
||||
Ui::FlatLabel::InitType::Simple,
|
||||
st::settingsBioCountdown);
|
||||
|
||||
rpl::combine(
|
||||
bio->geometryValue(),
|
||||
countdown->widthValue()
|
||||
) | rpl::start_with_next([=](QRect geometry, int width) {
|
||||
countdown->move(
|
||||
geometry.x() + geometry.width() - width,
|
||||
geometry.y() + style->textMargins.top());
|
||||
}, countdown->lifetime());
|
||||
|
||||
const auto updated = [=] {
|
||||
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);
|
||||
}
|
||||
changed->fire(*current != text);
|
||||
const auto countLeft = qMax(kMaxBioLength - text.size(), 0);
|
||||
countdown->setText(QString::number(countLeft));
|
||||
};
|
||||
const auto save = [=](FnMut<void()> done) {
|
||||
Auth().api().saveSelfBio(
|
||||
TextUtilities::PrepareForSending(bio->getLastText()),
|
||||
std::move(done));
|
||||
};
|
||||
|
||||
Info::Profile::BioValue(
|
||||
self
|
||||
) | rpl::start_with_next([=](const TextWithEntities &text) {
|
||||
*current = text.text;
|
||||
changed->fire(*current != bio->getLastText());
|
||||
}, bio->lifetime());
|
||||
|
||||
bio->setMaxLength(kMaxBioLength);
|
||||
bio->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
|
||||
auto cursor = bio->textCursor();
|
||||
cursor.setPosition(bio->getLastText().size());
|
||||
bio->setTextCursor(cursor);
|
||||
QObject::connect(bio, &Ui::InputField::submitted, [=] {
|
||||
save(nullptr);
|
||||
});
|
||||
QObject::connect(bio, &Ui::InputField::changed, updated);
|
||||
bio->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
bio->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
updated();
|
||||
|
||||
AddSkip(container);
|
||||
AddDividerText(container, Lang::Viewer(lng_settings_about_bio));
|
||||
|
||||
return BioManager{
|
||||
changed->events() | rpl::distinct_until_changed(),
|
||||
save
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Information::Information(QWidget *parent, not_null<UserData*> self)
|
||||
: Section(parent)
|
||||
|
@ -21,10 +262,21 @@ Information::Information(QWidget *parent, not_null<UserData*> self)
|
|||
setupContent();
|
||||
}
|
||||
|
||||
rpl::producer<bool> Information::sectionCanSaveChanges() {
|
||||
return _canSaveChanges.value();
|
||||
}
|
||||
|
||||
void Information::sectionSaveChanges(FnMut<void()> done) {
|
||||
_save(std::move(done));
|
||||
}
|
||||
|
||||
void Information::setupContent() {
|
||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
|
||||
content->add(object_ptr<BoxContentDivider>(content));
|
||||
SetupRows(content, _self);
|
||||
auto manager = SetupBio(content, _self);
|
||||
_canSaveChanges = std::move(manager.canSave);
|
||||
_save = std::move(manager.save);
|
||||
|
||||
Ui::ResizeFitChild(this, content);
|
||||
}
|
||||
|
|
|
@ -15,10 +15,15 @@ class Information : public Section {
|
|||
public:
|
||||
Information(QWidget *parent, not_null<UserData*> self);
|
||||
|
||||
rpl::producer<bool> sectionCanSaveChanges() override;
|
||||
void sectionSaveChanges(FnMut<void()> done) override;
|
||||
|
||||
private:
|
||||
void setupContent();
|
||||
|
||||
not_null<UserData*> _self;
|
||||
rpl::variable<bool> _canSaveChanges;
|
||||
Fn<void(FnMut<void()> done)> _save;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "settings/settings_common.h"
|
||||
#include "settings/settings_codes.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "boxes/language_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/about_box.h"
|
||||
|
|
Loading…
Reference in New Issue