2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-09-05 00:46:08 +00:00

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

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