Improve edit privacy box design.

Also move calls peer-to-peer settings to EditPrivacyBox.
This commit is contained in:
John Preston 2018-09-19 12:06:21 +03:00
parent 3ba2a7931e
commit 9388e154cf
9 changed files with 316 additions and 409 deletions

View File

@ -425,8 +425,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passcode_remove" = "Remove local passcode"; "lng_passcode_remove" = "Remove local passcode";
"lng_passcode_turn_off" = "Turn off"; "lng_passcode_turn_off" = "Turn off";
"lng_passcode_autolock" = "Auto-Lock"; "lng_passcode_autolock" = "Auto-Lock";
"lng_passcode_autolock_away" = "Auto-Lock if away for:"; "lng_passcode_autolock_away" = "Auto-Lock if away for...";
"lng_passcode_autolock_inactive" = "Auto-Lock if inactive for:"; "lng_passcode_autolock_inactive" = "Auto-Lock if inactive for...";
"lng_passcode_autolock_minutes#one" = "{count} minute"; "lng_passcode_autolock_minutes#one" = "{count} minute";
"lng_passcode_autolock_minutes#other" = "{count} minutes"; "lng_passcode_autolock_minutes#other" = "{count} minutes";
"lng_passcode_autolock_hours#one" = "{count} hour"; "lng_passcode_autolock_hours#one" = "{count} hour";
@ -558,38 +558,29 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_exceptions" = "Add exceptions"; "lng_edit_privacy_exceptions" = "Add exceptions";
"lng_edit_privacy_lastseen_title" = "Last seen privacy"; "lng_edit_privacy_lastseen_title" = "Last seen privacy";
"lng_edit_privacy_lastseen_description" = "You can choose who can see your last seen time:"; "lng_edit_privacy_lastseen_header" = "Who can see your last seen time";
"lng_edit_privacy_lastseen_warning" = "Important: you won't be able to see Last Seen times for people with whom you don't share your Last Seen time. Approximate last seen will be shown instead (recently, within a week, within a month)."; "lng_edit_privacy_lastseen_warning" = "Important: you won't be able to see Last Seen times for people with whom you don't share your Last Seen time. Approximate last seen will be shown instead (recently, within a week, within a month).";
"lng_edit_privacy_lastseen_always_empty" = "Always share with"; "lng_edit_privacy_lastseen_always_empty" = "Always share with";
"lng_edit_privacy_lastseen_always#one" = "Always share with {count} user";
"lng_edit_privacy_lastseen_always#other" = "Always share with {count} users";
"lng_edit_privacy_lastseen_never_empty" = "Never share with"; "lng_edit_privacy_lastseen_never_empty" = "Never share with";
"lng_edit_privacy_lastseen_never#one" = "Never share with {count} user"; "lng_edit_privacy_exceptions_count#one" = "{count} user";
"lng_edit_privacy_lastseen_never#other" = "Never share with {count} users"; "lng_edit_privacy_exceptions_count#other" = "{count} users";
"lng_edit_privacy_exceptions_add" = "Add users";
"lng_edit_privacy_lastseen_exceptions" = "These settings will override the values above."; "lng_edit_privacy_lastseen_exceptions" = "These settings will override the values above.";
"lng_edit_privacy_lastseen_always_title" = "Always share with"; "lng_edit_privacy_lastseen_always_title" = "Always share with";
"lng_edit_privacy_lastseen_never_title" = "Never share with"; "lng_edit_privacy_lastseen_never_title" = "Never share with";
"lng_edit_privacy_groups_title" = "Group invite settings"; "lng_edit_privacy_groups_title" = "Group invite settings";
"lng_edit_privacy_groups_description" = "You can choose who can add you to groups and channels with granular precision:"; "lng_edit_privacy_groups_header" = "Who can invite you to groups and channels";
"lng_edit_privacy_groups_always_empty" = "Always allow"; "lng_edit_privacy_groups_always_empty" = "Always allow";
"lng_edit_privacy_groups_always#one" = "Always allow {count} user";
"lng_edit_privacy_groups_always#other" = "Always allow {count} users";
"lng_edit_privacy_groups_never_empty" = "Never allow"; "lng_edit_privacy_groups_never_empty" = "Never allow";
"lng_edit_privacy_groups_never#one" = "Never allow {count} user";
"lng_edit_privacy_groups_never#other" = "Never allow {count} users";
"lng_edit_privacy_groups_exceptions" = "These users will or will not be able to add you to groups and channels regardless of the settings above."; "lng_edit_privacy_groups_exceptions" = "These users will or will not be able to add you to groups and channels regardless of the settings above.";
"lng_edit_privacy_groups_always_title" = "Always allow"; "lng_edit_privacy_groups_always_title" = "Always allow";
"lng_edit_privacy_groups_never_title" = "Never allow"; "lng_edit_privacy_groups_never_title" = "Never allow";
"lng_edit_privacy_calls_title" = "Telegram call privacy"; "lng_edit_privacy_calls_title" = "Telegram call privacy";
"lng_edit_privacy_calls_description" = "You can restrict who can call you:"; "lng_edit_privacy_calls_header" = "Who can call you";
"lng_edit_privacy_calls_always_empty" = "Always allow"; "lng_edit_privacy_calls_always_empty" = "Always allow";
"lng_edit_privacy_calls_always#one" = "Always allow {count} user";
"lng_edit_privacy_calls_always#other" = "Always allow {count} users";
"lng_edit_privacy_calls_never_empty" = "Never allow"; "lng_edit_privacy_calls_never_empty" = "Never allow";
"lng_edit_privacy_calls_never#one" = "Never allow {count} user";
"lng_edit_privacy_calls_never#other" = "Never allow {count} users";
"lng_edit_privacy_calls_exceptions" = "These users will or will not be able to call you regardless of the settings above."; "lng_edit_privacy_calls_exceptions" = "These users will or will not be able to call you regardless of the settings above.";
"lng_edit_privacy_calls_always_title" = "Always allow"; "lng_edit_privacy_calls_always_title" = "Always allow";
"lng_edit_privacy_calls_never_title" = "Never allow"; "lng_edit_privacy_calls_never_title" = "Never allow";

