Support "premiums and ..." privacy editing.
This commit is contained in:
		
							parent
							
								
									1e6fb202f0
								
							
						
					
					
						commit
						5741bd9cca
					
				
					 14 changed files with 328 additions and 55 deletions
				
			
		| 
						 | 
				
			
			@ -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";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>();
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -161,6 +161,7 @@ settingsPrivacyOption: Checkbox(settingsCheckbox) {
 | 
			
		|||
settingsPrivacySecurityPadding: 12px;
 | 
			
		||||
settingsPrivacySkip: 14px;
 | 
			
		||||
settingsPrivacySkipTop: 4px;
 | 
			
		||||
settingsPrivacyPremium: icon{{ "profile_premium", premiumButtonFg }};
 | 
			
		||||
 | 
			
		||||
settingsPrivacyAddBirthday: FlatLabel(defaultFlatLabel) {
 | 
			
		||||
	minWidth: 256px;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -440,10 +440,7 @@ void SetupPremium(
 | 
			
		|||
	button->addClickHandler([=] {
 | 
			
		||||
		showOther(BusinessId());
 | 
			
		||||
	});
 | 
			
		||||
	constexpr auto kNewExpiresAt = int(1711958400);
 | 
			
		||||
	if (base::unixtime::now() < kNewExpiresAt) {
 | 
			
		||||
	Ui::NewBadge::AddToRight(button);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (controller->session().premiumCanBuy()) {
 | 
			
		||||
		const auto button = AddButtonWithIcon(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue