Support groups in privacy settings.

This commit is contained in:
John Preston 2019-05-20 20:40:53 +02:00
parent 3556fbf702
commit 6b80ff4bcc
8 changed files with 125 additions and 85 deletions

View File

@ -602,7 +602,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_exceptions_count#one" = "{count} user"; "lng_edit_privacy_exceptions_count#one" = "{count} user";
"lng_edit_privacy_exceptions_count#other" = "{count} users"; "lng_edit_privacy_exceptions_count#other" = "{count} users";
"lng_edit_privacy_exceptions_add" = "Add users"; "lng_edit_privacy_exceptions_add" = "Add users or groups";
"lng_edit_privacy_lastseen_title" = "Last seen privacy"; "lng_edit_privacy_lastseen_title" = "Last seen privacy";
"lng_edit_privacy_lastseen_header" = "Who can see your last seen time"; "lng_edit_privacy_lastseen_header" = "Who can see your last seen time";

View File

@ -5625,10 +5625,8 @@ auto ApiWrap::parsePrivacy(const QVector<MTPPrivacyRule> &rules)
optionSet = true; optionSet = true;
result.option = option; result.option = option;
}; };
auto &alwaysUsers = result.alwaysUsers; auto &always = result.always;
auto &neverUsers = result.neverUsers; auto &never = result.never;
auto &alwaysChats = result.alwaysChats;
auto &neverChats = result.neverChats;
const auto Feed = [&](const MTPPrivacyRule &rule) { const auto Feed = [&](const MTPPrivacyRule &rule) {
rule.match([&](const MTPDprivacyValueAllowAll &) { rule.match([&](const MTPDprivacyValueAllowAll &) {
SetOption(Option::Everyone); SetOption(Option::Everyone);
@ -5636,25 +5634,26 @@ auto ApiWrap::parsePrivacy(const QVector<MTPPrivacyRule> &rules)
SetOption(Option::Contacts); SetOption(Option::Contacts);
}, [&](const MTPDprivacyValueAllowUsers &data) { }, [&](const MTPDprivacyValueAllowUsers &data) {
const auto &users = data.vusers.v; const auto &users = data.vusers.v;
alwaysUsers.reserve(alwaysUsers.size() + users.size()); always.reserve(always.size() + users.size());
for (const auto userId : users) { for (const auto userId : users) {
const auto user = _session->data().user(UserId(userId.v)); const auto user = _session->data().user(UserId(userId.v));
if (!base::contains(neverUsers, user) if (!base::contains(never, user)
&& !base::contains(alwaysUsers, user)) { && !base::contains(always, user)) {
alwaysUsers.push_back(user); always.emplace_back(user);
} }
} }
}, [&](const MTPDprivacyValueAllowChatParticipants &data) { }, [&](const MTPDprivacyValueAllowChatParticipants &data) {
const auto &chats = data.vchats.v; const auto &chats = data.vchats.v;
alwaysChats.reserve(alwaysChats.size() + chats.size()); always.reserve(always.size() + chats.size());
for (const auto chatId : chats) { for (const auto chatId : chats) {
const auto chat = _session->data().chatLoaded(chatId.v); const auto chat = _session->data().chatLoaded(chatId.v);
const auto peer = chat const auto peer = chat
? static_cast<PeerData*>(chat) ? static_cast<PeerData*>(chat)
: _session->data().channel(chatId.v); : _session->data().channelLoaded(chatId.v);
if (!base::contains(neverChats, peer) if (peer
&& !base::contains(alwaysChats, peer)) { && !base::contains(never, peer)
alwaysChats.emplace_back(peer); && !base::contains(always, peer)) {
always.emplace_back(peer);
} }
} }
}, [&](const MTPDprivacyValueDisallowContacts &) { }, [&](const MTPDprivacyValueDisallowContacts &) {
@ -5663,25 +5662,26 @@ auto ApiWrap::parsePrivacy(const QVector<MTPPrivacyRule> &rules)
SetOption(Option::Nobody); SetOption(Option::Nobody);
}, [&](const MTPDprivacyValueDisallowUsers &data) { }, [&](const MTPDprivacyValueDisallowUsers &data) {
const auto &users = data.vusers.v; const auto &users = data.vusers.v;
neverUsers.reserve(neverUsers.size() + users.size()); never.reserve(never.size() + users.size());
for (const auto userId : users) { for (const auto userId : users) {
const auto user = _session->data().user(UserId(userId.v)); const auto user = _session->data().user(UserId(userId.v));
if (!base::contains(alwaysUsers, user) if (!base::contains(always, user)
&& !base::contains(neverUsers, user)) { && !base::contains(never, user)) {
neverUsers.push_back(user); never.emplace_back(user);
} }
} }
}, [&](const MTPDprivacyValueDisallowChatParticipants &data) { }, [&](const MTPDprivacyValueDisallowChatParticipants &data) {
const auto &chats = data.vchats.v; const auto &chats = data.vchats.v;
neverChats.reserve(neverChats.size() + chats.size()); never.reserve(never.size() + chats.size());
for (const auto chatId : chats) { for (const auto chatId : chats) {
const auto chat = _session->data().chatLoaded(chatId.v); const auto chat = _session->data().chatLoaded(chatId.v);
const auto peer = chat const auto peer = chat
? static_cast<PeerData*>(chat) ? static_cast<PeerData*>(chat)
: _session->data().channel(chatId.v); : _session->data().channelLoaded(chatId.v);
if (!base::contains(alwaysChats, peer) if (peer
&& !base::contains(neverChats, peer)) { && !base::contains(always, peer)
neverChats.emplace_back(peer); && !base::contains(never, peer)) {
never.emplace_back(peer);
} }
} }
}); });

View File

@ -75,10 +75,8 @@ public:
Nobody, Nobody,
}; };
Option option = Option::Everyone; Option option = Option::Everyone;
std::vector<not_null<UserData*>> alwaysUsers; std::vector<not_null<PeerData*>> always;
std::vector<not_null<UserData*>> neverUsers; std::vector<not_null<PeerData*>> never;
std::vector<not_null<PeerData*>> alwaysChats;
std::vector<not_null<PeerData*>> neverChats;
static MTPInputPrivacyKey Input(Key key); static MTPInputPrivacyKey Input(Key key);
static std::optional<Key> KeyFromMTP(mtpTypeId type); static std::optional<Key> KeyFromMTP(mtpTypeId type);

