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_sessions_about" = "Control your sessions on other devices.";
|
||||||
"lng_settings_passcode_disable" = "Disable passcode";
|
"lng_settings_passcode_disable" = "Disable passcode";
|
||||||
"lng_settings_password_disable" = "Disable cloud password";
|
"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_backgrounds_header" = "Choose your new chat background";
|
||||||
"lng_theme_sure_keep" = "Keep this theme?";
|
"lng_theme_sure_keep" = "Keep this theme?";
|
||||||
|
|
|
@ -4946,6 +4946,37 @@ auto ApiWrap::passwordStateCurrent() const
|
||||||
: base::none;
|
: 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) {
|
void ApiWrap::readServerHistory(not_null<History*> history) {
|
||||||
if (history->unreadCount()) {
|
if (history->unreadCount()) {
|
||||||
readServerHistoryForce(history);
|
readServerHistoryForce(history);
|
||||||
|
|
|
@ -336,6 +336,8 @@ public:
|
||||||
rpl::producer<Core::CloudPasswordState> passwordState() const;
|
rpl::producer<Core::CloudPasswordState> passwordState() const;
|
||||||
base::optional<Core::CloudPasswordState> passwordStateCurrent() const;
|
base::optional<Core::CloudPasswordState> passwordStateCurrent() const;
|
||||||
|
|
||||||
|
void saveSelfBio(const QString &text, FnMut<void()> done);
|
||||||
|
|
||||||
~ApiWrap();
|
~ApiWrap();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -672,4 +674,8 @@ private:
|
||||||
std::unique_ptr<Core::CloudPasswordState> _passwordState;
|
std::unique_ptr<Core::CloudPasswordState> _passwordState;
|
||||||
rpl::event_stream<Core::CloudPasswordState> _passwordStateChanges;
|
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 kMaxGroupChannelTitle = 255; // See also edit_peer_info_box.
|
||||||
constexpr auto kMaxChannelDescription = 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;
|
constexpr auto kMinUsernameLength = 5;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
style::InputField CreateBioFieldStyle() {
|
style::InputField CreateBioFieldStyle() {
|
||||||
auto result = st::newGroupDescription;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
QString PeerFloodErrorText(PeerFloodType type) {
|
QString PeerFloodErrorText(PeerFloodType type) {
|
||||||
auto link = textcmdLink(
|
auto link = textcmdLink(
|
||||||
Messenger::Instance().createInternalLinkFull(qsl("spambot")),
|
Messenger::Instance().createInternalLinkFull(qsl("spambot")),
|
||||||
|
|
|
@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
class ConfirmBox;
|
class ConfirmBox;
|
||||||
class PeerListBox;
|
class PeerListBox;
|
||||||
|
|
||||||
|
constexpr auto kMaxBioLength = 70;
|
||||||
|
|
||||||
|
style::InputField CreateBioFieldStyle();
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class FlatLabel;
|
class FlatLabel;
|
||||||
class InputField;
|
class InputField;
|
||||||
|
|
|
@ -155,6 +155,10 @@ infoTopBarCall: IconButton(infoTopBarNotifications) {
|
||||||
icon: icon {{ "top_bar_call", boxTitleCloseFg }};
|
icon: icon {{ "top_bar_call", boxTitleCloseFg }};
|
||||||
iconOver: icon {{ "top_bar_call", boxTitleCloseFgOver }};
|
iconOver: icon {{ "top_bar_call", boxTitleCloseFgOver }};
|
||||||
}
|
}
|
||||||
|
infoTopBarSave: IconButton(infoTopBarNotifications) {
|
||||||
|
icon: icon {{ "passport_ready", windowActiveTextFg }};
|
||||||
|
iconOver: icon {{ "passport_ready", windowActiveTextFg}};
|
||||||
|
}
|
||||||
infoTopBarForward: IconButton(infoTopBarBack) {
|
infoTopBarForward: IconButton(infoTopBarBack) {
|
||||||
width: 46px;
|
width: 46px;
|
||||||
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
|
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
|
||||||
|
@ -222,6 +226,10 @@ infoLayerTopBarCall: IconButton(infoLayerTopBarNotifications) {
|
||||||
icon: icon {{ "top_bar_call", boxTitleCloseFg }};
|
icon: icon {{ "top_bar_call", boxTitleCloseFg }};
|
||||||
iconOver: icon {{ "top_bar_call", boxTitleCloseFgOver }};
|
iconOver: icon {{ "top_bar_call", boxTitleCloseFgOver }};
|
||||||
}
|
}
|
||||||
|
infoLayerTopBarSave: IconButton(infoLayerTopBarNotifications) {
|
||||||
|
icon: icon {{ "passport_ready", windowActiveTextFg }};
|
||||||
|
iconOver: icon {{ "passport_ready", windowActiveTextFg }};
|
||||||
|
}
|
||||||
infoLayerTopBarForward: IconButton(infoLayerTopBarBack) {
|
infoLayerTopBarForward: IconButton(infoLayerTopBarBack) {
|
||||||
width: 45px;
|
width: 45px;
|
||||||
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
|
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
|
||||||
|
|
|
@ -222,6 +222,14 @@ rpl::producer<SelectedItems> ContentWidget::selectedListValue() const {
|
||||||
return rpl::single(SelectedItems(Storage::SharedMediaType::Photo));
|
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) {
|
void ContentWidget::refreshSearchField(bool shown) {
|
||||||
auto search = _controller->searchFieldController();
|
auto search = _controller->searchFieldController();
|
||||||
if (search && shown) {
|
if (search && shown) {
|
||||||
|
|
|
@ -73,6 +73,9 @@ public:
|
||||||
virtual void cancelSelection() {
|
virtual void cancelSelection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual rpl::producer<bool> canSaveChanges() const;
|
||||||
|
virtual void saveChanges(FnMut<void()> done);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename Widget>
|
template <typename Widget>
|
||||||
Widget *setInnerWidget(object_ptr<Widget> inner) {
|
Widget *setInnerWidget(object_ptr<Widget> inner) {
|
||||||
|
|
|
@ -269,6 +269,14 @@ rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(
|
||||||
limitAfter);
|
limitAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::setCanSaveChanges(rpl::producer<bool> can) {
|
||||||
|
_canSaveChanges = std::move(can);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> Controller::canSaveChanges() const {
|
||||||
|
return _canSaveChanges.value();
|
||||||
|
}
|
||||||
|
|
||||||
Controller::~Controller() = default;
|
Controller::~Controller() = default;
|
||||||
|
|
||||||
} // namespace Info
|
} // namespace Info
|
||||||
|
|
|
@ -188,6 +188,9 @@ public:
|
||||||
return base::take(_searchStartsFocused);
|
return base::take(_searchStartsFocused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCanSaveChanges(rpl::producer<bool> can);
|
||||||
|
rpl::producer<bool> canSaveChanges() const;
|
||||||
|
|
||||||
void saveSearchState(not_null<ContentMemento*> memento);
|
void saveSearchState(not_null<ContentMemento*> memento);
|
||||||
|
|
||||||
void showSection(
|
void showSection(
|
||||||
|
@ -218,6 +221,7 @@ private:
|
||||||
std::unique_ptr<Ui::SearchFieldController> _searchFieldController;
|
std::unique_ptr<Ui::SearchFieldController> _searchFieldController;
|
||||||
std::unique_ptr<Api::DelayedSearchController> _searchController;
|
std::unique_ptr<Api::DelayedSearchController> _searchController;
|
||||||
rpl::variable<bool> _seachEnabledByContent = false;
|
rpl::variable<bool> _seachEnabledByContent = false;
|
||||||
|
rpl::variable<bool> _canSaveChanges = false;
|
||||||
bool _searchStartsFocused = false;
|
bool _searchStartsFocused = false;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
|
@ -152,6 +152,13 @@ Ui::FadeWrap<Ui::RpWidget> *TopBar::pushButton(
|
||||||
return weak;
|
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(
|
void TopBar::setSearchField(
|
||||||
base::unique_qptr<Ui::InputField> field,
|
base::unique_qptr<Ui::InputField> field,
|
||||||
rpl::producer<bool> &&shown,
|
rpl::producer<bool> &&shown,
|
||||||
|
|
|
@ -56,6 +56,17 @@ public:
|
||||||
return result;
|
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(
|
void createSearchView(
|
||||||
not_null<Ui::SearchFieldController*> controller,
|
not_null<Ui::SearchFieldController*> controller,
|
||||||
rpl::producer<bool> &&shown,
|
rpl::producer<bool> &&shown,
|
||||||
|
@ -79,7 +90,11 @@ private:
|
||||||
void updateControlsGeometry(int newWidth);
|
void updateControlsGeometry(int newWidth);
|
||||||
void updateDefaultControlsGeometry(int newWidth);
|
void updateDefaultControlsGeometry(int newWidth);
|
||||||
void updateSelectionControlsGeometry(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 removeButton(not_null<Ui::RpWidget*> button);
|
||||||
void startHighlightAnimation();
|
void startHighlightAnimation();
|
||||||
void updateControlsVisibility(anim::type animated);
|
void updateControlsVisibility(anim::type animated);
|
||||||
|
|
|
@ -387,6 +387,9 @@ void WrapWidget::createTopBar() {
|
||||||
} else if (section.type() == Section::Type::Settings
|
} else if (section.type() == Section::Type::Settings
|
||||||
&& section.settingsType() == Section::SettingsType::Main) {
|
&& section.settingsType() == Section::SettingsType::Main) {
|
||||||
addTopBarMenuButton();
|
addTopBarMenuButton();
|
||||||
|
} else if (section.type() == Section::Type::Settings
|
||||||
|
&& section.settingsType() == Section::SettingsType::Information) {
|
||||||
|
addContentSaveButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
_topBar->lower();
|
_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() {
|
void WrapWidget::addProfileCallsButton() {
|
||||||
Expects(_topBar != nullptr);
|
Expects(_topBar != nullptr);
|
||||||
|
|
||||||
|
|
|
@ -192,6 +192,7 @@ private:
|
||||||
bool requireTopBarSearch() const;
|
bool requireTopBarSearch() const;
|
||||||
|
|
||||||
void addTopBarMenuButton();
|
void addTopBarMenuButton();
|
||||||
|
void addContentSaveButton();
|
||||||
void addProfileCallsButton();
|
void addProfileCallsButton();
|
||||||
void addProfileNotificationsButton();
|
void addProfileNotificationsButton();
|
||||||
void showTopBarMenu();
|
void showTopBarMenu();
|
||||||
|
|
|
@ -274,12 +274,13 @@ Cover *Cover::setOnlineCount(rpl::producer<int> &&count) {
|
||||||
|
|
||||||
void Cover::initViewers() {
|
void Cover::initViewers() {
|
||||||
using Flag = Notify::PeerUpdate::Flag;
|
using Flag = Notify::PeerUpdate::Flag;
|
||||||
Notify::PeerUpdateValue(
|
NameValue(
|
||||||
_peer,
|
_peer
|
||||||
Flag::NameChanged
|
) | rpl::start_with_next([=](const TextWithEntities &name) {
|
||||||
) | rpl::start_with_next(
|
_name->setText(name.text);
|
||||||
[this] { refreshNameText(); },
|
refreshNameGeometry(width());
|
||||||
lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
Notify::PeerUpdateValue(
|
Notify::PeerUpdateValue(
|
||||||
_peer,
|
_peer,
|
||||||
Flag::UserOnlineChanged | Flag::MembersChanged
|
Flag::UserOnlineChanged | Flag::MembersChanged
|
||||||
|
@ -331,11 +332,6 @@ void Cover::setVerified(bool verified) {
|
||||||
refreshNameGeometry(width());
|
refreshNameGeometry(width());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cover::refreshNameText() {
|
|
||||||
_name->setText(App::peerName(_peer));
|
|
||||||
refreshNameGeometry(width());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cover::refreshStatusText() {
|
void Cover::refreshStatusText() {
|
||||||
auto hasMembersLink = [&] {
|
auto hasMembersLink = [&] {
|
||||||
if (auto megagroup = _peer->asMegagroup()) {
|
if (auto megagroup = _peer->asMegagroup()) {
|
||||||
|
|
|
@ -76,7 +76,6 @@ public:
|
||||||
private:
|
private:
|
||||||
void setupChildGeometry();
|
void setupChildGeometry();
|
||||||
void initViewers();
|
void initViewers();
|
||||||
void refreshNameText();
|
|
||||||
void refreshStatusText();
|
void refreshStatusText();
|
||||||
void refreshNameGeometry(int newWidth);
|
void refreshNameGeometry(int newWidth);
|
||||||
void refreshStatusGeometry(int newWidth);
|
void refreshStatusGeometry(int newWidth);
|
||||||
|
|
|
@ -23,6 +23,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Info {
|
namespace Info {
|
||||||
namespace Profile {
|
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(
|
rpl::producer<TextWithEntities> PhoneValue(
|
||||||
not_null<UserData*> user) {
|
not_null<UserData*> user) {
|
||||||
return Notify::PeerUpdateValue(
|
return Notify::PeerUpdateValue(
|
||||||
|
|
|
@ -42,6 +42,8 @@ inline auto ToUpperValue() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<TextWithEntities> NameValue(
|
||||||
|
not_null<PeerData*> peer);
|
||||||
rpl::producer<TextWithEntities> PhoneValue(
|
rpl::producer<TextWithEntities> PhoneValue(
|
||||||
not_null<UserData*> user);
|
not_null<UserData*> user);
|
||||||
rpl::producer<TextWithEntities> BioValue(
|
rpl::producer<TextWithEntities> BioValue(
|
||||||
|
|
|
@ -41,16 +41,18 @@ Widget::Widget(
|
||||||
not_null<Controller*> controller)
|
not_null<Controller*> controller)
|
||||||
: ContentWidget(parent, controller)
|
: ContentWidget(parent, controller)
|
||||||
, _self(controller->key().settingsSelf())
|
, _self(controller->key().settingsSelf())
|
||||||
, _type(controller->section().settingsType()) {
|
, _type(controller->section().settingsType())
|
||||||
const auto inner = setInnerWidget(::Settings::CreateSection(
|
, _inner(setInnerWidget(::Settings::CreateSection(
|
||||||
_type,
|
_type,
|
||||||
this,
|
this,
|
||||||
controller->parentController(),
|
controller->parentController(),
|
||||||
_self));
|
_self))) {
|
||||||
inner->sectionShowOther(
|
_inner->sectionShowOther(
|
||||||
) | rpl::start_with_next([=](Type type) {
|
) | rpl::start_with_next([=](Type type) {
|
||||||
this->controller()->showSettings(type);
|
this->controller()->showSettings(type);
|
||||||
}, inner->lifetime());
|
}, _inner->lifetime());
|
||||||
|
|
||||||
|
controller->setCanSaveChanges(_inner->sectionCanSaveChanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<UserData*> Widget::self() const {
|
not_null<UserData*> Widget::self() const {
|
||||||
|
@ -76,6 +78,14 @@ void Widget::setInternalState(
|
||||||
restoreState(memento);
|
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() {
|
std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||||
auto result = std::make_unique<Memento>(self(), _type);
|
auto result = std::make_unique<Memento>(self(), _type);
|
||||||
saveState(result.get());
|
saveState(result.get());
|
||||||
|
|
|
@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "info/info_content_widget.h"
|
#include "info/info_content_widget.h"
|
||||||
#include "info/info_controller.h"
|
#include "info/info_controller.h"
|
||||||
|
|
||||||
|
namespace Settings {
|
||||||
|
class Section;
|
||||||
|
} // namespace Settings
|
||||||
|
|
||||||
namespace Info {
|
namespace Info {
|
||||||
namespace Settings {
|
namespace Settings {
|
||||||
|
|
||||||
|
@ -59,6 +63,9 @@ public:
|
||||||
const QRect &geometry,
|
const QRect &geometry,
|
||||||
not_null<Memento*> memento);
|
not_null<Memento*> memento);
|
||||||
|
|
||||||
|
rpl::producer<bool> canSaveChanges() const override;
|
||||||
|
void saveChanges(FnMut<void()> done) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void saveState(not_null<Memento*> memento);
|
void saveState(not_null<Memento*> memento);
|
||||||
void restoreState(not_null<Memento*> memento);
|
void restoreState(not_null<Memento*> memento);
|
||||||
|
@ -68,6 +75,8 @@ private:
|
||||||
not_null<UserData*> _self;
|
not_null<UserData*> _self;
|
||||||
Type _type = Type();
|
Type _type = Type();
|
||||||
|
|
||||||
|
not_null<::Settings::Section*> _inner;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
|
|
@ -114,6 +114,7 @@ public:
|
||||||
| start_with_next([this](auto &&data) {
|
| start_with_next([this](auto &&data) {
|
||||||
assign(std::forward<decltype(data)>(data));
|
assign(std::forward<decltype(data)>(data));
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type current() const {
|
Type current() const {
|
||||||
|
|
|
@ -76,3 +76,27 @@ settingsCloudPasswordLabel: FlatLabel(defaultFlatLabel) {
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
}
|
}
|
||||||
settingsCloudPasswordLabelPadding: margins(22px, 8px, 10px, 8px);
|
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() {
|
virtual rpl::producer<Type> sectionShowOther() {
|
||||||
return rpl::never<Type>();
|
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_information.h"
|
||||||
|
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
#include "boxes/abstract_box.h"
|
|
||||||
#include "ui/wrap/vertical_layout.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 "lang/lang_keys.h"
|
||||||
|
#include "auth_session.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
#include "styles/style_settings.h"
|
#include "styles/style_settings.h"
|
||||||
|
#include "styles/style_old_settings.h"
|
||||||
|
|
||||||
namespace Settings {
|
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)
|
Information::Information(QWidget *parent, not_null<UserData*> self)
|
||||||
: Section(parent)
|
: Section(parent)
|
||||||
|
@ -21,10 +262,21 @@ Information::Information(QWidget *parent, not_null<UserData*> self)
|
||||||
setupContent();
|
setupContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> Information::sectionCanSaveChanges() {
|
||||||
|
return _canSaveChanges.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Information::sectionSaveChanges(FnMut<void()> done) {
|
||||||
|
_save(std::move(done));
|
||||||
|
}
|
||||||
|
|
||||||
void Information::setupContent() {
|
void Information::setupContent() {
|
||||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
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);
|
Ui::ResizeFitChild(this, content);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,15 @@ class Information : public Section {
|
||||||
public:
|
public:
|
||||||
Information(QWidget *parent, not_null<UserData*> self);
|
Information(QWidget *parent, not_null<UserData*> self);
|
||||||
|
|
||||||
|
rpl::producer<bool> sectionCanSaveChanges() override;
|
||||||
|
void sectionSaveChanges(FnMut<void()> done) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupContent();
|
void setupContent();
|
||||||
|
|
||||||
not_null<UserData*> _self;
|
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_common.h"
|
||||||
#include "settings/settings_codes.h"
|
#include "settings/settings_codes.h"
|
||||||
#include "boxes/abstract_box.h"
|
|
||||||
#include "boxes/language_box.h"
|
#include "boxes/language_box.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "boxes/about_box.h"
|
#include "boxes/about_box.h"
|
||||||
|
|
Loading…
Reference in New Issue