Support "premiums and ..." privacy editing.

This commit is contained in:
John Preston 2024-04-01 17:03:04 +04:00
parent 1e6fb202f0
commit 5741bd9cca
14 changed files with 328 additions and 55 deletions

View File

@ -1107,9 +1107,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_nobody" = "Nobody";
"lng_edit_privacy_premium" = "Premium users";
"lng_edit_privacy_exceptions" = "Add exceptions";
"lng_edit_privacy_user_types" = "User types";
"lng_edit_privacy_users_and_groups" = "Users and groups";
"lng_edit_privacy_premium_status" = "all Telegram Premium subscribers";
"lng_edit_privacy_exceptions_count#one" = "{count} user";
"lng_edit_privacy_exceptions_count#other" = "{count} users";
"lng_edit_privacy_exceptions_premium_and" = "Premium & {users}";
"lng_edit_privacy_exceptions_add" = "Add users or groups";
"lng_edit_privacy_phone_number_title" = "Phone number privacy";

View File

@ -26,7 +26,9 @@ using TLInputRules = MTPVector<MTPInputPrivacyRule>;
using TLRules = MTPVector<MTPPrivacyRule>;
TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
const auto collectInputUsers = [](const auto &peers) {
using Exceptions = UserPrivacy::Exceptions;
const auto collectInputUsers = [](const Exceptions &exceptions) {
const auto &peers = exceptions.peers;
auto result = QVector<MTPInputUser>();
result.reserve(peers.size());
for (const auto &peer : peers) {
@ -36,7 +38,8 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
}
return result;
};
const auto collectInputChats = [](const auto &peers) {
const auto collectInputChats = [](const Exceptions &exceptions) {
const auto &peers = exceptions.peers;
auto result = QVector<MTPlong>();
result.reserve(peers.size());
for (const auto &peer : peers) {
@ -47,6 +50,7 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
return result;
};
using Option = UserPrivacy::Option;
auto result = QVector<MTPInputPrivacyRule>();
result.reserve(kMaxRules);
if (!rule.ignoreAlways) {
@ -62,6 +66,9 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
MTP_inputPrivacyValueAllowChatParticipants(
MTP_vector<MTPlong>(chats)));
}
if (rule.always.premiums && (rule.option != Option::Everyone)) {
result.push_back(MTP_inputPrivacyValueAllowPremium());
}
}
if (!rule.ignoreNever) {
const auto users = collectInputUsers(rule.never);
@ -78,14 +85,11 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
}
}
result.push_back([&] {
using Option = UserPrivacy::Option;
switch (rule.option) {
case Option::Everyone: return MTP_inputPrivacyValueAllowAll();
case Option::Contacts: return MTP_inputPrivacyValueAllowContacts();
case Option::CloseFriends:
return MTP_inputPrivacyValueAllowCloseFriends();
case Option::ContactsAndPremium:
return MTP_inputPrivacyValueAllowPremium();
case Option::Nobody: return MTP_inputPrivacyValueDisallowAll();
}
Unexpected("Option value in Api::UserPrivacy::RulesToTL.");
@ -102,6 +106,7 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) {
using Option = UserPrivacy::Option;
auto result = UserPrivacy::Rule();
auto optionSet = false;
auto allowPremium = false;
const auto setOption = [&](Option option) {
if (optionSet) {
return;
@ -109,8 +114,8 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) {
optionSet = true;
result.option = option;
};
auto &always = result.always;
auto &never = result.never;
auto &always = result.always.peers;
auto &never = result.never.peers;
const auto feed = [&](const MTPPrivacyRule &rule) {
rule.match([&](const MTPDprivacyValueAllowAll &) {
setOption(Option::Everyone);
@ -119,7 +124,7 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) {
}, [&](const MTPDprivacyValueAllowCloseFriends &) {
setOption(Option::CloseFriends);
}, [&](const MTPDprivacyValueAllowPremium &) {
setOption(Option::ContactsAndPremium);
result.always.premiums = true;
}, [&](const MTPDprivacyValueAllowUsers &data) {
const auto &users = data.vusers().v;
always.reserve(always.size() + users.size());

View File

@ -36,13 +36,16 @@ public:
Everyone,
Contacts,
CloseFriends,
ContactsAndPremium,
Nobody,
};
struct Exceptions {
std::vector<not_null<PeerData*>> peers;
bool premiums = false;
};
struct Rule {
Option option = Option::Everyone;
std::vector<not_null<PeerData*>> always;
std::vector<not_null<PeerData*>> never;
Exceptions always;
Exceptions never;
bool ignoreAlways = false;
bool ignoreNever = false;
};

View File

@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/edit_privacy_box.h"
#include "api/api_global_privacy.h"
#include "boxes/filters/edit_filter_chats_list.h"
#include "ui/effects/premium_graphics.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/shadow.h"
@ -30,12 +32,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_peer_values.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_settings.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
namespace {
namespace {
constexpr auto kPremiumsRowId = PeerId(FakeChatId(BareId(1))).value;
using Exceptions = Api::UserPrivacy::Exceptions;
void CreateRadiobuttonLock(
not_null<Ui::RpWidget*> widget,
@ -90,37 +96,167 @@ void AddPremiumRequiredRow(
}, row->lifetime());
}
} // namespace
class PrivacyExceptionsBoxController : public ChatsListBoxController {
public:
PrivacyExceptionsBoxController(
not_null<Main::Session*> session,
rpl::producer<QString> title,
const std::vector<not_null<PeerData*>> &selected);
const Exceptions &selected,
bool allowChoosePremiums);
Main::Session &session() const override;
void rowClicked(not_null<PeerListRow*> row) override;
bool isForeignRow(PeerListRowId itemId) override;
bool handleDeselectForeignRow(PeerListRowId itemId) override;
[[nodiscard]] bool premiumsSelected() const;
protected:
void prepareViewHook() override;
std::unique_ptr<Row> createRow(not_null<History*> history) override;
private:
[[nodiscard]] object_ptr<Ui::RpWidget> preparePremiumsRowList();
const not_null<Main::Session*> _session;
rpl::producer<QString> _title;
std::vector<not_null<PeerData*>> _selected;
Exceptions _selected;
bool _allowChoosePremiums = false;
PeerListContentDelegate *_typesDelegate = nullptr;
Fn<void(PeerListRowId)> _deselectOption;
};
struct RowSelectionChange {
not_null<PeerListRow*> row;
bool checked = false;
};
class PremiumsRow final : public PeerListRow {
public:
PremiumsRow();
QString generateName() override;
QString generateShortName() override;
PaintRoundImageCallback generatePaintUserpicCallback(
bool forceRound) override;
bool useForumLikeUserpic() const override;
};
class TypesController final : public PeerListController {
public:
TypesController(not_null<Main::Session*> session, bool premiums);
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
[[nodiscard]] bool premiumsSelected() const;
[[nodiscard]] rpl::producer<bool> premiumsChanges() const;
[[nodiscard]] auto rowSelectionChanges() const
-> rpl::producer<RowSelectionChange>;
private:
[[nodiscard]] std::unique_ptr<PeerListRow> createRow() const;
const not_null<Main::Session*> _session;
bool _premiums = false;
rpl::event_stream<> _selectionChanged;
rpl::event_stream<RowSelectionChange> _rowSelectionChanges;
};
PremiumsRow::PremiumsRow() : PeerListRow(kPremiumsRowId) {
setCustomStatus(tr::lng_edit_privacy_premium_status(tr::now));
}
QString PremiumsRow::generateName() {
return tr::lng_edit_privacy_premium(tr::now);
}
QString PremiumsRow::generateShortName() {
return generateName();
}
PaintRoundImageCallback PremiumsRow::generatePaintUserpicCallback(
bool forceRound) {
return [=](QPainter &p, int x, int y, int outerWidth, int size) {
auto gradient = QLinearGradient(
QPointF(x, y),
QPointF(x + size, y + size));
gradient.setStops(Ui::Premium::ButtonGradientStops());
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(gradient);
if (forceRound) {
p.drawEllipse(x, y, size, size);
} else {
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(x, y, size, size, radius, radius);
}
st::settingsPrivacyPremium.paintInCenter(p, { x, y, size, size });
};
}
bool PremiumsRow::useForumLikeUserpic() const {
return true;
}
TypesController::TypesController(
not_null<Main::Session*> session,
bool premiums)
: _session(session)
, _premiums(premiums) {
}
Main::Session &TypesController::session() const {
return *_session;
}
void TypesController::prepare() {
delegate()->peerListAppendRow(std::make_unique<PremiumsRow>());
delegate()->peerListRefreshRows();
}
bool TypesController::premiumsSelected() const {
const auto row = delegate()->peerListFindRow(kPremiumsRowId);
Assert(row != nullptr);
return row->checked();
}
void TypesController::rowClicked(not_null<PeerListRow*> row) {
const auto checked = !row->checked();
delegate()->peerListSetRowChecked(row, checked);
_rowSelectionChanges.fire({ row, checked });
}
rpl::producer<bool> TypesController::premiumsChanges() const {
return _rowSelectionChanges.events(
) | rpl::map([=] {
return premiumsSelected();
});
}
auto TypesController::rowSelectionChanges() const
-> rpl::producer<RowSelectionChange> {
return _rowSelectionChanges.events();
}
PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(
not_null<Main::Session*> session,
rpl::producer<QString> title,
const std::vector<not_null<PeerData*>> &selected)
const Exceptions &selected,
bool allowChoosePremiums)
: ChatsListBoxController(session)
, _session(session)
, _title(std::move(title))
, _selected(selected) {
, _selected(selected)
, _allowChoosePremiums(allowChoosePremiums) {
}
Main::Session &PrivacyExceptionsBoxController::session() const {
@ -129,7 +265,81 @@ Main::Session &PrivacyExceptionsBoxController::session() const {
void PrivacyExceptionsBoxController::prepareViewHook() {
delegate()->peerListSetTitle(std::move(_title));
delegate()->peerListAddSelectedPeers(_selected);
if (_allowChoosePremiums || _selected.premiums) {
delegate()->peerListSetAboveWidget(preparePremiumsRowList());
}
delegate()->peerListAddSelectedPeers(_selected.peers);
}
bool PrivacyExceptionsBoxController::isForeignRow(PeerListRowId itemId) {
return (itemId == kPremiumsRowId);
}
bool PrivacyExceptionsBoxController::handleDeselectForeignRow(
PeerListRowId itemId) {
if (isForeignRow(itemId)) {
_deselectOption(itemId);
return true;
}
return false;
}
auto PrivacyExceptionsBoxController::preparePremiumsRowList()
-> object_ptr<Ui::RpWidget> {
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
const auto container = result.data();
container->add(CreatePeerListSectionSubtitle(
container,
tr::lng_edit_privacy_user_types()));
auto &lifetime = container->lifetime();
_typesDelegate = lifetime.make_state<PeerListContentDelegateSimple>();
const auto controller = lifetime.make_state<TypesController>(
&session(),
_selected.premiums);
const auto content = result->add(object_ptr<PeerListContent>(
container,
controller));
_typesDelegate->setContent(content);
controller->setDelegate(_typesDelegate);
if (_selected.premiums) {
const auto row = _typesDelegate->peerListFindRow(kPremiumsRowId);
Assert(row != nullptr);
content->changeCheckState(row, true, anim::type::instant);
this->delegate()->peerListSetForeignRowChecked(
row,
true,
anim::type::instant);
}
container->add(CreatePeerListSectionSubtitle(
container,
tr::lng_edit_privacy_users_and_groups()));
controller->premiumsChanges(
) | rpl::start_with_next([=](bool premiums) {
_selected.premiums = premiums;
}, lifetime);
controller->rowSelectionChanges(
) | rpl::start_with_next([=](RowSelectionChange update) {
this->delegate()->peerListSetForeignRowChecked(
update.row,
update.checked,
anim::type::normal);
}, lifetime);
_deselectOption = [=](PeerListRowId itemId) {
if (const auto row = _typesDelegate->peerListFindRow(itemId)) {
_typesDelegate->peerListSetRowChecked(row, false);
}
};
return result;
}
[[nodiscard]] bool PrivacyExceptionsBoxController::premiumsSelected() const {
return _selected.premiums;
}
void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
@ -145,7 +355,8 @@ void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
}
}
std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxController::createRow(not_null<History*> history) {
auto PrivacyExceptionsBoxController::createRow(not_null<History*> history)
-> std::unique_ptr<Row> {
if (history->peer->isSelf() || history->peer->isRepliesChat()) {
return nullptr;
} else if (!history->peer->isUser()
@ -210,11 +421,13 @@ void EditPrivacyBox::editExceptions(
auto controller = std::make_unique<PrivacyExceptionsBoxController>(
&_window->session(),
_controller->exceptionBoxTitle(exception),
exceptions(exception));
exceptions(exception),
_controller->allowPremiumsToggle(exception));
auto initBox = [=, controller = controller.get()](
not_null<PeerListBox*> box) {
box->addButton(tr::lng_settings_save(), crl::guard(this, [=] {
exceptions(exception) = box->collectSelectedRows();
exceptions(exception).peers = box->collectSelectedRows();
exceptions(exception).premiums = controller->premiumsSelected();
const auto type = [&] {
switch (exception) {
case Exception::Always: return Exception::Never;
@ -222,8 +435,8 @@ void EditPrivacyBox::editExceptions(
}
Unexpected("Invalid exception value.");
}();
auto &removeFrom = exceptions(type);
for (const auto peer : exceptions(exception)) {
auto &removeFrom = exceptions(type).peers;
for (const auto peer : exceptions(exception).peers) {
removeFrom.erase(
ranges::remove(removeFrom, peer),
end(removeFrom));
@ -237,7 +450,7 @@ void EditPrivacyBox::editExceptions(
Box<PeerListBox>(std::move(controller), std::move(initBox)));
}
std::vector<not_null<PeerData*>> &EditPrivacyBox::exceptions(Exception exception) {
EditPrivacyBox::Exceptions &EditPrivacyBox::exceptions(Exception exception) {
switch (exception) {
case Exception::Always: return _value.always;
case Exception::Never: return _value.never;
@ -340,16 +553,28 @@ void EditPrivacyBox::setupContent() {
const auto addExceptionLink = [=](Exception exception) {
const auto update = Ui::CreateChild<rpl::event_stream<>>(content);
auto label = update->events_starting_with({}) | rpl::map([=] {
return Settings::ExceptionUsersCount(exceptions(exception));
}) | rpl::map([](int count) {
return count
? tr::lng_edit_privacy_exceptions_count(tr::now, lt_count, count)
const auto &value = exceptions(exception);
const auto count = Settings::ExceptionUsersCount(value.peers);
const auto users = count
? tr::lng_edit_privacy_exceptions_count(
tr::now,
lt_count,
count)
: tr::lng_edit_privacy_exceptions_add(tr::now);
return !value.premiums
? users
: !count
? tr::lng_edit_privacy_premium(tr::now)
: tr::lng_edit_privacy_exceptions_premium_and(
tr::now,
lt_users,
users);
});
_controller->handleExceptionsChange(
exception,
update->events_starting_with({}) | rpl::map([=] {
return Settings::ExceptionUsersCount(exceptions(exception));
return Settings::ExceptionUsersCount(
exceptions(exception).peers);
}));
auto text = _controller->exceptionButtonTextKey(exception);
const auto button = content->add(
@ -448,7 +673,7 @@ void EditPrivacyBox::setupContent() {
addButton(tr::lng_settings_save(), [=] {
const auto someAreDisallowed = (_value.option != Option::Everyone)
|| !_value.never.empty();
|| !_value.never.peers.empty();
_controller->confirmSave(someAreDisallowed, crl::guard(this, [=] {
_value.ignoreAlways = !showExceptionLink(Exception::Always);
_value.ignoreNever = !showExceptionLink(Exception::Never);

View File

@ -48,7 +48,8 @@ public:
[[nodiscard]] virtual rpl::producer<TextWithEntities> warning() const {
return nullptr;
}
virtual void prepareWarningLabel(not_null<Ui::FlatLabel*> warning) const {
virtual void prepareWarningLabel(
not_null<Ui::FlatLabel*> warning) const {
}
[[nodiscard]] virtual rpl::producer<QString> exceptionButtonTextKey(
Exception exception) const = 0;
@ -56,6 +57,10 @@ public:
Exception exception) const = 0;
[[nodiscard]] virtual auto exceptionsDescription()
const -> rpl::producer<QString> = 0;
[[nodiscard]] virtual bool allowPremiumsToggle(
Exception exception) const {
return false;
}
virtual void handleExceptionsChange(
Exception exception,
rpl::producer<int> value) {
@ -117,6 +122,7 @@ class EditPrivacyBox final : public Ui::BoxContent {
public:
using Value = Api::UserPrivacy::Rule;
using Option = Api::UserPrivacy::Option;
using Exceptions = Api::UserPrivacy::Exceptions;
using Exception = EditPrivacyController::Exception;
EditPrivacyBox(
@ -148,7 +154,7 @@ private:
int topSkip);
void editExceptions(Exception exception, Fn<void()> done);
std::vector<not_null<PeerData*>> &exceptions(Exception exception);
Exceptions &exceptions(Exception exception);
const not_null<Window::SessionController*> _window;
std::unique_ptr<EditPrivacyController> _controller;

View File

@ -506,6 +506,22 @@ int PeerListBox::peerListSelectedRowsCount() {
return _select ? _select->entity()->getItemsCount() : 0;
}
std::vector<PeerListRowId> PeerListBox::collectSelectedIds() {
auto result = std::vector<PeerListRowId>();
auto items = _select
? _select->entity()->getItems()
: QVector<uint64>();
if (!items.empty()) {
result.reserve(items.size());
for (const auto itemId : items) {
if (!_controller->isForeignRow(itemId)) {
result.push_back(itemId);
}
}
}
return result;
}
auto PeerListBox::collectSelectedRows()
-> std::vector<not_null<PeerData*>> {
auto result = std::vector<not_null<PeerData*>>();
@ -887,11 +903,15 @@ void PeerListRow::lazyInitialize(const style::PeerListItem &st) {
refreshStatus();
}
bool PeerListRow::useForumLikeUserpic() const {
return !special() && peer()->isForum();
}
void PeerListRow::createCheckbox(
const style::RoundImageCheckbox &st,
Fn<void()> updateCallback) {
const auto generateRadius = [=](int size) {
return (!special() && peer()->isForum())
return useForumLikeUserpic()
? int(size * Ui::ForumUserpicRadiusMultiplier())
: std::optional<int>();
};

View File

@ -166,6 +166,8 @@ public:
return _name;
}
virtual bool useForumLikeUserpic() const;
enum class StatusType {
Online,
LastSeen,
@ -1042,6 +1044,7 @@ public:
std::unique_ptr<PeerListController> controller,
Fn<void(not_null<PeerListBox*>)> init);
[[nodiscard]] std::vector<PeerListRowId> collectSelectedIds();
[[nodiscard]] std::vector<not_null<PeerData*>> collectSelectedRows();
void peerListSetTitle(rpl::producer<QString> title) override {

View File

@ -30,7 +30,7 @@ public:
friend inline constexpr auto operator<=>(Birthday, Birthday) = default;
friend inline constexpr bool operator==(Birthday, Birthday) = default;
static constexpr auto kYearMin = 1900;
static constexpr auto kYearMin = 1875;
static constexpr auto kYearMax = 2100;
private:

View File

@ -161,6 +161,7 @@ settingsPrivacyOption: Checkbox(settingsCheckbox) {
settingsPrivacySecurityPadding: 12px;
settingsPrivacySkip: 14px;
settingsPrivacySkipTop: 4px;
settingsPrivacyPremium: icon{{ "profile_premium", premiumButtonFg }};
settingsPrivacyAddBirthday: FlatLabel(defaultFlatLabel) {
minWidth: 256px;

View File

@ -392,8 +392,9 @@ void SetupBirthday(
key
) | rpl::map([=](const Api::UserPrivacy::Rule &value) {
return (value.option == Api::UserPrivacy::Option::Contacts)
&& value.always.empty()
&& value.never.empty();
&& value.always.peers.empty()
&& !value.always.premiums
&& value.never.peers.empty();
}) | rpl::distinct_until_changed();
Ui::AddSkip(container);

View File

@ -440,10 +440,7 @@ void SetupPremium(
button->addClickHandler([=] {
showOther(BusinessId());
});
constexpr auto kNewExpiresAt = int(1711958400);
if (base::unixtime::now() < kNewExpiresAt) {
Ui::NewBadge::AddToRight(button);
}
Ui::NewBadge::AddToRight(button);
if (controller->session().premiumCanBuy()) {
const auto button = AddButtonWithIcon(

View File

@ -795,6 +795,11 @@ auto GroupsInvitePrivacyController::exceptionsDescription() const
return tr::lng_edit_privacy_groups_exceptions();
}
bool GroupsInvitePrivacyController::allowPremiumsToggle(
Exception exception) const {
return (exception == Exception::Always);
}
UserPrivacy::Key CallsPrivacyController::key() const {
return Key::Calls;
}

View File

@ -149,6 +149,7 @@ public:
rpl::producer<QString> exceptionBoxTitle(
Exception exception) const override;
rpl::producer<QString> exceptionsDescription() const override;
bool allowPremiumsToggle(Exception exception) const override;
};

View File

@ -114,31 +114,33 @@ void AddPremiumStar(
}, badge->lifetime());
}
QString PrivacyBase(Privacy::Key key, Privacy::Option option) {
QString PrivacyBase(Privacy::Key key, const Privacy::Rule &rule) {
using Key = Privacy::Key;
using Option = Privacy::Option;
switch (key) {
case Key::CallsPeer2Peer:
switch (option) {
switch (rule.option) {
case Option::Everyone:
return tr::lng_edit_privacy_calls_p2p_everyone(tr::now);
case Option::Contacts:
return tr::lng_edit_privacy_calls_p2p_contacts(tr::now);
case Option::CloseFriends:
return tr::lng_edit_privacy_close_friends(tr::now); // unused
case Option::Nobody:
return tr::lng_edit_privacy_calls_p2p_nobody(tr::now);
}
Unexpected("Value in Privacy::Option.");
[[fallthrough]];
default:
switch (option) {
switch (rule.option) {
case Option::Everyone: return tr::lng_edit_privacy_everyone(tr::now);
case Option::Contacts: return tr::lng_edit_privacy_contacts(tr::now);
case Option::Contacts:
return rule.always.premiums
? tr::lng_edit_privacy_contacts_and_premium(tr::now)
: tr::lng_edit_privacy_contacts(tr::now);
case Option::CloseFriends:
return tr::lng_edit_privacy_close_friends(tr::now);
case Option::ContactsAndPremium:
return tr::lng_edit_privacy_contacts_and_premium(tr::now);
case Option::Nobody: return tr::lng_edit_privacy_nobody(tr::now);
case Option::Nobody:
return rule.always.premiums
? tr::lng_edit_privacy_premium(tr::now)
: tr::lng_edit_privacy_nobody(tr::now);
}
Unexpected("Value in Privacy::Option.");
}
@ -152,17 +154,17 @@ rpl::producer<QString> PrivacyString(
key
) | rpl::map([=](const Privacy::Rule &value) {
auto add = QStringList();
if (const auto never = ExceptionUsersCount(value.never)) {
if (const auto never = ExceptionUsersCount(value.never.peers)) {
add.push_back("-" + QString::number(never));
}
if (const auto always = ExceptionUsersCount(value.always)) {
if (const auto always = ExceptionUsersCount(value.always.peers)) {
add.push_back("+" + QString::number(always));
}
if (!add.isEmpty()) {
return PrivacyBase(key, value.option)
return PrivacyBase(key, value)
+ " (" + add.join(", ") + ")";
} else {
return PrivacyBase(key, value.option);
return PrivacyBase(key, value);
}
});
}