diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 96ee55649..45f656430 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -355,9 +355,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_privacy_title" = "Privacy"; "lng_settings_last_seen" = "Last seen"; "lng_settings_calls" = "Voice calls"; -"lng_settings_calls_peer_to_peer" = "Peer-to-peer in calls"; +"lng_settings_calls_peer_to_peer_title" = "Peer-to-peer"; +"lng_settings_calls_peer_to_peer_button" = "Use peer-to-peer with"; "lng_settings_groups_invite" = "Groups"; "lng_settings_group_privacy_about" = "Change who can add you to groups and channels."; +"lng_settings_phone_number_privacy" = "Phone number"; "lng_settings_forwards_privacy" = "Forwarded messages"; "lng_settings_profile_photo_privacy" = "Profile photo"; "lng_settings_sessions_about" = "Control your sessions on other devices."; @@ -604,6 +606,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_exceptions_count#other" = "{count} users"; "lng_edit_privacy_exceptions_add" = "Add users or groups"; +"lng_edit_privacy_phone_number_title" = "Phone number privacy"; +"lng_edit_privacy_phone_number_header" = "Who can see your phone number"; +"lng_edit_privacy_phone_number_warning" = "Important: blabla."; +"lng_edit_privacy_phone_number_always_empty" = "Always share with"; +"lng_edit_privacy_phone_number_never_empty" = "Never share with"; +"lng_edit_privacy_phone_number_exceptions" = "These settings will override the values above."; +"lng_edit_privacy_phone_number_always_title" = "Always share with"; +"lng_edit_privacy_phone_number_never_title" = "Never share with"; + "lng_edit_privacy_lastseen_title" = "Last seen privacy"; "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)."; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index ccda0bb4f..f80ebd13a 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -173,6 +173,7 @@ MTPInputPrivacyKey ApiWrap::Privacy::Input(Key key) { switch (key) { case Privacy::Key::Calls: return MTP_inputPrivacyKeyPhoneCall(); case Privacy::Key::Invites: return MTP_inputPrivacyKeyChatInvite(); + case Privacy::Key::PhoneNumber: return MTP_inputPrivacyKeyPhoneNumber(); case Privacy::Key::LastSeen: return MTP_inputPrivacyKeyStatusTimestamp(); case Privacy::Key::CallsPeer2Peer: @@ -189,6 +190,8 @@ std::optional<ApiWrap::Privacy::Key> ApiWrap::Privacy::KeyFromMTP( mtpTypeId type) { using Key = Privacy::Key; switch (type) { + case mtpc_privacyKeyPhoneNumber: + case mtpc_inputPrivacyKeyPhoneNumber: return Key::PhoneNumber; case mtpc_privacyKeyStatusTimestamp: case mtpc_inputPrivacyKeyStatusTimestamp: return Key::LastSeen; case mtpc_privacyKeyChatInvite: diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 32a6564d7..51bcbeaa6 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -62,6 +62,7 @@ class ApiWrap : public MTP::Sender, private base::Subscriber { public: struct Privacy { enum class Key { + PhoneNumber, LastSeen, Calls, Invites, diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index 983a1c21c..98fda2c80 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -100,7 +100,7 @@ std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxControl } // namespace -LangKey EditPrivacyBox::Controller::optionLabelKey(Option option) { +LangKey EditPrivacyController::optionLabelKey(Option option) { switch (option) { case Option::Everyone: return lng_edit_privacy_everyone; case Option::Contacts: return lng_edit_privacy_contacts; @@ -111,7 +111,7 @@ LangKey EditPrivacyBox::Controller::optionLabelKey(Option option) { EditPrivacyBox::EditPrivacyBox( QWidget*, - std::unique_ptr<Controller> controller, + std::unique_ptr<EditPrivacyController> controller, const Value &value) : _controller(std::move(controller)) , _value(value) { @@ -350,6 +350,10 @@ void EditPrivacyBox::setupContent() { addLabel(content, _controller->exceptionsDescription()); AddSkip(content); + if (auto below = _controller->setupBelowWidget(content)) { + content->add(std::move(below)); + } + addButton(langFactory(lng_settings_save), [=] { const auto someAreDisallowed = (_value.option != Option::Everyone) || !_value.never.empty(); diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h index cd5636829..4d44db542 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.h +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h @@ -25,71 +25,79 @@ template <typename Widget> class SlideWrap; } // namespace Ui -class EditPrivacyBox : public BoxContent, private MTP::Sender { +class EditPrivacyBox; + +class EditPrivacyController { public: - using Value = ApiWrap::Privacy; - using Option = Value::Option; + using Key = ApiWrap::Privacy::Key; + using Option = ApiWrap::Privacy::Option; enum class Exception { Always, Never, }; - class Controller { - public: - using Key = ApiWrap::Privacy::Key; + [[nodiscard]] virtual Key key() = 0; + [[nodiscard]] virtual MTPInputPrivacyKey apiKey() = 0; - [[nodiscard]] virtual Key key() = 0; - [[nodiscard]] virtual MTPInputPrivacyKey apiKey() = 0; + [[nodiscard]] virtual QString title() = 0; + [[nodiscard]] virtual bool hasOption(Option option) { + return true; + } + [[nodiscard]] virtual LangKey optionsTitleKey() = 0; + [[nodiscard]] virtual LangKey optionLabelKey(Option option); + [[nodiscard]] virtual rpl::producer<QString> warning() { + return rpl::never<QString>(); + } + [[nodiscard]] virtual LangKey exceptionButtonTextKey( + Exception exception) = 0; + [[nodiscard]] virtual QString exceptionBoxTitle( + Exception exception) = 0; + [[nodiscard]] virtual auto exceptionsDescription() + -> rpl::producer<QString> = 0; - [[nodiscard]] virtual QString title() = 0; - [[nodiscard]] virtual bool hasOption(Option option) { - return true; - } - [[nodiscard]] virtual LangKey optionsTitleKey() = 0; - [[nodiscard]] virtual LangKey optionLabelKey(Option option); - [[nodiscard]] virtual rpl::producer<QString> warning() { - return rpl::never<QString>(); - } - [[nodiscard]] virtual LangKey exceptionButtonTextKey( - Exception exception) = 0; - [[nodiscard]] virtual QString exceptionBoxTitle( - Exception exception) = 0; - [[nodiscard]] virtual auto exceptionsDescription() - -> rpl::producer<QString> = 0; + [[nodiscard]] virtual object_ptr<Ui::RpWidget> setupAboveWidget( + not_null<QWidget*> parent, + rpl::producer<Option> option) { + return { nullptr }; + } + [[nodiscard]] virtual object_ptr<Ui::RpWidget> setupBelowWidget( + not_null<QWidget*> parent) { + return { nullptr }; + } - [[nodiscard]] virtual object_ptr<Ui::RpWidget> setupAboveWidget( - not_null<QWidget*> parent, - rpl::producer<Option> option) { - return { nullptr }; - } + virtual void confirmSave( + bool someAreDisallowed, + FnMut<void()> saveCallback) { + saveCallback(); + } - virtual void confirmSave( - bool someAreDisallowed, - FnMut<void()> saveCallback) { - saveCallback(); - } + virtual ~EditPrivacyController() = default; - virtual ~Controller() = default; +protected: + EditPrivacyBox *view() const { + return _view; + } - protected: - EditPrivacyBox *view() const { - return _view; - } +private: + void setView(EditPrivacyBox *box) { + _view = box; + } - private: - void setView(EditPrivacyBox *box) { - _view = box; - } + EditPrivacyBox *_view = nullptr; - EditPrivacyBox *_view = nullptr; + friend class EditPrivacyBox; - friend class EditPrivacyBox; +}; - }; +class EditPrivacyBox : public BoxContent, private MTP::Sender { +public: + using Value = ApiWrap::Privacy; + using Option = Value::Option; + using Exception = EditPrivacyController::Exception; EditPrivacyBox( QWidget*, - std::unique_ptr<Controller> controller, + std::unique_ptr<EditPrivacyController> controller, const Value &value); protected: @@ -111,7 +119,7 @@ private: void editExceptions(Exception exception, Fn<void()> done); std::vector<not_null<PeerData*>> &exceptions(Exception exception); - std::unique_ptr<Controller> _controller; + std::unique_ptr<EditPrivacyController> _controller; Value _value; }; diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index 5d33c00ee..1f26eebae 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/section_widget.h" #include "boxes/peer_list_controllers.h" #include "boxes/confirm_box.h" +#include "settings/settings_privacy_security.h" #include "styles/style_history.h" #include "styles/style_boxes.h" #include "styles/style_settings.h" @@ -295,6 +296,49 @@ std::unique_ptr<PeerListRow> BlockedBoxController::createRow( return std::move(row); } +ApiWrap::Privacy::Key PhoneNumberPrivacyController::key() { + return Key::PhoneNumber; +} + +MTPInputPrivacyKey PhoneNumberPrivacyController::apiKey() { + return MTP_inputPrivacyKeyPhoneNumber(); +} + +QString PhoneNumberPrivacyController::title() { + return lang(lng_edit_privacy_phone_number_title); +} + +LangKey PhoneNumberPrivacyController::optionsTitleKey() { + return lng_edit_privacy_phone_number_header; +} + +rpl::producer<QString> PhoneNumberPrivacyController::warning() { + return Lang::Viewer(lng_edit_privacy_phone_number_warning); +} + +LangKey PhoneNumberPrivacyController::exceptionButtonTextKey( + Exception exception) { + switch (exception) { + case Exception::Always: + return lng_edit_privacy_phone_number_always_empty; + case Exception::Never: + return lng_edit_privacy_phone_number_never_empty; + } + Unexpected("Invalid exception value."); +} + +QString PhoneNumberPrivacyController::exceptionBoxTitle(Exception exception) { + switch (exception) { + case Exception::Always: return lang(lng_edit_privacy_phone_number_always_title); + case Exception::Never: return lang(lng_edit_privacy_phone_number_never_title); + } + Unexpected("Invalid exception value."); +} + +rpl::producer<QString> PhoneNumberPrivacyController::exceptionsDescription() { + return Lang::Viewer(lng_edit_privacy_phone_number_exceptions); +} + ApiWrap::Privacy::Key LastSeenPrivacyController::key() { return Key::LastSeen; } @@ -439,6 +483,24 @@ rpl::producer<QString> CallsPrivacyController::exceptionsDescription() { return Lang::Viewer(lng_edit_privacy_calls_exceptions); } +object_ptr<Ui::RpWidget> CallsPrivacyController::setupBelowWidget( + not_null<QWidget*> parent) { + auto result = object_ptr<Ui::VerticalLayout>(parent); + const auto content = result.data(); + + AddDivider(content); + AddSkip(content); + AddSubsectionTitle(content, lng_settings_calls_peer_to_peer_title); + Settings::AddPrivacyButton( + content, + lng_settings_calls_peer_to_peer_button, + ApiWrap::Privacy::Key::CallsPeer2Peer, + [] { return std::make_unique<CallsPeer2PeerPrivacyController>(); }); + AddSkip(content); + + return result; +} + ApiWrap::Privacy::Key CallsPeer2PeerPrivacyController::key() { return Key::CallsPeer2Peer; } diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.h b/Telegram/SourceFiles/settings/settings_privacy_controllers.h index 1eff6b321..c43f5360a 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.h +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.h @@ -37,7 +37,24 @@ private: }; -class LastSeenPrivacyController : public EditPrivacyBox::Controller { +class PhoneNumberPrivacyController : public EditPrivacyController { +public: + using Option = EditPrivacyBox::Option; + using Exception = EditPrivacyBox::Exception; + + Key key() override; + MTPInputPrivacyKey apiKey() override; + + QString title() override; + LangKey optionsTitleKey() override; + rpl::producer<QString> warning() override; + LangKey exceptionButtonTextKey(Exception exception) override; + QString exceptionBoxTitle(Exception exception) override; + rpl::producer<QString> exceptionsDescription() override; + +}; + +class LastSeenPrivacyController : public EditPrivacyController { public: using Option = EditPrivacyBox::Option; using Exception = EditPrivacyBox::Exception; @@ -56,7 +73,7 @@ public: }; -class GroupsInvitePrivacyController : public EditPrivacyBox::Controller { +class GroupsInvitePrivacyController : public EditPrivacyController { public: using Option = EditPrivacyBox::Option; using Exception = EditPrivacyBox::Exception; @@ -73,7 +90,7 @@ public: }; -class CallsPrivacyController : public EditPrivacyBox::Controller { +class CallsPrivacyController : public EditPrivacyController { public: using Option = EditPrivacyBox::Option; using Exception = EditPrivacyBox::Exception; @@ -87,9 +104,12 @@ public: QString exceptionBoxTitle(Exception exception) override; rpl::producer<QString> exceptionsDescription() override; + object_ptr<Ui::RpWidget> setupBelowWidget( + not_null<QWidget*> parent) override; + }; -class CallsPeer2PeerPrivacyController : public EditPrivacyBox::Controller { +class CallsPeer2PeerPrivacyController : public EditPrivacyController { public: using Option = EditPrivacyBox::Option; using Exception = EditPrivacyBox::Exception; @@ -108,7 +128,7 @@ public: }; class ForwardsPrivacyController - : public EditPrivacyBox::Controller + : public EditPrivacyController , private HistoryView::SimpleElementDelegate { public: using Option = EditPrivacyBox::Option; @@ -140,7 +160,7 @@ private: }; -class ProfilePhotoPrivacyController : public EditPrivacyBox::Controller { +class ProfilePhotoPrivacyController : public EditPrivacyController { public: using Option = EditPrivacyBox::Option; using Exception = EditPrivacyBox::Exception; diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index 06f2977f4..961e01ad9 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -38,6 +38,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Settings { namespace { +using Privacy = ApiWrap::Privacy; + rpl::producer<> PasscodeChanges() { return rpl::single( rpl::empty_value() @@ -46,12 +48,10 @@ rpl::producer<> PasscodeChanges() { )); } -QString PrivacyBase( - ApiWrap::Privacy::Key key, - ApiWrap::Privacy::Option option) { +QString PrivacyBase(Privacy::Key key, Privacy::Option option) { const auto phrase = [&] { - using Key = ApiWrap::Privacy::Key; - using Option = ApiWrap::Privacy::Option; + using Key = Privacy::Key; + using Option = Privacy::Option; switch (key) { case Key::CallsPeer2Peer: switch (option) { @@ -75,6 +75,27 @@ QString PrivacyBase( return lang(phrase); } +rpl::producer<QString> PrivacyString(Privacy::Key key) { + Auth().api().reloadPrivacy(key); + return Auth().api().privacyValue( + key + ) | rpl::map([=](const Privacy &value) { + auto add = QStringList(); + if (const auto never = ExceptionUsersCount(value.never)) { + add.push_back("-" + QString::number(never)); + } + if (const auto always = ExceptionUsersCount(value.always)) { + add.push_back("+" + QString::number(always)); + } + if (!add.isEmpty()) { + return PrivacyBase(key, value.option) + + " (" + add.join(", ") + ")"; + } else { + return PrivacyBase(key, value.option); + } + }); +} + void SetupPrivacy(not_null<Ui::VerticalLayout*> container) { AddSkip(container, st::settingsPrivacySkip); AddSubsectionTitle(container, lng_settings_privacy_title); @@ -97,69 +118,33 @@ void SetupPrivacy(not_null<Ui::VerticalLayout*> container) { initBox)); }); - using Privacy = ApiWrap::Privacy; - const auto PrivacyString = [](Privacy::Key key) { - Auth().api().reloadPrivacy(key); - return Auth().api().privacyValue( - key - ) | rpl::map([=](const Privacy &value) { - auto add = QStringList(); - if (const auto never = ExceptionUsersCount(value.never)) { - add.push_back("-" + QString::number(never)); - } - if (const auto always = ExceptionUsersCount(value.always)) { - add.push_back("+" + QString::number(always)); - } - if (!add.isEmpty()) { - return PrivacyBase(key, value.option) - + " (" + add.join(", ") + ")"; - } else { - return PrivacyBase(key, value.option); - } - }); - }; - const auto add = [&](LangKey label, Privacy::Key key, auto controller) { - const auto shower = Ui::CreateChild<rpl::lifetime>(container.get()); - AddButtonWithLabel( - container, - label, - PrivacyString(key), - st::settingsButton - )->addClickHandler([=] { - *shower = Auth().api().privacyValue( - key - ) | rpl::take( - 1 - ) | rpl::start_with_next([=](const Privacy &value) { - Ui::show(Box<EditPrivacyBox>( - controller(), - value)); - }); - }); + using Key = Privacy::Key; + const auto add = [&](LangKey label, Key key, auto controller) { + AddPrivacyButton(container, label, key, controller); }; + add( + lng_settings_phone_number_privacy, + Key::PhoneNumber, + [] { return std::make_unique<PhoneNumberPrivacyController>(); }); add( lng_settings_last_seen, - Privacy::Key::LastSeen, + Key::LastSeen, [] { return std::make_unique<LastSeenPrivacyController>(); }); add( lng_settings_forwards_privacy, - Privacy::Key::Forwards, + Key::Forwards, [] { return std::make_unique<ForwardsPrivacyController>(); }); add( lng_settings_profile_photo_privacy, - Privacy::Key::ProfilePhoto, + Key::ProfilePhoto, [] { return std::make_unique<ProfilePhotoPrivacyController>(); }); add( lng_settings_calls, - Privacy::Key::Calls, + Key::Calls, [] { return std::make_unique<CallsPrivacyController>(); }); - add( - lng_settings_calls_peer_to_peer, - Privacy::Key::CallsPeer2Peer, - [] { return std::make_unique<CallsPeer2PeerPrivacyController>(); }); add( lng_settings_groups_invite, - Privacy::Key::Invites, + Key::Invites, [] { return std::make_unique<GroupsInvitePrivacyController>(); }); AddSkip(container, st::settingsPrivacySecurityPadding); @@ -539,6 +524,30 @@ int ExceptionUsersCount(const std::vector<not_null<PeerData*>> &exceptions) { return ranges::accumulate(exceptions, 0, add); } +void AddPrivacyButton( + not_null<Ui::VerticalLayout*> container, + LangKey label, + Privacy::Key key, + Fn<std::unique_ptr<EditPrivacyController>()> controller) { + const auto shower = Ui::CreateChild<rpl::lifetime>(container.get()); + AddButtonWithLabel( + container, + label, + PrivacyString(key), + st::settingsButton + )->addClickHandler([=] { + *shower = Auth().api().privacyValue( + key + ) | rpl::take( + 1 + ) | rpl::start_with_next([=](const Privacy &value) { + Ui::show( + Box<EditPrivacyBox>(controller(), value), + LayerOption::KeepOther); + }); + }); +} + PrivacySecurity::PrivacySecurity(QWidget *parent, not_null<UserData*> self) : Section(parent) , _self(self) { diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.h b/Telegram/SourceFiles/settings/settings_privacy_security.h index 592e46a23..c6a7c8578 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.h +++ b/Telegram/SourceFiles/settings/settings_privacy_security.h @@ -8,11 +8,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "settings/settings_common.h" +#include "apiwrap.h" + +class EditPrivacyController; namespace Settings { int ExceptionUsersCount(const std::vector<not_null<PeerData*>> &exceptions); +void AddPrivacyButton( + not_null<Ui::VerticalLayout*> container, + LangKey label, + ApiWrap::Privacy::Key key, + Fn<std::unique_ptr<EditPrivacyController>()> controller); + class PrivacySecurity : public Section { public: PrivacySecurity(QWidget *parent, not_null<UserData*> self);