View File

@ -619,27 +619,6 @@ colorValueInput: InputField(defaultInputField) {
colorResultInput: InputField(colorValueInput) { colorResultInput: InputField(colorValueInput) {
} }
editPrivacyOptionMargin: margins(23px, 14px, 21px, 0px);
editPrivacyPadding: margins(23px, 0px, 21px, 0px);
editPrivacyWarningPadding: margins(23px, 14px, 21px, 0px);
editPrivacyTitle: FlatLabel(defaultFlatLabel) {
minWidth: 320px;
textFg: boxTitleFg;
maxHeight: 56px;
style: TextStyle(defaultTextStyle) {
font: boxTitleFont;
linkFont: boxTitleFont;
linkFontOver: boxTitleFont;
}
}
editPrivacyTitlePadding: margins(23px, 20px, 21px, 13px);
editPrivacyLabel: FlatLabel(defaultFlatLabel) {
minWidth: 320px;
textFg: membersAboutLimitFg;
style: defaultTextStyle;
}
editPrivacyLinkMargin: margins(0px, 0px, 0px, 8px);
changePhoneIcon: icon { changePhoneIcon: icon {
{ "phone_simcard_from", changePhoneSimcardFrom }, { "phone_simcard_from", changePhoneSimcardFrom },
{ "phone_simcard_migrate", changePhoneSimcardTo, point(30px, 11px) }, { "phone_simcard_migrate", changePhoneSimcardTo, point(30px, 11px) },

View File

@ -7,18 +7,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "boxes/edit_privacy_box.h" #include "boxes/edit_privacy_box.h"
#include "styles/style_boxes.h"
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/wrap/slide_wrap.h" #include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "history/history.h" #include "history/history.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "info/profile/info_profile_button.h"
#include "settings/settings_common.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "base/binary_guard.h" #include "base/binary_guard.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "auth_session.h" #include "auth_session.h"
#include "styles/style_settings.h"
#include "styles/style_boxes.h"
namespace { namespace {
@ -82,111 +86,32 @@ std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxControl
EditPrivacyBox::EditPrivacyBox( EditPrivacyBox::EditPrivacyBox(
QWidget*, QWidget*,
std::unique_ptr<Controller> controller, std::unique_ptr<Controller> controller,
rpl::producer<Value> preloaded) const Value &value)
: _controller(std::move(controller)) : _controller(std::move(controller))
, _loading(this, lang(lng_contacts_loading), Ui::FlatLabel::InitType::Simple, st::membersAbout) { , _value(value) {
std::move(
preloaded
) | rpl::take(
1
) | rpl::start_with_next([=](Value &&data) {
dataReady(std::move(data));
}, lifetime());
} }
void EditPrivacyBox::prepare() { void EditPrivacyBox::prepare() {
_controller->setView(this); _controller->setView(this);
setTitle([this] { return _controller->title(); }); setTitle([=] { return _controller->title(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
if (_loading) { setupContent();
_prepared = true;
} else {
createWidgets();
}
setDimensions(st::boxWideWidth, countDefaultHeight(st::boxWideWidth));
} }
int EditPrivacyBox::resizeGetHeight(int newWidth) { void EditPrivacyBox::editExceptionUsers(
auto top = 0; Exception exception,
auto layoutRow = [&](auto &widget, style::margins padding) { Fn<void()> done) {
if (!widget) return; auto controller = std::make_unique<PrivacyExceptionsBoxController>(
widget->resizeToNaturalWidth(newWidth - padding.left() - padding.right()); crl::guard(this, [=] {
widget->moveToLeft(padding.left(), top + padding.top()); return _controller->exceptionBoxTitle(exception);
top = widget->bottomNoMargins() + padding.bottom(); }),
}; exceptionUsers(exception));
auto initBox = [=, controller = controller.get()](
layoutRow(_description, st::editPrivacyPadding); not_null<PeerListBox*> box) {
layoutRow(_everyone, st::editPrivacyOptionMargin); box->addButton(langFactory(lng_settings_save), crl::guard(this, [=] {
layoutRow(_contacts, st::editPrivacyOptionMargin);
layoutRow(_nobody, st::editPrivacyOptionMargin);
layoutRow(_warning, st::editPrivacyWarningPadding);
layoutRow(_exceptionsTitle, st::editPrivacyTitlePadding);
auto linksTop = top;
layoutRow(_alwaysLink, st::editPrivacyPadding);
layoutRow(_neverLink, st::editPrivacyPadding);
auto linksHeight = top - linksTop;
layoutRow(_exceptionsDescription, st::editPrivacyPadding);
// Add full width of both links in any case
auto linkMargins = exceptionLinkMargins();
top -= linksHeight;
top += linkMargins.top() + st::boxLinkButton.font->height + linkMargins.bottom();
top += linkMargins.top() + st::boxLinkButton.font->height + linkMargins.bottom();
return top;
}
void EditPrivacyBox::resizeEvent(QResizeEvent *e) {
if (_loading) {
_loading->moveToLeft((width() - _loading->width()) / 2, height() / 3);
}
}
int EditPrivacyBox::countDefaultHeight(int newWidth) {
auto height = 0;
auto optionHeight = [this](Option option) {
if (!_controller->hasOption(option)) {
return 0;
}
return st::editPrivacyOptionMargin.top() + st::defaultCheck.diameter + st::editPrivacyOptionMargin.bottom();
};
auto labelHeight = [newWidth](const QString &text, const style::FlatLabel &st, style::margins padding) {
if (text.isEmpty()) {
return 0;
}
auto fake = object_ptr<Ui::FlatLabel>(nullptr, text, Ui::FlatLabel::InitType::Simple, st);
fake->resizeToNaturalWidth(newWidth - padding.left() - padding.right());
return padding.top() + fake->heightNoMargins() + padding.bottom();
};
auto linkHeight = [this]() {
auto linkMargins = exceptionLinkMargins();
return linkMargins.top() + st::boxLinkButton.font->height + linkMargins.bottom();
};
height += labelHeight(_controller->description(), st::editPrivacyLabel, st::editPrivacyPadding);
height += optionHeight(Option::Everyone);
height += optionHeight(Option::Contacts);
height += optionHeight(Option::Nobody);
height += labelHeight(_controller->warning(), st::editPrivacyLabel, st::editPrivacyWarningPadding);
height += labelHeight(lang(lng_edit_privacy_exceptions), st::editPrivacyTitle, st::editPrivacyTitlePadding);
height += linkHeight();
height += linkHeight();
height += labelHeight(_controller->exceptionsDescription(), st::editPrivacyLabel, st::editPrivacyPadding);
return height;
}
void EditPrivacyBox::editExceptionUsers(Exception exception) {
auto controller = std::make_unique<PrivacyExceptionsBoxController>(crl::guard(this, [this, exception] {
return _controller->exceptionBoxTitle(exception);
}), exceptionUsers(exception));
auto initBox = [this, exception, controller = controller.get()](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_settings_save), crl::guard(this, [this, box, exception, controller] {
exceptionUsers(exception) = controller->getResult(); exceptionUsers(exception) = controller->getResult();
exceptionLink(exception)->entity()->setText(exceptionLinkText(exception)); const auto removeFrom = ([=] {
auto removeFrom = ([exception] {
switch (exception) { switch (exception) {
case Exception::Always: return Exception::Never; case Exception::Always: return Exception::Never;
case Exception::Never: return Exception::Always; case Exception::Never: return Exception::Always;
@ -194,30 +119,20 @@ void EditPrivacyBox::editExceptionUsers(Exception exception) {
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
})(); })();
auto &removeFromUsers = exceptionUsers(removeFrom); auto &removeFromUsers = exceptionUsers(removeFrom);
auto removedSome = false; for (const auto user : exceptionUsers(exception)) {
for (auto user : exceptionUsers(exception)) { const auto from = ranges::remove(removeFromUsers, user);
auto removedStart = std::remove(removeFromUsers.begin(), removeFromUsers.end(), user); removeFromUsers.erase(from, end(removeFromUsers));
if (removedStart != removeFromUsers.end()) {
removeFromUsers.erase(removedStart, removeFromUsers.end());
removedSome = true;
}
}
if (removedSome) {
exceptionLink(removeFrom)->entity()->setText(exceptionLinkText(removeFrom));
} }
done();
box->closeBox(); box->closeBox();
})); }));
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); }); box->addButton(langFactory(lng_cancel), [=] { box->closeBox(); });
}; };
Ui::show( Ui::show(
Box<PeerListBox>(std::move(controller), std::move(initBox)), Box<PeerListBox>(std::move(controller), std::move(initBox)),
LayerOption::KeepOther); LayerOption::KeepOther);
} }
QString EditPrivacyBox::exceptionLinkText(Exception exception) {
return _controller->exceptionLinkText(exception, exceptionUsers(exception).size());
}
QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() { QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
auto collectInputUsers = [](auto &users) { auto collectInputUsers = [](auto &users) {
auto result = QVector<MTPInputUser>(); auto result = QVector<MTPInputUser>();
@ -237,19 +152,18 @@ QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
if (showExceptionLink(Exception::Never) && !_value.never.empty()) { if (showExceptionLink(Exception::Never) && !_value.never.empty()) {
result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector<MTPInputUser>(collectInputUsers(_value.never)))); result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector<MTPInputUser>(collectInputUsers(_value.never))));
} }
switch (_value.option) { result.push_back([&] {
case Option::Everyone: result.push_back(MTP_inputPrivacyValueAllowAll()); break; switch (_value.option) {
case Option::Contacts: result.push_back(MTP_inputPrivacyValueAllowContacts()); break; case Option::Everyone: return MTP_inputPrivacyValueAllowAll();
case Option::Nobody: result.push_back(MTP_inputPrivacyValueDisallowAll()); break; case Option::Contacts: return MTP_inputPrivacyValueAllowContacts();
} case Option::Nobody: return MTP_inputPrivacyValueDisallowAll();
}
Unexpected("Option value in EditPrivacyBox::collectResult.");
}());
return result; return result;
} }
style::margins EditPrivacyBox::exceptionLinkMargins() const {
return st::editPrivacyLinkMargin;
}
std::vector<not_null<UserData*>> &EditPrivacyBox::exceptionUsers(Exception exception) { std::vector<not_null<UserData*>> &EditPrivacyBox::exceptionUsers(Exception exception) {
switch (exception) { switch (exception) {
case Exception::Always: return _value.always; case Exception::Always: return _value.always;
@ -258,154 +172,165 @@ std::vector<not_null<UserData*>> &EditPrivacyBox::exceptionUsers(Exception excep
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
} }
object_ptr<Ui::SlideWrap<Ui::LinkButton>> &EditPrivacyBox::exceptionLink(Exception exception) {
switch (exception) {
case Exception::Always: return _alwaysLink;
case Exception::Never: return _neverLink;
}
Unexpected("Invalid exception value.");
}
bool EditPrivacyBox::showExceptionLink(Exception exception) const { bool EditPrivacyBox::showExceptionLink(Exception exception) const {
switch (exception) { switch (exception) {
case Exception::Always: return (_value.option == Option::Contacts) || (_value.option == Option::Nobody); case Exception::Always:
case Exception::Never: return (_value.option == Option::Everyone) || (_value.option == Option::Contacts); return (_value.option == Option::Contacts)
|| (_value.option == Option::Nobody);
case Exception::Never:
return (_value.option == Option::Everyone)
|| (_value.option == Option::Contacts);
} }
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
} }
void EditPrivacyBox::createWidgets() { Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::AddOption(
_loading.destroy(); not_null<Ui::VerticalLayout*> container,
_optionGroup = std::make_shared<Ui::RadioenumGroup<Option>>(_value.option); const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
Option option) {
auto createOption = [this](object_ptr<Ui::Radioenum<Option>> &widget, Option option, const QString &label) { const auto label = [&] {
if (_controller->hasOption(option) || (_value.option == option)) { switch (option) {
widget.create(this, _optionGroup, option, label, st::defaultBoxCheckbox); case Option::Everyone: return lng_edit_privacy_everyone;
case Option::Contacts: return lng_edit_privacy_contacts;
case Option::Nobody: return lng_edit_privacy_nobody;
} }
Unexpected("Option value in EditPrivacyBox::AddOption.");
}();
return container->add(
object_ptr<Ui::Radioenum<Option>>(
container,
group,
option,
lang(label),
st::settingsSendType),
st::settingsSendTypePadding);
}
Ui::FlatLabel *EditPrivacyBox::AddLabel(
not_null<Ui::VerticalLayout*> container,
rpl::producer<QString> text) {
const auto wrap = container->add(
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
container,
object_ptr<Ui::FlatLabel>(
container,
rpl::duplicate(text),
st::boxDividerLabel),
st::settingsPrivacyEditLabelPadding));
wrap->hide(anim::type::instant);
wrap->toggleOn(std::move(
text
) | rpl::map([](const QString &text) {
return !text.isEmpty();
}));
return wrap->entity();
}
void EditPrivacyBox::setupContent() {
using namespace Settings;
auto wrap = object_ptr<Ui::VerticalLayout>(this);
const auto content = wrap.data();
setInnerWidget(object_ptr<Ui::OverrideMargins>(
this,
std::move(wrap)));
const auto group = std::make_shared<Ui::RadioenumGroup<Option>>(
_value.option);
const auto toggle = Ui::AttachAsChild(content, rpl::event_stream<>());
group->setChangedCallback([=](Option value) {
_value.option = value;
toggle->fire({});
});
const auto addOption = [&](Option option) {
return (_controller->hasOption(option) || (_value.option == option))
? AddOption(content, group, option)
: nullptr;
}; };
auto createLabel = [this](object_ptr<Ui::FlatLabel> &widget, const QString &text, const style::FlatLabel &st) { const auto addExceptionLink = [=](Exception exception) {
if (text.isEmpty()) { const auto update = Ui::AttachAsChild(
return; content,
} rpl::event_stream<>());
widget.create(this, text, Ui::FlatLabel::InitType::Simple, st); auto label = update->events_starting_with(
}; rpl::empty_value()
auto createExceptionLink = [this](Exception exception) { ) | rpl::map([=] {
exceptionLink(exception).create(this, object_ptr<Ui::LinkButton>(this, exceptionLinkText(exception)), exceptionLinkMargins()); return exceptionUsers(exception).size();
exceptionLink(exception)->heightValue( }) | rpl::map([](int count) {
) | rpl::start_with_next([this] { return count
resizeToWidth(width()); ? lng_edit_privacy_exceptions_count(lt_count, count)
}, lifetime()); : lang(lng_edit_privacy_exceptions_add);
exceptionLink(exception)->entity()->setClickedCallback([this, exception] { editExceptionUsers(exception); }); });
auto text = _controller->exceptionButtonTextKey(exception);
const auto button = content->add(
object_ptr<Ui::SlideWrap<Button>>(
content,
object_ptr<Button>(
content,
Lang::Viewer(text),
st::settingsButton)));
CreateRightLabel(
button->entity(),
std::move(label),
st::settingsButton,
text);
button->toggleOn(toggle->events_starting_with(
rpl::empty_value()
) | rpl::map([=] {
return showExceptionLink(exception);
}))->entity()->addClickHandler([=] {
editExceptionUsers(exception, [=] { update->fire({}); });
});
return button;
}; };
createLabel(_description, _controller->description(), st::editPrivacyLabel); AddSubsectionTitle(content, _controller->optionsTitleKey());
createOption(_everyone, Option::Everyone, lang(lng_edit_privacy_everyone)); addOption(Option::Everyone);
createOption(_contacts, Option::Contacts, lang(lng_edit_privacy_contacts)); addOption(Option::Contacts);
createOption(_nobody, Option::Nobody, lang(lng_edit_privacy_nobody)); addOption(Option::Nobody);
createLabel(_warning, _controller->warning(), st::editPrivacyLabel); AddLabel(content, _controller->warning());
createLabel(_exceptionsTitle, lang(lng_edit_privacy_exceptions), st::editPrivacyTitle); AddSkip(content);
createExceptionLink(Exception::Always);
createExceptionLink(Exception::Never);
createLabel(_exceptionsDescription, _controller->exceptionsDescription(), st::editPrivacyLabel);
clearButtons(); AddDivider(content);
addButton(langFactory(lng_settings_save), [this] { AddSkip(content);
auto someAreDisallowed = (_value.option != Option::Everyone) || !_value.never.empty(); AddSubsectionTitle(content, lng_edit_privacy_exceptions);
_controller->confirmSave(someAreDisallowed, crl::guard(this, [this] { const auto always = addExceptionLink(Exception::Always);
const auto never = addExceptionLink(Exception::Never);
AddLabel(content, _controller->exceptionsDescription());
AddSkip(content);
const auto saveAdditional = _controller->setupAdditional(content);
addButton(langFactory(lng_settings_save), [=] {
const auto someAreDisallowed = (_value.option != Option::Everyone)
|| !_value.never.empty();
_controller->confirmSave(someAreDisallowed, crl::guard(this, [=] {
Auth().api().savePrivacy( Auth().api().savePrivacy(
_controller->apiKey(), _controller->apiKey(),
collectResult()); collectResult());
if (saveAdditional) {
saveAdditional();
}
closeBox(); closeBox();
})); }));
}); });
addButton(langFactory(lng_cancel), [this] { closeBox(); }); addButton(langFactory(lng_cancel), [this] { closeBox(); });
_optionGroup->setChangedCallback([this](Option value) { const auto linkHeight = st::settingsButton.padding.top()
_value.option = value; + st::settingsButton.height
_alwaysLink->toggle( + st::settingsButton.padding.bottom();
showExceptionLink(Exception::Always),
anim::type::normal);
_neverLink->toggle(
showExceptionLink(Exception::Never),
anim::type::normal);
});
showChildren(); widthValue(
_alwaysLink->toggle( ) | rpl::start_with_next([=](int width) {
showExceptionLink(Exception::Always), content->resizeToWidth(width);
anim::type::instant); }, content->lifetime());
_neverLink->toggle(
showExceptionLink(Exception::Never),
anim::type::instant);
setDimensions(st::boxWideWidth, resizeGetHeight(st::boxWideWidth)); content->heightValue(
} ) | rpl::map([=](int height) {
return height - always->height() - never->height() + 2 * linkHeight;
void EditPrivacyBox::dataReady(Value &&value) { }) | rpl::distinct_until_changed(
_value = std::move(value); ) | rpl::start_with_next([=](int height) {
_loading.destroy(); setDimensions(st::boxWideWidth, height);
if (_prepared) { }, content->lifetime());
createWidgets();
}
}
void EditCallsPeerToPeer::prepare() {
setTitle(langFactory(lng_settings_peer_to_peer));
addButton(langFactory(lng_box_ok), [=] { closeBox(); });
const auto options = {
PeerToPeer::Everyone,
PeerToPeer::Contacts,
PeerToPeer::Nobody
};
const auto value = Auth().settings().callsPeerToPeer();
const auto adjusted = [&] {
if (value == PeerToPeer::DefaultContacts) {
return PeerToPeer::Contacts;
} else if (value == PeerToPeer::DefaultEveryone) {
return PeerToPeer::Everyone;
}
return value;
}();
const auto label = [](PeerToPeer value) {
switch (value) {
case PeerToPeer::Everyone: return lang(lng_edit_privacy_everyone);
case PeerToPeer::Contacts: return lang(lng_edit_privacy_contacts);
case PeerToPeer::Nobody: return lang(lng_edit_privacy_nobody);
}
Unexpected("Adjusted Calls::PeerToPeer value.");
};
auto group = std::make_shared<Ui::RadioenumGroup<PeerToPeer>>(adjusted);
auto y = st::boxOptionListPadding.top() + st::langsButton.margin.top();
auto count = int(options.size());
_options.reserve(count);
for (const auto option : options) {
_options.emplace_back(
this,
group,
option,
label(option),
st::langsButton);
_options.back()->moveToLeft(
st::boxPadding.left() + st::boxOptionListPadding.left(),
y);
y += _options.back()->heightNoMargins() + st::boxOptionListSkip;
}
group->setChangedCallback([=](PeerToPeer value) { chosen(value); });
setDimensions(
st::langsWidth,
(st::boxOptionListPadding.top()
+ count * _options.back()->heightNoMargins()
+ (count - 1) * st::boxOptionListSkip
+ st::boxOptionListPadding.bottom()
+ st::boxPadding.bottom()));
}
void EditCallsPeerToPeer::chosen(PeerToPeer value) {
Auth().settings().setCallsPeerToPeer(value);
Auth().saveSettingsDelayed();
closeBox();
} }

View File

@ -11,11 +11,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h" #include "mtproto/sender.h"
#include "apiwrap.h" #include "apiwrap.h"
enum LangKey : int;
namespace Calls { namespace Calls {
enum class PeerToPeer; enum class PeerToPeer;
} // namespace Calls } // namespace Calls
namespace Ui { namespace Ui {
class VerticalLayout;
class FlatLabel; class FlatLabel;
class LinkButton; class LinkButton;
template <typename Enum> template <typename Enum>
@ -46,17 +49,23 @@ public:
virtual bool hasOption(Option option) { virtual bool hasOption(Option option) {
return true; return true;
} }
virtual QString description() = 0; virtual LangKey optionsTitleKey() = 0;
virtual QString warning() { virtual rpl::producer<QString> warning() {
return QString(); return rpl::never<QString>();
} }
virtual QString exceptionLinkText(Exception exception, int count) = 0; virtual LangKey exceptionButtonTextKey(Exception exception) = 0;
virtual QString exceptionBoxTitle(Exception exception) = 0; virtual QString exceptionBoxTitle(Exception exception) = 0;
virtual QString exceptionsDescription() = 0; virtual rpl::producer<QString> exceptionsDescription() = 0;
virtual void confirmSave(bool someAreDisallowed, FnMut<void()> saveCallback) { virtual void confirmSave(
bool someAreDisallowed,
FnMut<void()> saveCallback) {
saveCallback(); saveCallback();
} }
virtual Fn<void()> setupAdditional(
not_null<Ui::VerticalLayout*> container) {
return nullptr;
}
virtual ~Controller() = default; virtual ~Controller() = default;
@ -79,59 +88,28 @@ public:
EditPrivacyBox( EditPrivacyBox(
QWidget*, QWidget*,
std::unique_ptr<Controller> controller, std::unique_ptr<Controller> controller,
rpl::producer<Value> preloaded); const Value &value);
static Ui::Radioenum<Option> *AddOption(
not_null<Ui::VerticalLayout*> container,
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
Option option);
static Ui::FlatLabel *AddLabel(
not_null<Ui::VerticalLayout*> container,
rpl::producer<QString> text);
protected: protected:
void prepare() override; void prepare() override;
int resizeGetHeight(int newWidth) override;
void resizeEvent(QResizeEvent *e) override;
private: private:
style::margins exceptionLinkMargins() const;
bool showExceptionLink(Exception exception) const; bool showExceptionLink(Exception exception) const;
void createWidgets(); void setupContent();
QVector<MTPInputPrivacyRule> collectResult(); QVector<MTPInputPrivacyRule> collectResult();
void dataReady(Value &&value);
int countDefaultHeight(int newWidth);
void editExceptionUsers(Exception exception); void editExceptionUsers(Exception exception, Fn<void()> done);
QString exceptionLinkText(Exception exception);
std::vector<not_null<UserData*>> &exceptionUsers(Exception exception); std::vector<not_null<UserData*>> &exceptionUsers(Exception exception);
object_ptr<Ui::SlideWrap<Ui::LinkButton>> &exceptionLink(
Exception exception);
std::unique_ptr<Controller> _controller; std::unique_ptr<Controller> _controller;
Value _value; Value _value;
bool _prepared = false;
std::shared_ptr<Ui::RadioenumGroup<Option>> _optionGroup;
object_ptr<Ui::FlatLabel> _loading;
object_ptr<Ui::FlatLabel> _description = { nullptr };
object_ptr<Ui::Radioenum<Option>> _everyone = { nullptr };
object_ptr<Ui::Radioenum<Option>> _contacts = { nullptr };
object_ptr<Ui::Radioenum<Option>> _nobody = { nullptr };
object_ptr<Ui::FlatLabel> _warning = { nullptr };
object_ptr<Ui::FlatLabel> _exceptionsTitle = { nullptr };
object_ptr<Ui::SlideWrap<Ui::LinkButton>> _alwaysLink = { nullptr };
object_ptr<Ui::SlideWrap<Ui::LinkButton>> _neverLink = { nullptr };
object_ptr<Ui::FlatLabel> _exceptionsDescription = { nullptr };
};
class EditCallsPeerToPeer : public BoxContent {
public:
EditCallsPeerToPeer(QWidget*) {
}
protected:
void prepare() override;
private:
using PeerToPeer = Calls::PeerToPeer;
void chosen(PeerToPeer value);
std::vector<object_ptr<Ui::Radioenum<PeerToPeer>>> _options;
}; };

View File

@ -150,3 +150,5 @@ settingsBioCountdown: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg; textFg: windowSubTextFg;
} }
settingsBioLabelPadding: margins(22px, 11px, 22px, 0px); settingsBioLabelPadding: margins(22px, 11px, 22px, 0px);
settingsPrivacyEditLabelPadding: margins(22px, 11px, 22px, 11px);

View File

@ -530,8 +530,8 @@ void SetupDataStorage(not_null<Ui::VerticalLayout*> container) {
Ui::show(Box<AutoDownloadBox>()); Ui::show(Box<AutoDownloadBox>());
}); });
SetupExport(container);
SetupLocalStorage(container); SetupLocalStorage(container);
SetupExport(container);
AddSkip(container, st::settingsCheckboxesSkip); AddSkip(container, st::settingsCheckboxesSkip);
} }
@ -688,8 +688,8 @@ void Chat::setupContent() {
SetupStickersEmoji(content); SetupStickersEmoji(content);
SetupMessages(content); SetupMessages(content);
SetupChatBackground(content);
SetupDataStorage(content); SetupDataStorage(content);
SetupChatBackground(content);
SetupThemeOptions(content); SetupThemeOptions(content);
Ui::ResizeFitChild(this, content); Ui::ResizeFitChild(this, content);

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "settings/settings_privacy_controllers.h" #include "settings/settings_privacy_controllers.h"
#include "settings/settings_common.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "observer_peer.h" #include "observer_peer.h"
@ -14,8 +15,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "auth_session.h" #include "auth_session.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "history/history.h" #include "history/history.h"
#include "calls/calls_instance.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/vertical_layout.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "styles/style_boxes.h"
#include "styles/style_settings.h"
namespace Settings { namespace Settings {
namespace { namespace {
@ -232,18 +238,21 @@ QString LastSeenPrivacyController::title() {
return lang(lng_edit_privacy_lastseen_title); return lang(lng_edit_privacy_lastseen_title);
} }
QString LastSeenPrivacyController::description() { LangKey LastSeenPrivacyController::optionsTitleKey() {
return lang(lng_edit_privacy_lastseen_description); return lng_edit_privacy_lastseen_header;
} }
QString LastSeenPrivacyController::warning() { rpl::producer<QString> LastSeenPrivacyController::warning() {
return lang(lng_edit_privacy_lastseen_warning); return Lang::Viewer(lng_edit_privacy_lastseen_warning);
} }
QString LastSeenPrivacyController::exceptionLinkText(Exception exception, int count) { LangKey LastSeenPrivacyController::exceptionButtonTextKey(
Exception exception) {
switch (exception) { switch (exception) {
case Exception::Always: return (count > 0) ? lng_edit_privacy_lastseen_always(lt_count, count) : lang(lng_edit_privacy_lastseen_always_empty); case Exception::Always:
case Exception::Never: return (count > 0) ? lng_edit_privacy_lastseen_never(lt_count, count) : lang(lng_edit_privacy_lastseen_never_empty); return lng_edit_privacy_lastseen_always_empty;
case Exception::Never:
return lng_edit_privacy_lastseen_never_empty;
} }
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
} }
@ -256,8 +265,8 @@ QString LastSeenPrivacyController::exceptionBoxTitle(Exception exception) {
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
} }
QString LastSeenPrivacyController::exceptionsDescription() { rpl::producer<QString> LastSeenPrivacyController::exceptionsDescription() {
return lang(lng_edit_privacy_lastseen_exceptions); return Lang::Viewer(lng_edit_privacy_lastseen_exceptions);
} }
void LastSeenPrivacyController::confirmSave(bool someAreDisallowed, FnMut<void()> saveCallback) { void LastSeenPrivacyController::confirmSave(bool someAreDisallowed, FnMut<void()> saveCallback) {
@ -294,14 +303,15 @@ bool GroupsInvitePrivacyController::hasOption(Option option) {
return (option != Option::Nobody); return (option != Option::Nobody);
} }
QString GroupsInvitePrivacyController::description() { LangKey GroupsInvitePrivacyController::optionsTitleKey() {
return lang(lng_edit_privacy_groups_description); return lng_edit_privacy_groups_header;
} }
QString GroupsInvitePrivacyController::exceptionLinkText(Exception exception, int count) { LangKey GroupsInvitePrivacyController::exceptionButtonTextKey(
Exception exception) {
switch (exception) { switch (exception) {
case Exception::Always: return (count > 0) ? lng_edit_privacy_groups_always(lt_count, count) : lang(lng_edit_privacy_groups_always_empty); case Exception::Always: return lng_edit_privacy_groups_always_empty;
case Exception::Never: return (count > 0) ? lng_edit_privacy_groups_never(lt_count, count) : lang(lng_edit_privacy_groups_never_empty); case Exception::Never: return lng_edit_privacy_groups_never_empty;
} }
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
} }
@ -314,8 +324,9 @@ QString GroupsInvitePrivacyController::exceptionBoxTitle(Exception exception) {
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
} }
QString GroupsInvitePrivacyController::exceptionsDescription() { auto GroupsInvitePrivacyController::exceptionsDescription()
return lang(lng_edit_privacy_groups_exceptions); -> rpl::producer<QString> {
return Lang::Viewer(lng_edit_privacy_groups_exceptions);
} }
ApiWrap::Privacy::Key CallsPrivacyController::key() { ApiWrap::Privacy::Key CallsPrivacyController::key() {
@ -330,14 +341,15 @@ QString CallsPrivacyController::title() {
return lang(lng_edit_privacy_calls_title); return lang(lng_edit_privacy_calls_title);
} }
QString CallsPrivacyController::description() { LangKey CallsPrivacyController::optionsTitleKey() {
return lang(lng_edit_privacy_calls_description); return lng_edit_privacy_calls_header;
} }
QString CallsPrivacyController::exceptionLinkText(Exception exception, int count) { LangKey CallsPrivacyController::exceptionButtonTextKey(
Exception exception) {
switch (exception) { switch (exception) {
case Exception::Always: return (count > 0) ? lng_edit_privacy_calls_always(lt_count, count) : lang(lng_edit_privacy_calls_always_empty); case Exception::Always: return lng_edit_privacy_calls_always_empty;
case Exception::Never: return (count > 0) ? lng_edit_privacy_calls_never(lt_count, count) : lang(lng_edit_privacy_calls_never_empty); case Exception::Never: return lng_edit_privacy_calls_never_empty;
} }
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
} }
@ -350,8 +362,54 @@ QString CallsPrivacyController::exceptionBoxTitle(Exception exception) {
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
} }
QString CallsPrivacyController::exceptionsDescription() { rpl::producer<QString> CallsPrivacyController::exceptionsDescription() {
return lang(lng_edit_privacy_calls_exceptions); return Lang::Viewer(lng_edit_privacy_calls_exceptions);
}
Fn<void()> CallsPrivacyController::setupAdditional(
not_null<Ui::VerticalLayout*> container) {
using PeerToPeer = Calls::PeerToPeer;
const auto convert = [](PeerToPeer value) {
switch (value) {
case PeerToPeer::DefaultContacts: return Option::Contacts;
case PeerToPeer::DefaultEveryone: return Option::Everyone;
case PeerToPeer::Everyone: return Option::Everyone;
case PeerToPeer::Contacts: return Option::Contacts;
case PeerToPeer::Nobody: return Option::Nobody;
}
Unexpected("Calls::PeerToPeer value.");
};
const auto group = std::make_shared<Ui::RadioenumGroup<Option>>(
convert(Auth().settings().callsPeerToPeer()));
const auto changed = Ui::AttachAsChild(container, false);
group->setChangedCallback([=](Option) {
*changed = true;
});
AddDivider(container);
AddSkip(container);
AddSubsectionTitle(container, lng_settings_peer_to_peer);
EditPrivacyBox::AddOption(container, group, Option::Everyone);
EditPrivacyBox::AddOption(container, group, Option::Contacts);
EditPrivacyBox::AddOption(container, group, Option::Nobody);
EditPrivacyBox::AddLabel(
container,
Lang::Viewer(lng_settings_peer_to_peer_about));
AddSkip(container);
return [=] {
if (*changed) {
Auth().settings().setCallsPeerToPeer([&] {
switch (group->value()) {
case Option::Everyone: return PeerToPeer::Everyone;
case Option::Contacts: return PeerToPeer::Contacts;
case Option::Nobody: return PeerToPeer::Nobody;
}
Unexpected("PeerToPeer edit value.");
}());
Auth().saveSettingsDelayed();
}
};
} }
} // namespace Settings } // namespace Settings

View File

@ -45,11 +45,11 @@ public:
MTPInputPrivacyKey apiKey() override; MTPInputPrivacyKey apiKey() override;
QString title() override; QString title() override;
QString description() override; LangKey optionsTitleKey() override;
QString warning() override; rpl::producer<QString> warning() override;
QString exceptionLinkText(Exception exception, int count) override; LangKey exceptionButtonTextKey(Exception exception) override;
QString exceptionBoxTitle(Exception exception) override; QString exceptionBoxTitle(Exception exception) override;
QString exceptionsDescription() override; rpl::producer<QString> exceptionsDescription() override;
void confirmSave(bool someAreDisallowed, FnMut<void()> saveCallback) override; void confirmSave(bool someAreDisallowed, FnMut<void()> saveCallback) override;
@ -65,10 +65,10 @@ public:
QString title() override; QString title() override;
bool hasOption(Option option) override; bool hasOption(Option option) override;
QString description() override; LangKey optionsTitleKey() override;
QString exceptionLinkText(Exception exception, int count) override; LangKey exceptionButtonTextKey(Exception exception) override;
QString exceptionBoxTitle(Exception exception) override; QString exceptionBoxTitle(Exception exception) override;
QString exceptionsDescription() override; rpl::producer<QString> exceptionsDescription() override;
}; };
@ -81,10 +81,13 @@ public:
MTPInputPrivacyKey apiKey() override; MTPInputPrivacyKey apiKey() override;
QString title() override; QString title() override;
QString description() override; LangKey optionsTitleKey() override;
QString exceptionLinkText(Exception exception, int count) override; LangKey exceptionButtonTextKey(Exception exception) override;
QString exceptionBoxTitle(Exception exception) override; QString exceptionBoxTitle(Exception exception) override;
QString exceptionsDescription() override; rpl::producer<QString> exceptionsDescription() override;
Fn<void()> setupAdditional(
not_null<Ui::VerticalLayout*> container) override;
}; };

