From 59663d66615c4b0b049216a7d435bccfa3975d16 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 22 Mar 2024 12:41:28 +0400 Subject: [PATCH] Track birthdays, edit birthday privacy. --- Telegram/Resources/langs/lang.strings | 18 +++++ Telegram/SourceFiles/api/api_user_privacy.cpp | 3 + Telegram/SourceFiles/api/api_user_privacy.h | 1 + .../SourceFiles/boxes/edit_privacy_box.cpp | 1 + Telegram/SourceFiles/boxes/edit_privacy_box.h | 1 + Telegram/SourceFiles/data/data_birthday.cpp | 69 ++++++++++++++++ Telegram/SourceFiles/data/data_birthday.h | 38 +++++++++ Telegram/SourceFiles/data/data_changes.h | 31 +++---- Telegram/SourceFiles/data/data_user.cpp | 25 ++++++ Telegram/SourceFiles/data/data_user.h | 12 ++- Telegram/SourceFiles/settings/settings.style | 4 + .../settings/settings_privacy_controllers.cpp | 81 +++++++++++++++++-- .../settings/settings_privacy_controllers.h | 25 ++++++ .../settings/settings_privacy_security.cpp | 6 ++ Telegram/cmake/td_ui.cmake | 2 + 15 files changed, 293 insertions(+), 24 deletions(-) create mode 100644 Telegram/SourceFiles/data/data_birthday.cpp create mode 100644 Telegram/SourceFiles/data/data_birthday.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 02ba53c30..ca78a2d9d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -652,6 +652,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_messages_privacy" = "Messages"; "lng_settings_voices_privacy" = "Voice messages"; "lng_settings_bio_privacy" = "Bio"; +"lng_settings_birthday_privacy" = "Date of Birth"; "lng_settings_privacy_premium" = "Only subscribers of {link} can restrict receiving voice messages."; "lng_settings_privacy_premium_link" = "Telegram Premium"; "lng_settings_passcode_disable" = "Disable Passcode"; @@ -664,6 +665,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_phone_label" = "Phone number"; "lng_settings_username_add" = "Add username"; "lng_settings_username_about" = "Username lets people contact you on Telegram without needing your phone number."; +"lng_settings_birthday_label" = "Date of Birth"; +"lng_settings_birthday_add" = "Add"; +"lng_settings_birthday_about" = "Choose who can see your birthday in {link}."; +"lng_settings_birthday_about_link" = "Settings"; +"lng_settings_birthday_contacts" = "Only your contacts can see your birthday. {link}"; +"lng_settings_birthday_contacts_link" = "Change >"; "lng_settings_add_account_about" = "You can add up to four accounts with different phone numbers."; "lng_settings_peer_to_peer_about" = "Disabling peer-to-peer will relay all calls through Telegram servers to avoid revealing your IP address, but may slightly decrease audio quality."; "lng_settings_advanced" = "Advanced"; @@ -1087,6 +1094,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_everyone" = "Everybody"; "lng_edit_privacy_contacts" = "My contacts"; "lng_edit_privacy_close_friends" = "Close friends"; +"lng_edit_privacy_contacts_and_premium" = "Contacts and Premium"; "lng_edit_privacy_nobody" = "Nobody"; "lng_edit_privacy_premium" = "Premium users"; "lng_edit_privacy_exceptions" = "Add exceptions"; @@ -1136,6 +1144,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_about_always_title" = "Always allow"; "lng_edit_privacy_about_never_title" = "Never allow"; +"lng_edit_privacy_birthday_title" = "Date of birth privacy"; +"lng_edit_privacy_birthday_header" = "Who can see my date of birth"; +"lng_edit_privacy_birthday_always_empty" = "Always allow"; +"lng_edit_privacy_birthday_never_empty" = "Never allow"; +"lng_edit_privacy_birthday_exceptions" = "These users will or will not be able to see your date of birth regardless of the settings above."; +"lng_edit_privacy_birthday_always_title" = "Always allow"; +"lng_edit_privacy_birthday_never_title" = "Never allow"; +"lng_edit_privacy_birthday_yet" = "You haven't entered your date of birth yet.\n{link}"; +"lng_edit_privacy_birthday_yet_link" = "Add my birthday >"; + "lng_edit_privacy_calls_title" = "Voice calls privacy"; "lng_edit_privacy_calls_header" = "Who can call you"; "lng_edit_privacy_calls_always_empty" = "Always allow"; diff --git a/Telegram/SourceFiles/api/api_user_privacy.cpp b/Telegram/SourceFiles/api/api_user_privacy.cpp index eaf6b8a08..ce9dc91f9 100644 --- a/Telegram/SourceFiles/api/api_user_privacy.cpp +++ b/Telegram/SourceFiles/api/api_user_privacy.cpp @@ -194,6 +194,7 @@ MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) { case Key::ProfilePhoto: return MTP_inputPrivacyKeyProfilePhoto(); case Key::Voices: return MTP_inputPrivacyKeyVoiceMessages(); case Key::About: return MTP_inputPrivacyKeyAbout(); + case Key::Birthday: return MTP_inputPrivacyKeyBirthday(); } Unexpected("Key in Api::UserPrivacy::KetToTL."); } @@ -221,6 +222,8 @@ std::optional<UserPrivacy::Key> TLToKey(mtpTypeId type) { case mtpc_inputPrivacyKeyVoiceMessages: return Key::Voices; case mtpc_privacyKeyAbout: case mtpc_inputPrivacyKeyAbout: return Key::About; + case mtpc_privacyKeyBirthday: + case mtpc_inputPrivacyKeyBirthday: return Key::Birthday; } return std::nullopt; } diff --git a/Telegram/SourceFiles/api/api_user_privacy.h b/Telegram/SourceFiles/api/api_user_privacy.h index 4dc951f6a..884000fb7 100644 --- a/Telegram/SourceFiles/api/api_user_privacy.h +++ b/Telegram/SourceFiles/api/api_user_privacy.h @@ -30,6 +30,7 @@ public: ProfilePhoto, Voices, About, + Birthday, }; enum class Option { Everyone, diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index 367742971..cd472746e 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -374,6 +374,7 @@ void EditPrivacyBox::setupContent() { }; auto above = _controller->setupAboveWidget( + _window, content, rpl::duplicate(optionValue), getDelegate()->outerContainer()); diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h index cfdc14ad7..da8bc98cf 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.h +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h @@ -62,6 +62,7 @@ public: } [[nodiscard]] virtual object_ptr<Ui::RpWidget> setupAboveWidget( + not_null<Window::SessionController*> controller, not_null<QWidget*> parent, rpl::producer<Option> option, not_null<QWidget*> outerContainer) { diff --git a/Telegram/SourceFiles/data/data_birthday.cpp b/Telegram/SourceFiles/data/data_birthday.cpp new file mode 100644 index 000000000..4b9df693f --- /dev/null +++ b/Telegram/SourceFiles/data/data_birthday.cpp @@ -0,0 +1,69 @@ +/* +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 "data/data_birthday.h" + +namespace Data { +namespace { + +[[nodiscard]] bool Validate(int day, int month, int year) { + if (year != 0 && (year < 1900 || year > 2100)) { + return false; + } else if (day < 1) { + return false; + } else if (month == 2) { + if (day == 29) { + return !year + || (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + } + return day <= 28; + } else if (month == 4 || month == 6 || month == 9 || month == 11) { + return day <= 30; + } else if (month > 0 && month <= 12) { + return day <= 31; + } + return false; +} + +[[nodiscard]] int Serialize(int day, int month, int year) { + return day + month * 100 + year * 10000; +} + +} // namespace + +Birthday::Birthday() = default; + +Birthday::Birthday(int day, int month, int year) +: _value(Validate(day, month, year) ? Serialize(day, month, year) : 0) { +} + +Birthday Birthday::FromSerialized(int value) { + return Birthday(value % 100, (value / 100) % 100, value / 10000); +} + +int Birthday::serialize() const { + return _value; +} + +bool Birthday::valid() const { + return _value != 0; +} + +int Birthday::day() const { + return _value % 100; +} + +int Birthday::month() const { + return (_value / 100) % 100; +} + +int Birthday::year() const { + return _value / 10000; +} + +} // namespace Data + diff --git a/Telegram/SourceFiles/data/data_birthday.h b/Telegram/SourceFiles/data/data_birthday.h new file mode 100644 index 000000000..13a5e4cfe --- /dev/null +++ b/Telegram/SourceFiles/data/data_birthday.h @@ -0,0 +1,38 @@ +/* +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 +*/ +#pragma once + +namespace Data { + +class Birthday final { +public: + Birthday(); + Birthday(int day, int month, int year = 0); + + [[nodiscard]] static Birthday FromSerialized(int value); + [[nodiscard]] int serialize() const; + + [[nodiscard]] bool valid() const; + + [[nodiscard]] int day() const; + [[nodiscard]] int month() const; + [[nodiscard]] int year() const; + + explicit operator bool() const { + return valid(); + } + + friend inline constexpr auto operator<=>(Birthday, Birthday) = default; + friend inline constexpr bool operator==(Birthday, Birthday) = default; + +private: + int _value = 0; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index bd85b02fa..eeb80ccb7 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -89,27 +89,28 @@ struct PeerUpdate { IsBot = (1ULL << 27), EmojiStatus = (1ULL << 28), BusinessDetails = (1ULL << 29), + Birthday = (1ULL << 30), // For chats and channels - InviteLinks = (1ULL << 30), - Members = (1ULL << 31), - Admins = (1ULL << 32), - BannedUsers = (1ULL << 33), - Rights = (1ULL << 34), - PendingRequests = (1ULL << 35), - Reactions = (1ULL << 36), + InviteLinks = (1ULL << 31), + Members = (1ULL << 32), + Admins = (1ULL << 33), + BannedUsers = (1ULL << 34), + Rights = (1ULL << 35), + PendingRequests = (1ULL << 36), + Reactions = (1ULL << 37), // For channels - ChannelAmIn = (1ULL << 37), - StickersSet = (1ULL << 38), - EmojiSet = (1ULL << 39), - ChannelLinkedChat = (1ULL << 40), - ChannelLocation = (1ULL << 41), - Slowmode = (1ULL << 42), - GroupCall = (1ULL << 43), + ChannelAmIn = (1ULL << 38), + StickersSet = (1ULL << 39), + EmojiSet = (1ULL << 40), + ChannelLinkedChat = (1ULL << 41), + ChannelLocation = (1ULL << 42), + Slowmode = (1ULL << 43), + GroupCall = (1ULL << 44), // For iteration - LastUsedBit = (1ULL << 43), + LastUsedBit = (1ULL << 44), }; using Flags = base::flags<Flag>; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index ebe681ac3..ea5d0dad1 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -482,6 +482,30 @@ void UserData::setCallsStatus(CallsStatus callsStatus) { } } + +Data::Birthday UserData::birthday() const { + return _birthday; +} + +void UserData::setBirthday(Data::Birthday value) { + if (_birthday != value) { + _birthday = value; + session().changes().peerUpdated(this, UpdateFlag::Birthday); + } +} + +void UserData::setBirthday(const tl::conditional<MTPBirthday> &value) { + if (!value) { + setBirthday(Data::Birthday()); + } else { + const auto &data = value->data(); + setBirthday(Data::Birthday( + data.vday().v, + data.vmonth().v, + data.vyear().value_or_empty())); + } +} + bool UserData::hasCalls() const { return (callsStatus() != CallsStatus::Disabled) && (callsStatus() != CallsStatus::Unknown); @@ -598,6 +622,7 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) { update.vbusiness_work_hours(), update.vbusiness_location(), update.vbusiness_intro())); + user->setBirthday(update.vbirthday()); if (user->isSelf()) { user->owner().businessInfo().applyAwaySettings( FromMTP(&user->owner(), update.vbusiness_away_message())); diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index cf9eafef7..fd992a0ef 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "data/data_birthday.h" #include "data/data_peer.h" #include "data/data_chat_participant_status.h" #include "data/data_lastseen_status.h" @@ -179,6 +180,10 @@ public: bool hasCalls() const; void setCallsStatus(CallsStatus callsStatus); + [[nodiscard]] Data::Birthday birthday() const; + void setBirthday(Data::Birthday value); + void setBirthday(const tl::conditional<MTPBirthday> &value); + std::unique_ptr<BotInfo> botInfo; void setUnavailableReasons( @@ -204,6 +209,10 @@ private: Flags _flags; Data::LastseenStatus _lastseen; + Data::Birthday _birthday; + int _commonChatsCount = 0; + ContactStatus _contactStatus = ContactStatus::Unknown; + CallsStatus _callsStatus = CallsStatus::Unknown; Data::UsernamesInfo _username; @@ -211,9 +220,6 @@ private: std::vector<Data::UnavailableReason> _unavailableReasons; QString _phone; QString _privateForwardName; - ContactStatus _contactStatus = ContactStatus::Unknown; - CallsStatus _callsStatus = CallsStatus::Unknown; - int _commonChatsCount = 0; uint64 _accessHash = 0; static constexpr auto kInaccessibleAccessHashOld diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 2545afbf9..9ad3e3954 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -152,6 +152,10 @@ settingsPrivacySecurityPadding: 12px; settingsPrivacySkip: 14px; settingsPrivacySkipTop: 4px; +settingsPrivacyAddBirthday: FlatLabel(defaultFlatLabel) { + minWidth: 256px; +} + settingsCloudPasswordIconSize: 100px; settingLocalPasscodeInputField: InputField(defaultInputField) { diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index 36a4ef35a..b0657d2b1 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -975,6 +975,7 @@ auto ForwardsPrivacyController::exceptionsDescription() const } object_ptr<Ui::RpWidget> ForwardsPrivacyController::setupAboveWidget( + not_null<Window::SessionController*> controller, not_null<QWidget*> parent, rpl::producer<Option> optionValue, not_null<QWidget*> outerContainer) { @@ -982,7 +983,7 @@ object_ptr<Ui::RpWidget> ForwardsPrivacyController::setupAboveWidget( auto message = GenerateForwardedItem( delegate(), - _controller->session().data().history( + controller->session().data().history( PeerData::kServiceNotificationsId), tr::lng_edit_privacy_forwards_sample_message(tr::now)); const auto view = message.get(); @@ -1072,18 +1073,18 @@ object_ptr<Ui::RpWidget> ForwardsPrivacyController::setupAboveWidget( ) | rpl::start_with_next([=](QRect rect) { // #TODO themes Window::SectionWidget::PaintBackground( - _controller, - _controller->defaultChatTheme().get(), // #TODO themes + controller, + controller->defaultChatTheme().get(), // #TODO themes widget, rect); Painter p(widget); - const auto theme = _controller->defaultChatTheme().get(); + const auto theme = controller->defaultChatTheme().get(); auto context = theme->preparePaintContext( _chatStyle.get(), widget->rect(), widget->rect(), - _controller->isGifPausedAtLeastFor( + controller->isGifPausedAtLeastFor( Window::GifPauseReason::Layer)); p.translate(padding / 2, padding + view->marginBottom()); context.outbg = view->hasOutLayout(); @@ -1115,6 +1116,7 @@ rpl::producer<QString> ProfilePhotoPrivacyController::optionsTitleKey() const { } object_ptr<Ui::RpWidget> ProfilePhotoPrivacyController::setupAboveWidget( + not_null<Window::SessionController*> controller, not_null<QWidget*> parent, rpl::producer<Option> optionValue, not_null<QWidget*> outerContainer) { @@ -1475,7 +1477,74 @@ rpl::producer<QString> AboutPrivacyController::exceptionBoxTitle( auto AboutPrivacyController::exceptionsDescription() const -> rpl::producer<QString> { - return tr::lng_edit_privacy_about_exceptions(); + return tr::lng_edit_privacy_birthday_exceptions(); +} + +UserPrivacy::Key BirthdayPrivacyController::key() const { + return Key::Birthday; +} + +rpl::producer<QString> BirthdayPrivacyController::title() const { + return tr::lng_edit_privacy_birthday_title(); +} + +rpl::producer<QString> BirthdayPrivacyController::optionsTitleKey() const { + return tr::lng_edit_privacy_birthday_header(); +} + +rpl::producer<QString> BirthdayPrivacyController::exceptionButtonTextKey( + Exception exception) const { + switch (exception) { + case Exception::Always: return tr::lng_edit_privacy_birthday_always_empty(); + case Exception::Never: return tr::lng_edit_privacy_birthday_never_empty(); + } + Unexpected("Invalid exception value."); +} + +rpl::producer<QString> BirthdayPrivacyController::exceptionBoxTitle( + Exception exception) const { + switch (exception) { + case Exception::Always: return tr::lng_edit_privacy_birthday_always_title(); + case Exception::Never: return tr::lng_edit_privacy_birthday_never_title(); + } + Unexpected("Invalid exception value."); +} + +auto BirthdayPrivacyController::exceptionsDescription() const +-> rpl::producer<QString> { + return tr::lng_edit_privacy_birthday_exceptions(); +} + +object_ptr<Ui::RpWidget> BirthdayPrivacyController::setupAboveWidget( + not_null<Window::SessionController*> controller, + not_null<QWidget*> parent, + rpl::producer<Option> optionValue, + not_null<QWidget*> outerContainer) { + const auto session = &controller->session(); + const auto user = session->user(); + auto result = object_ptr<Ui::SlideWrap<Ui::FlatLabel>>( + parent, + object_ptr<Ui::FlatLabel>( + parent, + tr::lng_edit_privacy_birthday_yet( + lt_link, + tr::lng_edit_privacy_birthday_yet_link( + ) | Ui::Text::ToLink("internal:edit_birthday"), + Ui::Text::WithEntities), + st::settingsPrivacyAddBirthday), + st::boxRowPadding + style::margins( + 0, + st::defaultVerticalListSkip, + 0, + st::settingsPrivacySkipTop)); + result->toggleOn(session->changes().peerFlagsValue( + user, + Data::PeerUpdate::Flag::Birthday + ) | rpl::map([=] { + return !user->birthday(); + })); + result->finishAnimating(); + return result; } } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.h b/Telegram/SourceFiles/settings/settings_privacy_controllers.h index 25920a1f0..23a727808 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.h +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.h @@ -215,6 +215,7 @@ public: rpl::producer<QString> exceptionsDescription() const override; object_ptr<Ui::RpWidget> setupAboveWidget( + not_null<Window::SessionController*> controller, not_null<QWidget*> parent, rpl::producer<Option> optionValue, not_null<QWidget*> outerContainer) override; @@ -249,6 +250,7 @@ public: rpl::producer<int> value) override; object_ptr<Ui::RpWidget> setupAboveWidget( + not_null<Window::SessionController*> controller, not_null<QWidget*> parent, rpl::producer<Option> optionValue, not_null<QWidget*> outerContainer) override; @@ -314,4 +316,27 @@ public: }; +class BirthdayPrivacyController final : public EditPrivacyController { +public: + using Option = EditPrivacyBox::Option; + using Exception = EditPrivacyBox::Exception; + + Key key() const override; + + rpl::producer<QString> title() const override; + rpl::producer<QString> optionsTitleKey() const override; + rpl::producer<QString> exceptionButtonTextKey( + Exception exception) const override; + rpl::producer<QString> exceptionBoxTitle( + Exception exception) const override; + rpl::producer<QString> exceptionsDescription() const override; + + object_ptr<Ui::RpWidget> setupAboveWidget( + not_null<Window::SessionController*> controller, + not_null<QWidget*> parent, + rpl::producer<Option> optionValue, + not_null<QWidget*> outerContainer) override; + +}; + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index 6d16cac01..1b70efbb4 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -135,6 +135,8 @@ QString PrivacyBase(Privacy::Key key, Privacy::Option option) { case Option::Contacts: return tr::lng_edit_privacy_contacts(tr::now); case Option::CloseFriends: return tr::lng_edit_privacy_close_friends(tr::now); + case Option::ContactsAndPremium: + return tr::lng_edit_privacy_contacts_and_premium(tr::now); case Option::Nobody: return tr::lng_edit_privacy_nobody(tr::now); } Unexpected("Value in Privacy::Option."); @@ -355,6 +357,10 @@ void SetupPrivacy( tr::lng_settings_bio_privacy(), Key::About, [] { return std::make_unique<AboutPrivacyController>(); }); + add( + tr::lng_settings_birthday_privacy(), + Key::Birthday, + [] { return std::make_unique<BirthdayPrivacyController>(); }); add( tr::lng_settings_forwards_privacy(), Key::Forwards, diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 0e98ad7b8..5a4b2bd13 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -76,6 +76,8 @@ PRIVATE countries/countries_instance.cpp countries/countries_instance.h + data/data_birthday.cpp + data/data_birthday.h data/data_statistics_chart.cpp data/data_statistics_chart.h data/data_subscription_option.h