Add phone number privacy.

Also move calls peer-to-peer privacy inside calls privacy.
This commit is contained in:
John Preston 2019-05-21 14:51:24 +02:00
parent f5c79cb1b6
commit 8660f976a9
9 changed files with 236 additions and 109 deletions

View file

@ -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).";

View file

@ -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:

View file

@ -62,6 +62,7 @@ class ApiWrap : public MTP::Sender, private base::Subscriber {
public:
struct Privacy {
enum class Key {
PhoneNumber,
LastSeen,
Calls,
Invites,

View file

@ -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();

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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;

View file

@ -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) {

View file

@ -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);