From 67195f3825874d35bce1c3a1417c1d7051297fa0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Jul 2022 22:25:04 +0300 Subject: [PATCH] Added initial support of privacy setting for voice messages. --- Telegram/Resources/langs/lang.strings | 11 ++++ Telegram/SourceFiles/api/api_user_privacy.cpp | 11 +++- Telegram/SourceFiles/api/api_user_privacy.h | 1 + Telegram/SourceFiles/data/data_peer.cpp | 8 +++ Telegram/SourceFiles/data/data_peer.h | 2 + Telegram/SourceFiles/data/data_user.cpp | 9 +++- Telegram/SourceFiles/data/data_user.h | 2 + .../SourceFiles/history/history_widget.cpp | 17 ++++-- .../history_view_compose_controls.cpp | 18 +++++-- .../settings/settings_privacy_controllers.cpp | 53 +++++++++++++++++++ .../settings/settings_privacy_controllers.h | 22 ++++++++ .../settings/settings_privacy_security.cpp | 5 ++ 12 files changed, 148 insertions(+), 11 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 34b6a7967..1ce06b762 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -557,6 +557,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_phone_number_privacy" = "Phone number"; "lng_settings_forwards_privacy" = "Forwarded messages"; "lng_settings_profile_photo_privacy" = "Profile photo"; +"lng_settings_voices_privacy" = "Voice messages"; "lng_settings_sessions_about" = "Control your sessions on other devices."; "lng_settings_passcode_disable" = "Disable Passcode"; "lng_settings_passcode_disable_sure" = "Are you sure you want to disable passcode?"; @@ -987,6 +988,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_profile_photo_always_title" = "Always allow"; "lng_edit_privacy_profile_photo_never_title" = "Never allow"; +"lng_edit_privacy_voices_title" = "Voice messages settings"; +"lng_edit_privacy_voices_header" = "Who can send me voice messages"; +"lng_edit_privacy_voices_always_empty" = "Always allow"; +"lng_edit_privacy_voices_never_empty" = "Never allow"; +"lng_edit_privacy_voices_exceptions" = "These users will or will not be able to send voice and video messages to you regardless of the settings above."; +"lng_edit_privacy_voices_always_title" = "Always allow"; +"lng_edit_privacy_voices_never_title" = "Never allow"; + "lng_self_destruct_title" = "Account self-destruction"; "lng_self_destruct_description" = "If you don't come online at least once within this period, your account will be deleted along with all groups, messages and contacts."; "lng_self_destruct_sessions_title" = "Session termination"; @@ -2837,6 +2846,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_restricted_send_public_polls" = "Sorry, public polls can't be forwarded to channels."; +"lng_restricted_send_voices" = "{user} restricted sending of voice messages to them."; + "lng_exceptions_list_title" = "Exceptions"; "lng_removed_list_title" = "Removed users"; diff --git a/Telegram/SourceFiles/api/api_user_privacy.cpp b/Telegram/SourceFiles/api/api_user_privacy.cpp index 7a5f01de7..8c97479c6 100644 --- a/Telegram/SourceFiles/api/api_user_privacy.cpp +++ b/Telegram/SourceFiles/api/api_user_privacy.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "main/main_session.h" +#include "settings/settings_premium.h" // Settings::ShowPremium. namespace Api { namespace { @@ -186,6 +187,8 @@ MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) { return MTP_inputPrivacyKeyForwards(); case Key::ProfilePhoto: return MTP_inputPrivacyKeyProfilePhoto(); + case Key::Voices: + return MTP_inputPrivacyKeyVoiceMessages(); } Unexpected("Key in Api::UserPrivacy::KetToTL."); } @@ -209,6 +212,8 @@ std::optional TLToKey(mtpTypeId type) { case mtpc_inputPrivacyKeyForwards: return Key::Forwards; case mtpc_privacyKeyProfilePhoto: case mtpc_inputPrivacyKeyProfilePhoto: return Key::ProfilePhoto; + case mtpc_privacyKeyVoiceMessages: + case mtpc_inputPrivacyKeyVoiceMessages: return Key::Voices; } return std::nullopt; } @@ -241,7 +246,11 @@ void UserPrivacy::save( _privacySaveRequests.remove(keyTypeId); apply(keyTypeId, data.vrules(), true); }); - }).fail([=] { + }).fail([=](const MTP::Error &error) { + const auto message = error.type(); + if (message == u"PREMIUM_ACCOUNT_REQUIRED"_q) { + Settings::ShowPremium(_session, QString()); + } _privacySaveRequests.remove(keyTypeId); }).send(); diff --git a/Telegram/SourceFiles/api/api_user_privacy.h b/Telegram/SourceFiles/api/api_user_privacy.h index a706cea02..39d8025b5 100644 --- a/Telegram/SourceFiles/api/api_user_privacy.h +++ b/Telegram/SourceFiles/api/api_user_privacy.h @@ -28,6 +28,7 @@ public: CallsPeer2Peer, Forwards, ProfilePhoto, + Voices, }; enum class Option { Everyone, diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 01fe66a84..bc7c82cfb 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -1125,6 +1125,14 @@ std::optional RestrictionError( return std::nullopt; } +std::optional RestrictionVoicesError(not_null peer) { + const auto user = peer->asUser(); + if (user && !user->canReceiveVoices()) { + return tr::lng_restricted_send_voices(tr::now, lt_user, user->name); + } + return std::nullopt; +} + void SetTopPinnedMessageId(not_null peer, MsgId messageId) { if (const auto channel = peer->asChannel()) { if (messageId <= channel->availableMinId()) { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 9725b8ad0..cdc8fb324 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -469,6 +469,8 @@ std::optional RestrictionError( not_null peer, ChatRestriction restriction); +std::optional RestrictionVoicesError(not_null peer); + void SetTopPinnedMessageId(not_null peer, MsgId messageId); [[nodiscard]] FullMsgId ResolveTopPinnedId( not_null peer, diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 51bce614a..db6709b12 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -257,6 +257,10 @@ bool UserData::canReceiveGifts() const { return flags() & UserDataFlag::CanReceiveGifts; } +bool UserData::canReceiveVoices() const { + return flags() & UserDataFlag::CanReceiveVoices; +} + bool UserData::canShareThisContactFast() const { return !_phone.isEmpty(); } @@ -327,7 +331,10 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { | (update.is_phone_calls_available() ? Flag::HasPhoneCalls : Flag()) | (canReceiveGifts ? Flag::CanReceiveGifts : Flag()) | (update.is_can_pin_message() ? Flag::CanPinMessages : Flag()) - | (update.is_blocked() ? Flag::Blocked : Flag())); + | (update.is_blocked() ? Flag::Blocked : Flag()) + | (!update.is_voice_messages_forbidden() + ? Flag::CanReceiveVoices + : Flag())); user->setIsBlocked(update.is_blocked()); user->setCallsStatus(update.is_phone_calls_private() ? UserData::CallsStatus::Private diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index cabd43681..945894829 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -52,6 +52,7 @@ enum class UserDataFlag { Self = (1 << 13), Premium = (1 << 14), CanReceiveGifts = (1 << 15), + CanReceiveVoices = (1 << 16), }; inline constexpr bool is_flag_type(UserDataFlag) { return true; }; using UserDataFlags = base::flags; @@ -108,6 +109,7 @@ public: [[nodiscard]] bool canAddContact() const; [[nodiscard]] bool canReceiveGifts() const; + [[nodiscard]] bool canReceiveVoices() const; // In Data::Session::processUsers() we check only that. // When actually trying to share contact we perform diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 6279a7da5..c24fb6d9f 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -929,7 +929,7 @@ void HistoryWidget::initVoiceRecordBar() { auto scrollHeight = rpl::combine( _scroll->topValue(), _scroll->heightValue() - ) | rpl::map([=](int top, int height) { + ) | rpl::map([](int top, int height) { return top + height - st::historyRecordLockPosition.y(); }); _voiceRecordBar->setLockBottom(std::move(scrollHeight)); @@ -938,9 +938,18 @@ void HistoryWidget::initVoiceRecordBar() { _voiceRecordBar->setSendButtonGeometryValue(_send->geometryValue()); _voiceRecordBar->setStartRecordingFilter([=] { - const auto error = _peer - ? Data::RestrictionError(_peer, ChatRestriction::SendMedia) - : std::nullopt; + const auto error = [&]() -> std::optional { + if (_peer) { + const auto type = ChatRestriction::SendMedia; + if (const auto error = Data::RestrictionError(_peer, type)) { + return error; + } + if (const auto error = Data::RestrictionVoicesError(_peer)) { + return error; + } + } + return std::nullopt; + }(); if (error) { controller()->show(Ui::MakeInformBox(*error)); return true; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 54b6e87a1..a9c1653a5 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1962,11 +1962,19 @@ void ComposeControls::initVoiceRecordBar() { }, _wrap->lifetime()); _voiceRecordBar->setStartRecordingFilter([=] { - const auto error = _history - ? Data::RestrictionError( - _history->peer, - ChatRestriction::SendMedia) - : std::nullopt; + const auto error = [&]() -> std::optional { + const auto peer = _history ? _history->peer : nullptr; + if (!peer) { + const auto type = ChatRestriction::SendMedia; + if (const auto error = Data::RestrictionError(peer, type)) { + return error; + } + if (const auto error = Data::RestrictionVoicesError(peer)) { + return error; + } + } + return std::nullopt; + }(); if (error) { _window->show(Ui::MakeInformBox(*error)); return true; diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index b09be4621..bc14b6561 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_session.h" #include "data/data_changes.h" +#include "data/data_peer_values.h" // Data::AmPremiumValue. #include "core/application.h" #include "core/core_settings.h" #include "history/admin_log/history_admin_log_item.h" @@ -1007,4 +1008,56 @@ auto ProfilePhotoPrivacyController::exceptionsDescription() return tr::lng_edit_privacy_profile_photo_exceptions(); } +VoicesPrivacyController::VoicesPrivacyController( + not_null<::Main::Session*> session) { + Data::AmPremiumValue( + session + ) | rpl::start_with_next([=](bool premium) { + if (!premium) { + if (const auto box = view()) { + box->closeBox(); + } + } + }, _lifetime); +} + +UserPrivacy::Key VoicesPrivacyController::key() { + return Key::Voices; +} + +rpl::producer VoicesPrivacyController::title() { + return tr::lng_edit_privacy_voices_title(); +} + +bool VoicesPrivacyController::hasOption(Option option) { + return (option != Option::Nobody); +} + +rpl::producer VoicesPrivacyController::optionsTitleKey() { + return tr::lng_edit_privacy_voices_header(); +} + +rpl::producer VoicesPrivacyController::exceptionButtonTextKey( + Exception exception) { + switch (exception) { + case Exception::Always: return tr::lng_edit_privacy_voices_always_empty(); + case Exception::Never: return tr::lng_edit_privacy_voices_never_empty(); + } + Unexpected("Invalid exception value."); +} + +rpl::producer VoicesPrivacyController::exceptionBoxTitle( + Exception exception) { + switch (exception) { + case Exception::Always: return tr::lng_edit_privacy_voices_always_title(); + case Exception::Never: return tr::lng_edit_privacy_voices_never_title(); + } + Unexpected("Invalid exception value."); +} + +auto VoicesPrivacyController::exceptionsDescription() +-> rpl::producer { + return tr::lng_edit_privacy_voices_exceptions(); +} + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.h b/Telegram/SourceFiles/settings/settings_privacy_controllers.h index 60d26e2dc..d5eeb8042 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.h +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.h @@ -223,4 +223,26 @@ public: }; +class VoicesPrivacyController : public EditPrivacyController { +public: + using Option = EditPrivacyBox::Option; + using Exception = EditPrivacyBox::Exception; + + explicit VoicesPrivacyController(not_null<::Main::Session*> session); + + Key key() override; + + rpl::producer title() override; + bool hasOption(Option option) override; + rpl::producer optionsTitleKey() override; + rpl::producer exceptionButtonTextKey( + Exception exception) override; + rpl::producer exceptionBoxTitle(Exception exception) override; + rpl::producer exceptionsDescription() override; + +private: + rpl::lifetime _lifetime; + +}; + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index db605948b..d2458479f 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -175,6 +175,11 @@ void SetupPrivacy( { &st::settingsIconGroup, kIconDarkBlue }, Key::Invites, [] { return std::make_unique(); }); + add( + tr::lng_settings_voices_privacy(), + { &st::settingsPremiumIconVoice, kIconRed }, + Key::Voices, + [=] { return std::make_unique(session); }); session->api().userPrivacy().reload(Api::UserPrivacy::Key::AddedByPhone);