diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a38defcf0..96ee55649 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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#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_header" = "Who can see your last seen time"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index d824a289c..ccda0bb4f 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -5625,10 +5625,8 @@ auto ApiWrap::parsePrivacy(const QVector &rules) optionSet = true; result.option = option; }; - auto &alwaysUsers = result.alwaysUsers; - auto &neverUsers = result.neverUsers; - auto &alwaysChats = result.alwaysChats; - auto &neverChats = result.neverChats; + auto &always = result.always; + auto &never = result.never; const auto Feed = [&](const MTPPrivacyRule &rule) { rule.match([&](const MTPDprivacyValueAllowAll &) { SetOption(Option::Everyone); @@ -5636,25 +5634,26 @@ auto ApiWrap::parsePrivacy(const QVector &rules) SetOption(Option::Contacts); }, [&](const MTPDprivacyValueAllowUsers &data) { const auto &users = data.vusers.v; - alwaysUsers.reserve(alwaysUsers.size() + users.size()); + always.reserve(always.size() + users.size()); for (const auto userId : users) { const auto user = _session->data().user(UserId(userId.v)); - if (!base::contains(neverUsers, user) - && !base::contains(alwaysUsers, user)) { - alwaysUsers.push_back(user); + if (!base::contains(never, user) + && !base::contains(always, user)) { + always.emplace_back(user); } } }, [&](const MTPDprivacyValueAllowChatParticipants &data) { const auto &chats = data.vchats.v; - alwaysChats.reserve(alwaysChats.size() + chats.size()); + always.reserve(always.size() + chats.size()); for (const auto chatId : chats) { const auto chat = _session->data().chatLoaded(chatId.v); const auto peer = chat ? static_cast(chat) - : _session->data().channel(chatId.v); - if (!base::contains(neverChats, peer) - && !base::contains(alwaysChats, peer)) { - alwaysChats.emplace_back(peer); + : _session->data().channelLoaded(chatId.v); + if (peer + && !base::contains(never, peer) + && !base::contains(always, peer)) { + always.emplace_back(peer); } } }, [&](const MTPDprivacyValueDisallowContacts &) { @@ -5663,25 +5662,26 @@ auto ApiWrap::parsePrivacy(const QVector &rules) SetOption(Option::Nobody); }, [&](const MTPDprivacyValueDisallowUsers &data) { const auto &users = data.vusers.v; - neverUsers.reserve(neverUsers.size() + users.size()); + never.reserve(never.size() + users.size()); for (const auto userId : users) { const auto user = _session->data().user(UserId(userId.v)); - if (!base::contains(alwaysUsers, user) - && !base::contains(neverUsers, user)) { - neverUsers.push_back(user); + if (!base::contains(always, user) + && !base::contains(never, user)) { + never.emplace_back(user); } } }, [&](const MTPDprivacyValueDisallowChatParticipants &data) { const auto &chats = data.vchats.v; - neverChats.reserve(neverChats.size() + chats.size()); + never.reserve(never.size() + chats.size()); for (const auto chatId : chats) { const auto chat = _session->data().chatLoaded(chatId.v); const auto peer = chat ? static_cast(chat) - : _session->data().channel(chatId.v); - if (!base::contains(alwaysChats, peer) - && !base::contains(neverChats, peer)) { - neverChats.emplace_back(peer); + : _session->data().channelLoaded(chatId.v); + if (peer + && !base::contains(always, peer) + && !base::contains(never, peer)) { + never.emplace_back(peer); } } }); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index da9044913..32a6564d7 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -75,10 +75,8 @@ public: Nobody, }; Option option = Option::Everyone; - std::vector> alwaysUsers; - std::vector> neverUsers; - std::vector> alwaysChats; - std::vector> neverChats; + std::vector> always; + std::vector> never; static MTPInputPrivacyKey Input(Key key); static std::optional KeyFromMTP(mtpTypeId type); diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index 10d6603cf..983a1c21c 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -16,12 +16,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_controllers.h" #include "info/profile/info_profile_button.h" #include "settings/settings_common.h" +#include "settings/settings_privacy_security.h" #include "calls/calls_instance.h" #include "base/binary_guard.h" #include "lang/lang_keys.h" #include "apiwrap.h" #include "auth_session.h" #include "data/data_user.h" +#include "data/data_chat.h" +#include "data/data_channel.h" #include "styles/style_settings.h" #include "styles/style_boxes.h" @@ -29,10 +32,10 @@ namespace { class PrivacyExceptionsBoxController : public ChatsListBoxController { public: - PrivacyExceptionsBoxController(Fn titleFactory, const std::vector> &selected); + PrivacyExceptionsBoxController(Fn titleFactory, const std::vector> &selected); void rowClicked(not_null row) override; - std::vector> getResult() const; + std::vector> getResult() const; protected: void prepareViewHook() override; @@ -40,11 +43,13 @@ protected: private: Fn _titleFactory; - std::vector> _selected; + std::vector> _selected; }; -PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(Fn titleFactory, const std::vector> &selected) +PrivacyExceptionsBoxController::PrivacyExceptionsBoxController( + Fn titleFactory, + const std::vector> &selected) : _titleFactory(std::move(titleFactory)) , _selected(selected) { } @@ -54,32 +59,43 @@ void PrivacyExceptionsBoxController::prepareViewHook() { delegate()->peerListAddSelectedRows(_selected); } -std::vector> PrivacyExceptionsBoxController::getResult() const { - auto peers = delegate()->peerListCollectSelectedRows(); - auto users = std::vector>(); - 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; +std::vector> PrivacyExceptionsBoxController::getResult() const { + return delegate()->peerListCollectSelectedRows(); } void PrivacyExceptionsBoxController::rowClicked(not_null row) { delegate()->peerListSetRowChecked(row, !row->checked()); + if (const auto channel = row->peer()->asChannel()) { + if (!channel->membersCountKnown()) { + channel->updateFull(); + } + } } std::unique_ptr PrivacyExceptionsBoxController::createRow(not_null history) { if (history->peer->isSelf()) { return nullptr; + } else if (!history->peer->isUser() + && !history->peer->isChat() + && !history->peer->isMegagroup()) { + return nullptr; } - if (auto user = history->peer->asUser()) { - return std::make_unique(history); + auto result = std::make_unique(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 nullptr; + return result; } } // namespace @@ -107,29 +123,30 @@ void EditPrivacyBox::prepare() { setupContent(); } -void EditPrivacyBox::editExceptionUsers( +void EditPrivacyBox::editExceptions( Exception exception, Fn done) { auto controller = std::make_unique( crl::guard(this, [=] { return _controller->exceptionBoxTitle(exception); }), - exceptionUsers(exception)); + exceptions(exception)); auto initBox = [=, controller = controller.get()]( not_null box) { box->addButton(langFactory(lng_settings_save), crl::guard(this, [=] { - exceptionUsers(exception) = controller->getResult(); - const auto removeFrom = ([=] { + exceptions(exception) = controller->getResult(); + const auto type = [&] { switch (exception) { case Exception::Always: return Exception::Never; case Exception::Never: return Exception::Always; } Unexpected("Invalid exception value."); - })(); - auto &removeFromUsers = exceptionUsers(removeFrom); - for (const auto user : exceptionUsers(exception)) { - const auto from = ranges::remove(removeFromUsers, user); - removeFromUsers.erase(from, end(removeFromUsers)); + }(); + auto &removeFrom = exceptions(type); + for (const auto peer : exceptions(exception)) { + removeFrom.erase( + ranges::remove(removeFrom, peer), + end(removeFrom)); } done(); box->closeBox(); @@ -142,19 +159,23 @@ void EditPrivacyBox::editExceptionUsers( } QVector EditPrivacyBox::collectResult() { - const auto collectInputUsers = [](const auto &users) { + const auto collectInputUsers = [](const auto &peers) { auto result = QVector(); - result.reserve(users.size()); - for (const auto user : users) { - result.push_back(user->inputUser); + result.reserve(peers.size()); + for (const auto peer : peers) { + if (const auto user = peer->asUser()) { + result.push_back(user->inputUser); + } } return result; }; - const auto collectInputChats = [](const auto & chats) { + const auto collectInputChats = [](const auto &peers) { auto result = QVector(); - result.reserve(chats.size()); - for (const auto peer : chats) { - result.push_back(MTP_int(peer->bareId())); + result.reserve(peers.size()); + for (const auto peer : peers) { + if (!peer->isUser()) { + result.push_back(MTP_int(peer->bareId())); + } } return result; }; @@ -163,19 +184,23 @@ QVector EditPrivacyBox::collectResult() { auto result = QVector(); result.reserve(kMaxRules); if (showExceptionLink(Exception::Always)) { - if (!_value.alwaysUsers.empty()) { - result.push_back(MTP_inputPrivacyValueAllowUsers(MTP_vector(collectInputUsers(_value.alwaysUsers)))); + const auto users = collectInputUsers(_value.always); + const auto chats = collectInputChats(_value.always); + if (!users.empty()) { + result.push_back(MTP_inputPrivacyValueAllowUsers(MTP_vector(users))); } - if (!_value.alwaysChats.empty()) { - result.push_back(MTP_inputPrivacyValueAllowChatParticipants(MTP_vector(collectInputChats(_value.alwaysChats)))); + if (!chats.empty()) { + result.push_back(MTP_inputPrivacyValueAllowChatParticipants(MTP_vector(chats))); } } if (showExceptionLink(Exception::Never)) { - if (!_value.neverUsers.empty()) { - result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector(collectInputUsers(_value.neverUsers)))); + const auto users = collectInputUsers(_value.never); + const auto chats = collectInputChats(_value.never); + if (!users.empty()) { + result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector(users))); } - if (!_value.neverChats.empty()) { - result.push_back(MTP_inputPrivacyValueDisallowChatParticipants(MTP_vector(collectInputChats(_value.neverChats)))); + if (!chats.empty()) { + result.push_back(MTP_inputPrivacyValueDisallowChatParticipants(MTP_vector(chats))); } } result.push_back([&] { @@ -190,11 +215,10 @@ QVector EditPrivacyBox::collectResult() { return result; } -// #TODO privacy -std::vector> &EditPrivacyBox::exceptionUsers(Exception exception) { +std::vector> &EditPrivacyBox::exceptions(Exception exception) { switch (exception) { - case Exception::Always: return _value.alwaysUsers; - case Exception::Never: return _value.neverUsers; + case Exception::Always: return _value.always; + case Exception::Never: return _value.never; } Unexpected("Invalid exception value."); } @@ -275,7 +299,7 @@ void EditPrivacyBox::setupContent() { auto label = update->events_starting_with( rpl::empty_value() ) | rpl::map([=] { - return exceptionUsers(exception).size(); + return Settings::ExceptionUsersCount(exceptions(exception)); }) | rpl::map([](int count) { return count ? lng_edit_privacy_exceptions_count(lt_count, count) @@ -299,7 +323,7 @@ void EditPrivacyBox::setupContent() { ) | rpl::map([=] { return showExceptionLink(exception); }))->entity()->addClickHandler([=] { - editExceptionUsers(exception, [=] { update->fire({}); }); + editExceptions(exception, [=] { update->fire({}); }); }); return button; }; @@ -328,8 +352,7 @@ void EditPrivacyBox::setupContent() { addButton(langFactory(lng_settings_save), [=] { const auto someAreDisallowed = (_value.option != Option::Everyone) - || !_value.neverUsers.empty() - || !_value.neverChats.empty(); + || !_value.never.empty(); _controller->confirmSave(someAreDisallowed, crl::guard(this, [=] { Auth().api().savePrivacy( _controller->apiKey(), diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h index f0377299f..cd5636829 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.h +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h @@ -108,8 +108,8 @@ private: not_null container, rpl::producer text); - void editExceptionUsers(Exception exception, Fn done); - std::vector> &exceptionUsers(Exception exception); + void editExceptions(Exception exception, Fn done); + std::vector> &exceptions(Exception exception); std::unique_ptr _controller; Value _value; diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 972069dcf..5ba5dcf7c 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -131,9 +131,12 @@ public: } int membersCount() const { - return _membersCount; + return std::max(_membersCount, 1); } void setMembersCount(int newMembersCount); + bool membersCountKnown() const { + return (_membersCount >= 0); + } int adminsCount() const { return _adminsCount; @@ -364,7 +367,7 @@ private: PtsWaiter _ptsWaiter; - int _membersCount = 1; + int _membersCount = -1; int _adminsCount = 1; int _restrictedCount = 0; int _kickedCount = 0; diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index 8bd0ec76e..06f2977f4 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -28,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_specific.h" #include "lang/lang_keys.h" #include "data/data_session.h" +#include "data/data_chat.h" +#include "data/data_channel.h" #include "auth_session.h" #include "apiwrap.h" #include "styles/style_settings.h" @@ -102,10 +104,10 @@ void SetupPrivacy(not_null container) { key ) | rpl::map([=](const Privacy &value) { 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)); } - if (const auto always = value.alwaysUsers.size()) { + if (const auto always = ExceptionUsersCount(value.always)) { add.push_back("+" + QString::number(always)); } if (!add.isEmpty()) { @@ -525,6 +527,18 @@ void SetupSessionsList(not_null container) { } // namespace +int ExceptionUsersCount(const std::vector> &exceptions) { + const auto add = [](int already, not_null 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 self) : Section(parent) , _self(self) { diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.h b/Telegram/SourceFiles/settings/settings_privacy_security.h index ea0a03036..592e46a23 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.h +++ b/Telegram/SourceFiles/settings/settings_privacy_security.h @@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Settings { +int ExceptionUsersCount(const std::vector> &exceptions); + class PrivacySecurity : public Section { public: PrivacySecurity(QWidget *parent, not_null self);