View File

@ -16,12 +16,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "info/profile/info_profile_button.h" #include "info/profile/info_profile_button.h"
#include "settings/settings_common.h" #include "settings/settings_common.h"
#include "settings/settings_privacy_security.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 "data/data_user.h" #include "data/data_user.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "styles/style_settings.h" #include "styles/style_settings.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
@ -29,10 +32,10 @@ namespace {
class PrivacyExceptionsBoxController : public ChatsListBoxController { class PrivacyExceptionsBoxController : public ChatsListBoxController {
public: public:
PrivacyExceptionsBoxController(Fn<QString()> titleFactory, const std::vector<not_null<UserData*>> &selected); PrivacyExceptionsBoxController(Fn<QString()> titleFactory, const std::vector<not_null<PeerData*>> &selected);
void rowClicked(not_null<PeerListRow*> row) override; void rowClicked(not_null<PeerListRow*> row) override;
std::vector<not_null<UserData*>> getResult() const; std::vector<not_null<PeerData*>> getResult() const;
protected: protected:
void prepareViewHook() override; void prepareViewHook() override;
@ -40,11 +43,13 @@ protected:
private: private:
Fn<QString()> _titleFactory; Fn<QString()> _titleFactory;
std::vector<not_null<UserData*>> _selected; std::vector<not_null<PeerData*>> _selected;
}; };
PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(Fn<QString()> titleFactory, const std::vector<not_null<UserData*>> &selected) PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(
Fn<QString()> titleFactory,
const std::vector<not_null<PeerData*>> &selected)
: _titleFactory(std::move(titleFactory)) : _titleFactory(std::move(titleFactory))
, _selected(selected) { , _selected(selected) {
} }
@ -54,32 +59,43 @@ void PrivacyExceptionsBoxController::prepareViewHook() {
delegate()->peerListAddSelectedRows(_selected); delegate()->peerListAddSelectedRows(_selected);
} }
std::vector<not_null<UserData*>> PrivacyExceptionsBoxController::getResult() const { std::vector<not_null<PeerData*>> PrivacyExceptionsBoxController::getResult() const {
auto peers = delegate()->peerListCollectSelectedRows(); return delegate()->peerListCollectSelectedRows();
auto users = std::vector<not_null<UserData*>>();
if (!peers.empty()) {
users.reserve(peers.size());
for_const (auto peer, peers) {
auto user = peer->asUser();
Assert(user != nullptr);
users.push_back(user);
}
}
return users;
} }
void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) { void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
delegate()->peerListSetRowChecked(row, !row->checked()); delegate()->peerListSetRowChecked(row, !row->checked());
if (const auto channel = row->peer()->asChannel()) {
if (!channel->membersCountKnown()) {
channel->updateFull();
}
}
} }
std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxController::createRow(not_null<History*> history) { std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxController::createRow(not_null<History*> history) {
if (history->peer->isSelf()) { if (history->peer->isSelf()) {
return nullptr; return nullptr;
} } else if (!history->peer->isUser()
if (auto user = history->peer->asUser()) { && !history->peer->isChat()
return std::make_unique<Row>(history); && !history->peer->isMegagroup()) {
}
return nullptr; return nullptr;
}
auto result = std::make_unique<Row>(history);
const auto count = [&] {
if (const auto chat = history->peer->asChat()) {
return chat->count;
} else if (const auto channel = history->peer->asChannel()) {
return channel->membersCountKnown()
? channel->membersCount()
: 0;
}
return 0;
}();
if (count > 0) {
result->setCustomStatus(
lng_chat_status_members(lt_count_decimal, count));
}
return result;
} }
} // namespace } // namespace
@ -107,29 +123,30 @@ void EditPrivacyBox::prepare() {
setupContent(); setupContent();
} }
void EditPrivacyBox::editExceptionUsers( void EditPrivacyBox::editExceptions(
Exception exception, Exception exception,
Fn<void()> done) { Fn<void()> done) {
auto controller = std::make_unique<PrivacyExceptionsBoxController>( auto controller = std::make_unique<PrivacyExceptionsBoxController>(
crl::guard(this, [=] { crl::guard(this, [=] {
return _controller->exceptionBoxTitle(exception); return _controller->exceptionBoxTitle(exception);
}), }),
exceptionUsers(exception)); exceptions(exception));
auto initBox = [=, controller = controller.get()]( auto initBox = [=, controller = controller.get()](
not_null<PeerListBox*> box) { not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_settings_save), crl::guard(this, [=] { box->addButton(langFactory(lng_settings_save), crl::guard(this, [=] {
exceptionUsers(exception) = controller->getResult(); exceptions(exception) = controller->getResult();
const auto removeFrom = ([=] { const auto type = [&] {
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;
} }
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
})(); }();
auto &removeFromUsers = exceptionUsers(removeFrom); auto &removeFrom = exceptions(type);
for (const auto user : exceptionUsers(exception)) { for (const auto peer : exceptions(exception)) {
const auto from = ranges::remove(removeFromUsers, user); removeFrom.erase(
removeFromUsers.erase(from, end(removeFromUsers)); ranges::remove(removeFrom, peer),
end(removeFrom));
} }
done(); done();
box->closeBox(); box->closeBox();
@ -142,20 +159,24 @@ void EditPrivacyBox::editExceptionUsers(
} }
QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() { QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
const auto collectInputUsers = [](const auto &users) { const auto collectInputUsers = [](const auto &peers) {
auto result = QVector<MTPInputUser>(); auto result = QVector<MTPInputUser>();
result.reserve(users.size()); result.reserve(peers.size());
for (const auto user : users) { for (const auto peer : peers) {
if (const auto user = peer->asUser()) {
result.push_back(user->inputUser); result.push_back(user->inputUser);
} }
}
return result; return result;
}; };
const auto collectInputChats = [](const auto & chats) { const auto collectInputChats = [](const auto &peers) {
auto result = QVector<MTPint>(); auto result = QVector<MTPint>();
result.reserve(chats.size()); result.reserve(peers.size());
for (const auto peer : chats) { for (const auto peer : peers) {
if (!peer->isUser()) {
result.push_back(MTP_int(peer->bareId())); result.push_back(MTP_int(peer->bareId()));
} }
}
return result; return result;
}; };
@ -163,19 +184,23 @@ QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
auto result = QVector<MTPInputPrivacyRule>(); auto result = QVector<MTPInputPrivacyRule>();
result.reserve(kMaxRules); result.reserve(kMaxRules);
if (showExceptionLink(Exception::Always)) { if (showExceptionLink(Exception::Always)) {
if (!_value.alwaysUsers.empty()) { const auto users = collectInputUsers(_value.always);
result.push_back(MTP_inputPrivacyValueAllowUsers(MTP_vector<MTPInputUser>(collectInputUsers(_value.alwaysUsers)))); const auto chats = collectInputChats(_value.always);
if (!users.empty()) {
result.push_back(MTP_inputPrivacyValueAllowUsers(MTP_vector<MTPInputUser>(users)));
} }
if (!_value.alwaysChats.empty()) { if (!chats.empty()) {
result.push_back(MTP_inputPrivacyValueAllowChatParticipants(MTP_vector<MTPint>(collectInputChats(_value.alwaysChats)))); result.push_back(MTP_inputPrivacyValueAllowChatParticipants(MTP_vector<MTPint>(chats)));
} }
} }
if (showExceptionLink(Exception::Never)) { if (showExceptionLink(Exception::Never)) {
if (!_value.neverUsers.empty()) { const auto users = collectInputUsers(_value.never);
result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector<MTPInputUser>(collectInputUsers(_value.neverUsers)))); const auto chats = collectInputChats(_value.never);
if (!users.empty()) {
result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector<MTPInputUser>(users)));
} }
if (!_value.neverChats.empty()) { if (!chats.empty()) {
result.push_back(MTP_inputPrivacyValueDisallowChatParticipants(MTP_vector<MTPint>(collectInputChats(_value.neverChats)))); result.push_back(MTP_inputPrivacyValueDisallowChatParticipants(MTP_vector<MTPint>(chats)));
} }
} }
result.push_back([&] { result.push_back([&] {
@ -190,11 +215,10 @@ QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
return result; return result;
} }
// #TODO privacy std::vector<not_null<PeerData*>> &EditPrivacyBox::exceptions(Exception exception) {
std::vector<not_null<UserData*>> &EditPrivacyBox::exceptionUsers(Exception exception) {
switch (exception) { switch (exception) {
case Exception::Always: return _value.alwaysUsers; case Exception::Always: return _value.always;
case Exception::Never: return _value.neverUsers; case Exception::Never: return _value.never;
} }
Unexpected("Invalid exception value."); Unexpected("Invalid exception value.");
} }
@ -275,7 +299,7 @@ void EditPrivacyBox::setupContent() {
auto label = update->events_starting_with( auto label = update->events_starting_with(
rpl::empty_value() rpl::empty_value()
) | rpl::map([=] { ) | rpl::map([=] {
return exceptionUsers(exception).size(); return Settings::ExceptionUsersCount(exceptions(exception));
}) | rpl::map([](int count) { }) | rpl::map([](int count) {
return count return count
? lng_edit_privacy_exceptions_count(lt_count, count) ? lng_edit_privacy_exceptions_count(lt_count, count)
@ -299,7 +323,7 @@ void EditPrivacyBox::setupContent() {
) | rpl::map([=] { ) | rpl::map([=] {
return showExceptionLink(exception); return showExceptionLink(exception);
}))->entity()->addClickHandler([=] { }))->entity()->addClickHandler([=] {
editExceptionUsers(exception, [=] { update->fire({}); }); editExceptions(exception, [=] { update->fire({}); });
}); });
return button; return button;
}; };
@ -328,8 +352,7 @@ void EditPrivacyBox::setupContent() {
addButton(langFactory(lng_settings_save), [=] { addButton(langFactory(lng_settings_save), [=] {
const auto someAreDisallowed = (_value.option != Option::Everyone) const auto someAreDisallowed = (_value.option != Option::Everyone)
|| !_value.neverUsers.empty() || !_value.never.empty();
|| !_value.neverChats.empty();
_controller->confirmSave(someAreDisallowed, crl::guard(this, [=] { _controller->confirmSave(someAreDisallowed, crl::guard(this, [=] {
Auth().api().savePrivacy( Auth().api().savePrivacy(
_controller->apiKey(), _controller->apiKey(),

View File

@ -108,8 +108,8 @@ private:
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
rpl::producer<QString> text); rpl::producer<QString> text);
void editExceptionUsers(Exception exception, Fn<void()> done); void editExceptions(Exception exception, Fn<void()> done);
std::vector<not_null<UserData*>> &exceptionUsers(Exception exception); std::vector<not_null<PeerData*>> &exceptions(Exception exception);
std::unique_ptr<Controller> _controller; std::unique_ptr<Controller> _controller;
Value _value; Value _value;

View File

@ -131,9 +131,12 @@ public:
} }
int membersCount() const { int membersCount() const {
return _membersCount; return std::max(_membersCount, 1);
} }
void setMembersCount(int newMembersCount); void setMembersCount(int newMembersCount);
bool membersCountKnown() const {
return (_membersCount >= 0);
}
int adminsCount() const { int adminsCount() const {
return _adminsCount; return _adminsCount;
@ -364,7 +367,7 @@ private:
PtsWaiter _ptsWaiter; PtsWaiter _ptsWaiter;
int _membersCount = 1; int _membersCount = -1;
int _adminsCount = 1; int _adminsCount = 1;
int _restrictedCount = 0; int _restrictedCount = 0;
int _kickedCount = 0; int _kickedCount = 0;

View File

@ -28,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_specific.h" #include "platform/platform_specific.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "auth_session.h" #include "auth_session.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "styles/style_settings.h" #include "styles/style_settings.h"
@ -102,10 +104,10 @@ void SetupPrivacy(not_null<Ui::VerticalLayout*> container) {
key key
) | rpl::map([=](const Privacy &value) { ) | rpl::map([=](const Privacy &value) {
auto add = QStringList(); auto add = QStringList();
if (const auto never = value.neverUsers.size()) { // #TODO privacy if (const auto never = ExceptionUsersCount(value.never)) {
add.push_back("-" + QString::number(never)); add.push_back("-" + QString::number(never));
} }
if (const auto always = value.alwaysUsers.size()) { if (const auto always = ExceptionUsersCount(value.always)) {
add.push_back("+" + QString::number(always)); add.push_back("+" + QString::number(always));
} }
if (!add.isEmpty()) { if (!add.isEmpty()) {
@ -525,6 +527,18 @@ void SetupSessionsList(not_null<Ui::VerticalLayout*> container) {
} // namespace } // namespace
int ExceptionUsersCount(const std::vector<not_null<PeerData*>> &exceptions) {
const auto add = [](int already, not_null<PeerData*> peer) {
if (const auto chat = peer->asChat()) {
return already + chat->count;
} else if (const auto channel = peer->asChannel()) {
return already + channel->membersCount();
}
return already + 1;
};
return ranges::accumulate(exceptions, 0, add);
}
PrivacySecurity::PrivacySecurity(QWidget *parent, not_null<UserData*> self) PrivacySecurity::PrivacySecurity(QWidget *parent, not_null<UserData*> self)
: Section(parent) : Section(parent)
, _self(self) { , _self(self) {

View File

@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Settings { namespace Settings {
int ExceptionUsersCount(const std::vector<not_null<PeerData*>> &exceptions);
class PrivacySecurity : public Section { class PrivacySecurity : public Section {
public: public:
PrivacySecurity(QWidget *parent, not_null<UserData*> self); PrivacySecurity(QWidget *parent, not_null<UserData*> self);