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_turn_off" = "Turn off";
"lng_passcode_autolock" = "Auto-Lock";
"lng_passcode_autolock_away" = "Auto-Lock if away for:";
"lng_passcode_autolock_inactive" = "Auto-Lock if inactive for:";
"lng_passcode_autolock_away" = "Auto-Lock if away for...";
"lng_passcode_autolock_inactive" = "Auto-Lock if inactive for...";
"lng_passcode_autolock_minutes#one" = "{count} minute";
"lng_passcode_autolock_minutes#other" = "{count} minutes";
"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_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_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#one" = "Never share with {count} user";
"lng_edit_privacy_lastseen_never#other" = "Never share with {count} users";
"lng_edit_privacy_exceptions_count#one" = "{count} user";
"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_always_title" = "Always share with";
"lng_edit_privacy_lastseen_never_title" = "Never share with";
"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#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#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_always_title" = "Always allow";
"lng_edit_privacy_groups_never_title" = "Never allow";
"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#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#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_always_title" = "Always allow";
"lng_edit_privacy_calls_never_title" = "Never allow";

View File

@ -619,27 +619,6 @@ colorValueInput: InputField(defaultInputField) {
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 {
{ "phone_simcard_from", changePhoneSimcardFrom },
{ "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 "styles/style_boxes.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "history/history.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 "base/binary_guard.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "styles/style_settings.h"
#include "styles/style_boxes.h"
namespace {
@ -82,111 +86,32 @@ std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxControl
EditPrivacyBox::EditPrivacyBox(
QWidget*,
std::unique_ptr<Controller> controller,
rpl::producer<Value> preloaded)
const Value &value)
: _controller(std::move(controller))
, _loading(this, lang(lng_contacts_loading), Ui::FlatLabel::InitType::Simple, st::membersAbout) {
std::move(
preloaded
) | rpl::take(
1
) | rpl::start_with_next([=](Value &&data) {
dataReady(std::move(data));
}, lifetime());
, _value(value) {
}
void EditPrivacyBox::prepare() {
_controller->setView(this);
setTitle([this] { return _controller->title(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setTitle([=] { return _controller->title(); });
if (_loading) {
_prepared = true;
} else {
createWidgets();
}
setDimensions(st::boxWideWidth, countDefaultHeight(st::boxWideWidth));
setupContent();
}
int EditPrivacyBox::resizeGetHeight(int newWidth) {
auto top = 0;
auto layoutRow = [&](auto &widget, style::margins padding) {
if (!widget) return;
widget->resizeToNaturalWidth(newWidth - padding.left() - padding.right());
widget->moveToLeft(padding.left(), top + padding.top());
top = widget->bottomNoMargins() + padding.bottom();
};
layoutRow(_description, st::editPrivacyPadding);
layoutRow(_everyone, st::editPrivacyOptionMargin);
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] {
void EditPrivacyBox::editExceptionUsers(
Exception exception,
Fn<void()> done) {
auto controller = std::make_unique<PrivacyExceptionsBoxController>(
crl::guard(this, [=] {
return _controller->exceptionBoxTitle(exception);
}),
exceptionUsers(exception));
auto initBox = [=, controller = controller.get()](
not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_settings_save), crl::guard(this, [=] {
exceptionUsers(exception) = controller->getResult();
exceptionLink(exception)->entity()->setText(exceptionLinkText(exception));
auto removeFrom = ([exception] {
const auto removeFrom = ([=] {
switch (exception) {
case Exception::Always: return Exception::Never;
case Exception::Never: return Exception::Always;
@ -194,30 +119,20 @@ void EditPrivacyBox::editExceptionUsers(Exception exception) {
Unexpected("Invalid exception value.");
})();
auto &removeFromUsers = exceptionUsers(removeFrom);
auto removedSome = false;
for (auto user : exceptionUsers(exception)) {
auto removedStart = std::remove(removeFromUsers.begin(), removeFromUsers.end(), user);
if (removedStart != removeFromUsers.end()) {
removeFromUsers.erase(removedStart, removeFromUsers.end());
removedSome = true;
}
}
if (removedSome) {
exceptionLink(removeFrom)->entity()->setText(exceptionLinkText(removeFrom));
for (const auto user : exceptionUsers(exception)) {
const auto from = ranges::remove(removeFromUsers, user);
removeFromUsers.erase(from, end(removeFromUsers));
}
done();
box->closeBox();
}));
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
box->addButton(langFactory(lng_cancel), [=] { box->closeBox(); });
};
Ui::show(
Box<PeerListBox>(std::move(controller), std::move(initBox)),
LayerOption::KeepOther);
}
QString EditPrivacyBox::exceptionLinkText(Exception exception) {
return _controller->exceptionLinkText(exception, exceptionUsers(exception).size());
}
QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
auto collectInputUsers = [](auto &users) {
auto result = QVector<MTPInputUser>();
@ -237,19 +152,18 @@ QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
if (showExceptionLink(Exception::Never) && !_value.never.empty()) {
result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector<MTPInputUser>(collectInputUsers(_value.never))));
}
switch (_value.option) {
case Option::Everyone: result.push_back(MTP_inputPrivacyValueAllowAll()); break;
case Option::Contacts: result.push_back(MTP_inputPrivacyValueAllowContacts()); break;
case Option::Nobody: result.push_back(MTP_inputPrivacyValueDisallowAll()); break;
}
result.push_back([&] {
switch (_value.option) {
case Option::Everyone: return MTP_inputPrivacyValueAllowAll();
case Option::Contacts: return MTP_inputPrivacyValueAllowContacts();
case Option::Nobody: return MTP_inputPrivacyValueDisallowAll();
}
Unexpected("Option value in EditPrivacyBox::collectResult.");
}());
return result;
}
style::margins EditPrivacyBox::exceptionLinkMargins() const {
return st::editPrivacyLinkMargin;
}
std::vector<not_null<UserData*>> &EditPrivacyBox::exceptionUsers(Exception exception) {
switch (exception) {
case Exception::Always: return _value.always;
@ -258,154 +172,165 @@ std::vector<not_null<UserData*>> &EditPrivacyBox::exceptionUsers(Exception excep
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 {
switch (exception) {
case Exception::Always: return (_value.option == Option::Contacts) || (_value.option == Option::Nobody);
case Exception::Never: return (_value.option == Option::Everyone) || (_value.option == Option::Contacts);
case Exception::Always:
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.");
}
void EditPrivacyBox::createWidgets() {
_loading.destroy();
_optionGroup = std::make_shared<Ui::RadioenumGroup<Option>>(_value.option);
auto createOption = [this](object_ptr<Ui::Radioenum<Option>> &widget, Option option, const QString &label) {
if (_controller->hasOption(option) || (_value.option == option)) {
widget.create(this, _optionGroup, option, label, st::defaultBoxCheckbox);
Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::AddOption(
not_null<Ui::VerticalLayout*> container,
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
Option option) {
const auto label = [&] {
switch (option) {
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) {
if (text.isEmpty()) {
return;
}
widget.create(this, text, Ui::FlatLabel::InitType::Simple, st);
};
auto createExceptionLink = [this](Exception exception) {
exceptionLink(exception).create(this, object_ptr<Ui::LinkButton>(this, exceptionLinkText(exception)), exceptionLinkMargins());
exceptionLink(exception)->heightValue(
) | rpl::start_with_next([this] {
resizeToWidth(width());
}, lifetime());
exceptionLink(exception)->entity()->setClickedCallback([this, exception] { editExceptionUsers(exception); });
const auto addExceptionLink = [=](Exception exception) {
const auto update = Ui::AttachAsChild(
content,
rpl::event_stream<>());
auto label = update->events_starting_with(
rpl::empty_value()
) | rpl::map([=] {
return exceptionUsers(exception).size();
}) | rpl::map([](int count) {
return count
? lng_edit_privacy_exceptions_count(lt_count, count)
: lang(lng_edit_privacy_exceptions_add);
});
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);
createOption(_everyone, Option::Everyone, lang(lng_edit_privacy_everyone));
createOption(_contacts, Option::Contacts, lang(lng_edit_privacy_contacts));
createOption(_nobody, Option::Nobody, lang(lng_edit_privacy_nobody));
createLabel(_warning, _controller->warning(), st::editPrivacyLabel);
createLabel(_exceptionsTitle, lang(lng_edit_privacy_exceptions), st::editPrivacyTitle);
createExceptionLink(Exception::Always);
createExceptionLink(Exception::Never);
createLabel(_exceptionsDescription, _controller->exceptionsDescription(), st::editPrivacyLabel);
AddSubsectionTitle(content, _controller->optionsTitleKey());
addOption(Option::Everyone);
addOption(Option::Contacts);
addOption(Option::Nobody);
AddLabel(content, _controller->warning());
AddSkip(content);
clearButtons();
addButton(langFactory(lng_settings_save), [this] {
auto someAreDisallowed = (_value.option != Option::Everyone) || !_value.never.empty();
_controller->confirmSave(someAreDisallowed, crl::guard(this, [this] {
AddDivider(content);
AddSkip(content);
AddSubsectionTitle(content, lng_edit_privacy_exceptions);
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(
_controller->apiKey(),
collectResult());
if (saveAdditional) {
saveAdditional();
}
closeBox();
}));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
_optionGroup->setChangedCallback([this](Option value) {
_value.option = value;
_alwaysLink->toggle(
showExceptionLink(Exception::Always),
anim::type::normal);
_neverLink->toggle(
showExceptionLink(Exception::Never),
anim::type::normal);
});
const auto linkHeight = st::settingsButton.padding.top()
+ st::settingsButton.height
+ st::settingsButton.padding.bottom();
showChildren();
_alwaysLink->toggle(
showExceptionLink(Exception::Always),
anim::type::instant);
_neverLink->toggle(
showExceptionLink(Exception::Never),
anim::type::instant);
widthValue(
) | rpl::start_with_next([=](int width) {
content->resizeToWidth(width);
}, content->lifetime());
setDimensions(st::boxWideWidth, resizeGetHeight(st::boxWideWidth));
}
void EditPrivacyBox::dataReady(Value &&value) {
_value = std::move(value);
_loading.destroy();
if (_prepared) {
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();
content->heightValue(
) | rpl::map([=](int height) {
return height - always->height() - never->height() + 2 * linkHeight;
}) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](int height) {
setDimensions(st::boxWideWidth, height);
}, content->lifetime());
}

View File

@ -11,11 +11,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "apiwrap.h"
enum LangKey : int;
namespace Calls {
enum class PeerToPeer;
} // namespace Calls
namespace Ui {
class VerticalLayout;
class FlatLabel;
class LinkButton;
template <typename Enum>
@ -46,17 +49,23 @@ public:
virtual bool hasOption(Option option) {
return true;
}
virtual QString description() = 0;
virtual QString warning() {
return QString();
virtual LangKey optionsTitleKey() = 0;
virtual rpl::producer<QString> warning() {
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 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();
}
virtual Fn<void()> setupAdditional(
not_null<Ui::VerticalLayout*> container) {
return nullptr;
}
virtual ~Controller() = default;
@ -79,59 +88,28 @@ public:
EditPrivacyBox(
QWidget*,
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:
void prepare() override;
int resizeGetHeight(int newWidth) override;
void resizeEvent(QResizeEvent *e) override;
private:
style::margins exceptionLinkMargins() const;
bool showExceptionLink(Exception exception) const;
void createWidgets();
void setupContent();
QVector<MTPInputPrivacyRule> collectResult();
void dataReady(Value &&value);
int countDefaultHeight(int newWidth);
void editExceptionUsers(Exception exception);
QString exceptionLinkText(Exception exception);
void editExceptionUsers(Exception exception, Fn<void()> done);
std::vector<not_null<UserData*>> &exceptionUsers(Exception exception);
object_ptr<Ui::SlideWrap<Ui::LinkButton>> &exceptionLink(
Exception exception);
std::unique_ptr<Controller> _controller;
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;
}
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>());
});
SetupExport(container);
SetupLocalStorage(container);
SetupExport(container);
AddSkip(container, st::settingsCheckboxesSkip);
}
@ -688,8 +688,8 @@ void Chat::setupContent() {
SetupStickersEmoji(content);
SetupMessages(content);
SetupChatBackground(content);
SetupDataStorage(content);
SetupChatBackground(content);
SetupThemeOptions(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_common.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "observer_peer.h"
@ -14,8 +15,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "auth_session.h"
#include "storage/localstorage.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/confirm_box.h"
#include "styles/style_boxes.h"
#include "styles/style_settings.h"
namespace Settings {
namespace {
@ -232,18 +238,21 @@ QString LastSeenPrivacyController::title() {
return lang(lng_edit_privacy_lastseen_title);
}
QString LastSeenPrivacyController::description() {
return lang(lng_edit_privacy_lastseen_description);
LangKey LastSeenPrivacyController::optionsTitleKey() {
return lng_edit_privacy_lastseen_header;
}
QString LastSeenPrivacyController::warning() {
return lang(lng_edit_privacy_lastseen_warning);
rpl::producer<QString> LastSeenPrivacyController::warning() {
return Lang::Viewer(lng_edit_privacy_lastseen_warning);
}
QString LastSeenPrivacyController::exceptionLinkText(Exception exception, int count) {
LangKey LastSeenPrivacyController::exceptionButtonTextKey(
Exception 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::Never: return (count > 0) ? lng_edit_privacy_lastseen_never(lt_count, count) : lang(lng_edit_privacy_lastseen_never_empty);
case Exception::Always:
return lng_edit_privacy_lastseen_always_empty;
case Exception::Never:
return lng_edit_privacy_lastseen_never_empty;
}
Unexpected("Invalid exception value.");
}
@ -256,8 +265,8 @@ QString LastSeenPrivacyController::exceptionBoxTitle(Exception exception) {
Unexpected("Invalid exception value.");
}
QString LastSeenPrivacyController::exceptionsDescription() {
return lang(lng_edit_privacy_lastseen_exceptions);
rpl::producer<QString> LastSeenPrivacyController::exceptionsDescription() {
return Lang::Viewer(lng_edit_privacy_lastseen_exceptions);
}
void LastSeenPrivacyController::confirmSave(bool someAreDisallowed, FnMut<void()> saveCallback) {
@ -294,14 +303,15 @@ bool GroupsInvitePrivacyController::hasOption(Option option) {
return (option != Option::Nobody);
}
QString GroupsInvitePrivacyController::description() {
return lang(lng_edit_privacy_groups_description);
LangKey GroupsInvitePrivacyController::optionsTitleKey() {
return lng_edit_privacy_groups_header;
}
QString GroupsInvitePrivacyController::exceptionLinkText(Exception exception, int count) {
LangKey GroupsInvitePrivacyController::exceptionButtonTextKey(
Exception 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::Never: return (count > 0) ? lng_edit_privacy_groups_never(lt_count, count) : lang(lng_edit_privacy_groups_never_empty);
case Exception::Always: return lng_edit_privacy_groups_always_empty;
case Exception::Never: return lng_edit_privacy_groups_never_empty;
}
Unexpected("Invalid exception value.");
}
@ -314,8 +324,9 @@ QString GroupsInvitePrivacyController::exceptionBoxTitle(Exception exception) {
Unexpected("Invalid exception value.");
}
QString GroupsInvitePrivacyController::exceptionsDescription() {
return lang(lng_edit_privacy_groups_exceptions);
auto GroupsInvitePrivacyController::exceptionsDescription()
-> rpl::producer<QString> {
return Lang::Viewer(lng_edit_privacy_groups_exceptions);
}
ApiWrap::Privacy::Key CallsPrivacyController::key() {
@ -330,14 +341,15 @@ QString CallsPrivacyController::title() {
return lang(lng_edit_privacy_calls_title);
}
QString CallsPrivacyController::description() {
return lang(lng_edit_privacy_calls_description);
LangKey CallsPrivacyController::optionsTitleKey() {
return lng_edit_privacy_calls_header;
}
QString CallsPrivacyController::exceptionLinkText(Exception exception, int count) {
LangKey CallsPrivacyController::exceptionButtonTextKey(
Exception 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::Never: return (count > 0) ? lng_edit_privacy_calls_never(lt_count, count) : lang(lng_edit_privacy_calls_never_empty);
case Exception::Always: return lng_edit_privacy_calls_always_empty;
case Exception::Never: return lng_edit_privacy_calls_never_empty;
}
Unexpected("Invalid exception value.");
}
@ -350,8 +362,54 @@ QString CallsPrivacyController::exceptionBoxTitle(Exception exception) {
Unexpected("Invalid exception value.");
}
QString CallsPrivacyController::exceptionsDescription() {
return lang(lng_edit_privacy_calls_exceptions);
rpl::producer<QString> CallsPrivacyController::exceptionsDescription() {
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

View File

@ -45,11 +45,11 @@ public:
MTPInputPrivacyKey apiKey() override;
QString title() override;
QString description() override;
QString warning() override;
QString exceptionLinkText(Exception exception, int count) override;
LangKey optionsTitleKey() override;
rpl::producer<QString> warning() override;
LangKey exceptionButtonTextKey(Exception exception) override;
QString exceptionBoxTitle(Exception exception) override;
QString exceptionsDescription() override;
rpl::producer<QString> exceptionsDescription() override;
void confirmSave(bool someAreDisallowed, FnMut<void()> saveCallback) override;
@ -65,10 +65,10 @@ public:
QString title() override;
bool hasOption(Option option) override;
QString description() override;
QString exceptionLinkText(Exception exception, int count) override;
LangKey optionsTitleKey() override;
LangKey exceptionButtonTextKey(Exception exception) override;
QString exceptionBoxTitle(Exception exception) override;
QString exceptionsDescription() override;
rpl::producer<QString> exceptionsDescription() override;
};
@ -81,10 +81,13 @@ public:
MTPInputPrivacyKey apiKey() override;
QString title() override;
QString description() override;
QString exceptionLinkText(Exception exception, int count) override;
LangKey optionsTitleKey() override;
LangKey exceptionButtonTextKey(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 shower = Ui::AttachAsChild(container, rpl::lifetime());
AddButtonWithLabel(
container,
label,
PrivacyString(key),
st::settingsButton
)->addClickHandler([=] {
Ui::show(Box<EditPrivacyBox>(
controller(),
Auth().api().privacyValue(key)));
*shower = Auth().api().privacyValue(
key
) | rpl::take(
1
) | rpl::start_with_next([=](const Privacy &value) {
Ui::show(Box<EditPrivacyBox>(
controller(),
value));
});
});
};
add(
@ -418,41 +425,6 @@ void SetupSessionsList(not_null<Ui::VerticalLayout*> container) {
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
PrivacySecurity::PrivacySecurity(QWidget *parent, not_null<UserData*> self)
@ -469,7 +441,6 @@ void PrivacySecurity::setupContent() {
SetupCloudPassword(content);
SetupSessionsList(content);
SetupSelfDestruction(content);
SetupCalls(content);
Ui::ResizeFitChild(this, content);
}