diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index b918dfb80..0cff6472b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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"; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 6d1e89279..c885d0819 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -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) }, diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index 686e82e87..3c765d0c1 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -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 PrivacyExceptionsBoxControl EditPrivacyBox::EditPrivacyBox( QWidget*, std::unique_ptr controller, - rpl::producer 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(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(crl::guard(this, [this, exception] { - return _controller->exceptionBoxTitle(exception); - }), exceptionUsers(exception)); - auto initBox = [this, exception, controller = controller.get()](not_null box) { - box->addButton(langFactory(lng_settings_save), crl::guard(this, [this, box, exception, controller] { +void EditPrivacyBox::editExceptionUsers( + Exception exception, + Fn done) { + auto controller = std::make_unique( + crl::guard(this, [=] { + return _controller->exceptionBoxTitle(exception); + }), + exceptionUsers(exception)); + auto initBox = [=, controller = controller.get()]( + not_null 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(std::move(controller), std::move(initBox)), LayerOption::KeepOther); } -QString EditPrivacyBox::exceptionLinkText(Exception exception) { - return _controller->exceptionLinkText(exception, exceptionUsers(exception).size()); -} - QVector EditPrivacyBox::collectResult() { auto collectInputUsers = [](auto &users) { auto result = QVector(); @@ -237,19 +152,18 @@ QVector EditPrivacyBox::collectResult() { if (showExceptionLink(Exception::Never) && !_value.never.empty()) { result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector(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> &EditPrivacyBox::exceptionUsers(Exception exception) { switch (exception) { case Exception::Always: return _value.always; @@ -258,154 +172,165 @@ std::vector> &EditPrivacyBox::exceptionUsers(Exception excep Unexpected("Invalid exception value."); } -object_ptr> &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>(_value.option); - - auto createOption = [this](object_ptr> &widget, Option option, const QString &label) { - if (_controller->hasOption(option) || (_value.option == option)) { - widget.create(this, _optionGroup, option, label, st::defaultBoxCheckbox); +Ui::Radioenum *EditPrivacyBox::AddOption( + not_null container, + const std::shared_ptr> &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>( + container, + group, + option, + lang(label), + st::settingsSendType), + st::settingsSendTypePadding); +} + +Ui::FlatLabel *EditPrivacyBox::AddLabel( + not_null container, + rpl::producer text) { + const auto wrap = container->add( + object_ptr>( + container, + object_ptr( + 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(this); + const auto content = wrap.data(); + setInnerWidget(object_ptr( + this, + std::move(wrap))); + + const auto group = std::make_shared>( + _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 &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(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>( + content, + object_ptr