/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peers/edit_peer_permissions_box.h" #include "lang/lang_keys.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "ui/effects/toggle_arrow.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/layers/generic_box.h" #include "ui/painter.h" #include "ui/widgets/labels.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" #include "ui/widgets/continuous_sliders.h" #include "ui/widgets/box_content_divider.h" #include "ui/text/text_utilities.h" #include "ui/toasts/common_toasts.h" #include "info/profile/info_profile_icon.h" #include "info/profile/info_profile_values.h" #include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_peer_info_box.h" #include "window/window_session_controller.h" #include "window/window_controller.h" #include "main/main_session.h" #include "apiwrap.h" #include "settings/settings_common.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_info.h" #include "styles/style_window.h" #include "styles/style_settings.h" namespace { constexpr auto kSlowmodeValues = 7; constexpr auto kSuggestGigagroupThreshold = 199000; int SlowmodeDelayByIndex(int index) { Expects(index >= 0 && index < kSlowmodeValues); switch (index) { case 0: return 0; case 1: return 10; case 2: return 30; case 3: return 60; case 4: return 5 * 60; case 5: return 15 * 60; case 6: return 60 * 60; } Unexpected("Index in SlowmodeDelayByIndex."); } auto Dependencies(ChatRestrictions) -> std::vector> { using Flag = ChatRestriction; return { // stickers <-> gifs { Flag::SendGifs, Flag::SendStickers }, { Flag::SendStickers, Flag::SendGifs }, // stickers <-> games { Flag::SendGames, Flag::SendStickers }, { Flag::SendStickers, Flag::SendGames }, // stickers <-> inline { Flag::SendInline, Flag::SendStickers }, { Flag::SendStickers, Flag::SendInline }, // embed_links -> send_plain { Flag::EmbedLinks, Flag::SendOther }, // send_* -> view_messages { Flag::SendStickers, Flag::ViewMessages }, { Flag::SendGifs, Flag::ViewMessages }, { Flag::SendGames, Flag::ViewMessages }, { Flag::SendInline, Flag::ViewMessages }, { Flag::SendPolls, Flag::ViewMessages }, { Flag::SendPhotos, Flag::ViewMessages }, { Flag::SendVideos, Flag::ViewMessages }, { Flag::SendVideoMessages, Flag::ViewMessages }, { Flag::SendMusic, Flag::ViewMessages }, { Flag::SendVoiceMessages, Flag::ViewMessages }, { Flag::SendFiles, Flag::ViewMessages }, { Flag::SendOther, Flag::ViewMessages }, }; } ChatRestrictions NegateRestrictions(ChatRestrictions value) { using Flag = ChatRestriction; return (~value) & (Flag(0) // view_messages is always allowed, so it is never in restrictions. //| Flag::ViewMessages | Flag::ChangeInfo | Flag::EmbedLinks | Flag::AddParticipants | Flag::CreateTopics | Flag::PinMessages | Flag::SendGames | Flag::SendGifs | Flag::SendInline | Flag::SendPolls | Flag::SendStickers | Flag::SendPhotos | Flag::SendVideos | Flag::SendVideoMessages | Flag::SendMusic | Flag::SendVoiceMessages | Flag::SendFiles | Flag::SendOther); } [[nodiscard]] std::vector MediaRestrictions() { return std::vector{ ChatRestriction::SendPhotos, ChatRestriction::SendVideos, ChatRestriction::SendVideoMessages, ChatRestriction::SendMusic, ChatRestriction::SendVoiceMessages, ChatRestriction::SendFiles, ChatRestriction::SendStickers | ChatRestriction::SendGifs | ChatRestriction::SendGames | ChatRestriction::SendInline, ChatRestriction::EmbedLinks, ChatRestriction::SendPolls, }; } auto Dependencies(ChatAdminRights) -> std::vector> { return {}; } auto ToPositiveNumberString() { return rpl::map([](int count) { return count ? QString::number(count) : QString(); }); } ChatRestrictions DisabledByAdminRights(not_null peer) { using Flag = ChatRestriction; using Admin = ChatAdminRight; using Admins = ChatAdminRights; const auto adminRights = [&] { const auto full = ~Admins(0); if (const auto chat = peer->asChat()) { return chat->amCreator() ? full : chat->adminRights(); } else if (const auto channel = peer->asChannel()) { return channel->amCreator() ? full : channel->adminRights(); } Unexpected("User in DisabledByAdminRights."); }(); return Flag(0) | ((adminRights & Admin::ManageTopics) ? Flag(0) : Flag::CreateTopics) | ((adminRights & Admin::PinMessages) ? Flag(0) : Flag::PinMessages) | ((adminRights & Admin::InviteByLinkOrAdd) ? Flag(0) : Flag::AddParticipants) | ((adminRights & Admin::ChangeInfo) ? Flag(0) : Flag::ChangeInfo); } not_null SendMediaToggle( not_null container, rpl::producer checkedValue, int total, not_null*> wrap, Fn toggleMedia, std::optional locked) { const auto &stButton = st::rightsButton; const auto button = container->add(object_ptr( container, rpl::single(QString()), stButton)); const auto toggleButton = Ui::CreateChild( container.get(), rpl::single(QString()), stButton); { const auto separator = Ui::CreateChild(container.get()); separator->paintRequest( ) | rpl::start_with_next([=, bg = stButton.textBgOver] { auto p = QPainter(separator); p.fillRect(separator->rect(), bg); }, separator->lifetime()); const auto separatorHeight = 2 * stButton.toggle.border + stButton.toggle.diameter; button->geometryValue( ) | rpl::start_with_next([=](const QRect &r) { const auto w = st::rightsButtonToggleWidth; constexpr auto kLineWidth = int(1); toggleButton->setGeometry( r.x() + r.width() - w, r.y(), w, r.height()); separator->setGeometry( toggleButton->x() - kLineWidth, r.y() + (r.height() - separatorHeight) / 2, kLineWidth, separatorHeight); }, toggleButton->lifetime()); } using namespace rpl::mappers; button->toggleOn(rpl::duplicate(checkedValue) | rpl::map(_1 > 0), true); toggleButton->toggleOn(button->toggledValue(), true); button->setToggleLocked(locked.has_value()); toggleButton->setToggleLocked(locked.has_value()); struct State final { Ui::Animations::Simple animation; }; const auto state = button->lifetime().make_state(); const auto label = Ui::CreateChild( button, rpl::combine( tr::lng_rights_chat_send_media(), rpl::duplicate(checkedValue) ) | rpl::map([total](const QString &t, int checked) { auto count = Ui::Text::Bold(" " + QString::number(checked) + '/' + QString::number(total)); return TextWithEntities::Simple(t).append(std::move(count)); })); label->setAttribute(Qt::WA_TransparentForMouseEvents); const auto arrow = Ui::CreateChild(button); { const auto size = st::mainMenuToggleSize * 4; arrow->resize(size, size); arrow->paintRequest( ) | rpl::start_with_next([=] { auto p = QPainter(arrow); const auto path = Ui::ToggleUpDownArrowPath( size / 2., size / 2., size / 4., st::mainMenuToggleFourStrokes, state->animation.value(wrap->toggled() ? 1. : 0.)); auto hq = PainterHighQualityEnabler(p); p.fillPath(path, st::rightsButton.textFg); }, arrow->lifetime()); } button->sizeValue( ) | rpl::start_with_next([=](const QSize &s) { const auto labelLeft = st::rightsButton.padding.left(); const auto labelRight = s.width() - toggleButton->width(); label->resizeToWidth(labelRight - labelLeft - arrow->width()); label->moveToLeft( labelLeft, (s.height() - label->height()) / 2); arrow->moveToLeft( std::min( labelLeft + label->naturalWidth(), labelRight - arrow->width()), (s.height() - arrow->height()) / 2); }, button->lifetime()); wrap->toggledValue( ) | rpl::skip(1) | rpl::start_with_next([=](bool toggled) { state->animation.start( [=] { arrow->update(); }, toggled ? 0. : 1., toggled ? 1. : 0., st::slideWrapDuration); }, button->lifetime()); const auto handleLocked = [=] { if (locked.has_value()) { Ui::ShowMultilineToast({ .parentOverride = container, .text = { *locked }, }); return true; } return false; }; button->clicks( ) | rpl::start_with_next([=] { if (!handleLocked()) { wrap->toggle(!wrap->toggled(), anim::type::normal); } }, button->lifetime()); toggleButton->clicks( ) | rpl::start_with_next([=] { if (!handleLocked()) { toggleMedia(!button->toggled()); } }, toggleButton->lifetime()); return button; } not_null AddInnerCheckbox( not_null container, const QString &text, bool toggled, rpl::producer<> toggledChanges) { class Button final : public Ui::SettingsButton { public: using Ui::SettingsButton::SettingsButton; protected: void paintEvent(QPaintEvent *e) override { Painter p(this); const auto paintOver = (isOver() || isDown()) && !isDisabled(); Ui::SettingsButton::paintBg(p, e->rect(), paintOver); Ui::SettingsButton::paintRipple(p, 0, 0); } }; const auto checkbox = container->add( object_ptr( container, text, toggled, st::settingsCheckbox), st::rightsButton.padding); const auto button = Ui::CreateChild