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:
@@ -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);
|
||||
|
Reference in New Issue
Block a user