/* 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 "settings/settings_privacy_security.h" #include "api/api_authorizations.h" #include "api/api_blocked_peers.h" #include "api/api_cloud_password.h" #include "api/api_self_destruct.h" #include "api/api_sensitive_content.h" #include "api/api_global_privacy.h" #include "settings/settings_common.h" #include "settings/settings_privacy_controllers.h" #include "base/timer_rpl.h" #include "base/unixtime.h" #include "boxes/peer_list_box.h" #include "boxes/edit_privacy_box.h" #include "boxes/passcode_box.h" #include "boxes/auto_lock_box.h" #include "boxes/sessions_box.h" #include "ui/boxes/confirm_box.h" #include "boxes/self_destruction_box.h" #include "core/application.h" #include "core/core_settings.h" #include "ui/chat/chat_style.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/fade_wrap.h" #include "ui/widgets/shadow.h" #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/widgets/checkbox.h" #include "ui/layers/generic_box.h" #include "calls/calls_instance.h" #include "core/core_cloud_password.h" #include "core/update_checker.h" #include "base/platform/base_platform_last_input.h" #include "lang/lang_keys.h" #include "data/data_session.h" #include "data/data_chat.h" #include "data/data_channel.h" #include "main/main_domain.h" #include "main/main_session.h" #include "storage/storage_domain.h" #include "window/window_session_controller.h" #include "apiwrap.h" #include "facades.h" #include "styles/style_settings.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" #include namespace Settings { namespace { constexpr auto kUpdateTimeout = 60 * crl::time(1000); using Privacy = Api::UserPrivacy; QString PrivacyBase(Privacy::Key key, Privacy::Option option) { using Key = Privacy::Key; using Option = Privacy::Option; switch (key) { case Key::CallsPeer2Peer: switch (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::Nobody: return tr::lng_edit_privacy_calls_p2p_nobody(tr::now); } Unexpected("Value in Privacy::Option."); default: switch (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::Nobody: return tr::lng_edit_privacy_nobody(tr::now); } Unexpected("Value in Privacy::Option."); } } rpl::producer PrivacyString( not_null<::Main::Session*> session, Privacy::Key key) { session->api().userPrivacy().reload(key); return session->api().userPrivacy().value( key ) | rpl::map([=](const Privacy::Rule &value) { auto add = QStringList(); if (const auto never = ExceptionUsersCount(value.never)) { add.push_back("-" + QString::number(never)); } if (const auto always = ExceptionUsersCount(value.always)) { add.push_back("+" + QString::number(always)); } if (!add.isEmpty()) { return PrivacyBase(key, value.option) + " (" + add.join(", ") + ")"; } else { return PrivacyBase(key, value.option); } }); } rpl::producer BlockedPeersCount(not_null<::Main::Session*> session) { return session->api().blockedPeers().slice( ) | rpl::map([](const Api::BlockedPeers::Slice &data) { return data.total; }); } void SetupPrivacy( not_null controller, not_null container, rpl::producer<> updateTrigger) { AddSkip(container, st::settingsPrivacySkip); AddSubsectionTitle(container, tr::lng_settings_privacy_title()); const auto session = &controller->session(); auto count = rpl::combine( BlockedPeersCount(session), tr::lng_settings_no_blocked_users() ) | rpl::map([](int count, const QString &none) { return count ? QString::number(count) : none; }); const auto blockedPeers = AddButtonWithLabel( container, tr::lng_settings_blocked_users(), std::move(count), st::settingsButton); blockedPeers->addClickHandler([=] { const auto initBox = [=](not_null box) { box->addButton(tr::lng_close(), [=] { box->closeBox(); }); box->addLeftButton(tr::lng_blocked_list_add(), [=] { BlockedBoxController::BlockNewPeer(controller); }); }; controller->show(Box( std::make_unique(controller), initBox)); }); std::move( updateTrigger ) | rpl::start_with_next([=] { session->api().blockedPeers().reload(); }, blockedPeers->lifetime()); using Key = Privacy::Key; const auto add = [&]( rpl::producer label, Key key, auto controllerFactory) { AddPrivacyButton( controller, container, std::move(label), key, controllerFactory); }; add( tr::lng_settings_phone_number_privacy(), Key::PhoneNumber, [] { return std::make_unique(); }); add( tr::lng_settings_last_seen(), Key::LastSeen, [=] { return std::make_unique(session); }); add( tr::lng_settings_forwards_privacy(), Key::Forwards, [=] { return std::make_unique( controller); }); add( tr::lng_settings_profile_photo_privacy(), Key::ProfilePhoto, [] { return std::make_unique(); }); add( tr::lng_settings_calls(), Key::Calls, [] { return std::make_unique(); }); add( tr::lng_settings_groups_invite(), Key::Invites, [] { return std::make_unique(); }); session->api().userPrivacy().reload(Api::UserPrivacy::Key::AddedByPhone); AddSkip(container, st::settingsPrivacySecurityPadding); AddDividerText(container, tr::lng_settings_group_privacy_about()); } void SetupArchiveAndMute( not_null controller, not_null container) { using namespace rpl::mappers; const auto wrap = container->add( object_ptr>( container, object_ptr(container))); const auto inner = wrap->entity(); AddSkip(inner); AddSubsectionTitle(inner, tr::lng_settings_new_unknown()); const auto session = &controller->session(); const auto privacy = &session->api().globalPrivacy(); privacy->reload(); AddButton( inner, tr::lng_settings_auto_archive(), st::settingsButton )->toggleOn( privacy->archiveAndMute() )->toggledChanges( ) | rpl::filter([=](bool toggled) { return toggled != privacy->archiveAndMuteCurrent(); }) | rpl::start_with_next([=](bool toggled) { privacy->update(toggled); }, container->lifetime()); AddSkip(inner); AddDividerText(inner, tr::lng_settings_auto_archive_about()); using namespace rpl::mappers; wrap->toggleOn(rpl::single( false ) | rpl::then( session->api().globalPrivacy().showArchiveAndMute( ) | rpl::filter(_1) | rpl::take(1) )); } void SetupLocalPasscode( not_null controller, not_null container) { AddSkip(container); AddSubsectionTitle(container, tr::lng_settings_passcode_title()); auto has = rpl::single( rpl::empty_value() ) | rpl::then( controller->session().domain().local().localPasscodeChanged() ) | rpl::map([=] { return controller->session().domain().local().hasLocalPasscode(); }); auto text = rpl::combine( tr::lng_passcode_change(), tr::lng_passcode_turn_on(), base::duplicate(has), [](const QString &change, const QString &create, bool has) { return has ? change : create; }); container->add( object_ptr