View File

@ -98,15 +98,22 @@ void SetupPrivacy(not_null<Ui::VerticalLayout*> container) {
}); });
}; };
const auto add = [&](LangKey label, Privacy::Key key, auto controller) { const auto add = [&](LangKey label, Privacy::Key key, auto controller) {
const auto shower = Ui::AttachAsChild(container, rpl::lifetime());
AddButtonWithLabel( AddButtonWithLabel(
container, container,
label, label,
PrivacyString(key), PrivacyString(key),
st::settingsButton st::settingsButton
)->addClickHandler([=] { )->addClickHandler([=] {
Ui::show(Box<EditPrivacyBox>( *shower = Auth().api().privacyValue(
controller(), key
Auth().api().privacyValue(key))); ) | rpl::take(
1
) | rpl::start_with_next([=](const Privacy &value) {
Ui::show(Box<EditPrivacyBox>(
controller(),
value));
});
}); });
}; };
add( add(
@ -418,41 +425,6 @@ void SetupSessionsList(not_null<Ui::VerticalLayout*> container) {
Lang::Viewer(lng_settings_sessions_about)); Lang::Viewer(lng_settings_sessions_about));
} }
void SetupCalls(not_null<Ui::VerticalLayout*> container) {
AddDivider(container);
AddSkip(container);
AddSubsectionTitle(container, lng_settings_calls_title);
using Privacy = ApiWrap::Privacy;
using PeerToPeer = Calls::PeerToPeer;
auto text = Auth().settings().callsPeerToPeerValue(
) | rpl::map([](PeerToPeer value) {
switch (value) {
case PeerToPeer::DefaultContacts: return Privacy::Option::Contacts;
case PeerToPeer::DefaultEveryone: return Privacy::Option::Everyone;
case PeerToPeer::Everyone: return Privacy::Option::Everyone;
case PeerToPeer::Contacts: return Privacy::Option::Contacts;
case PeerToPeer::Nobody: return Privacy::Option::Nobody;
}
Unexpected("Calls::PeerToPeer value.");
}) | rpl::map([](Privacy::Option option) {
return PrivacyBase(option);
});
AddButtonWithLabel(
container,
lng_settings_peer_to_peer,
std::move(text),
st::settingsButton
)->addClickHandler([=] {
Ui::show(Box<EditCallsPeerToPeer>());
});
AddSkip(container, st::settingsPrivacySecurityPadding);
AddDividerText(
container,
Lang::Viewer(lng_settings_peer_to_peer_about));
}
} // namespace } // namespace
PrivacySecurity::PrivacySecurity(QWidget *parent, not_null<UserData*> self) PrivacySecurity::PrivacySecurity(QWidget *parent, not_null<UserData*> self)
@ -469,7 +441,6 @@ void PrivacySecurity::setupContent() {
SetupCloudPassword(content); SetupCloudPassword(content);
SetupSessionsList(content); SetupSessionsList(content);
SetupSelfDestruction(content); SetupSelfDestruction(content);
SetupCalls(content);
Ui::ResizeFitChild(this, content); Ui::ResizeFitChild(this, content);
} }