From ca25ad57b1a98e1673e9cbbf592e6f6d362ba601 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Jan 2024 13:13:30 +0400 Subject: [PATCH] Update API scheme to layer 172. --- Telegram/Resources/langs/lang.strings | 26 ++++++++ .../SourceFiles/api/api_global_privacy.cpp | 62 ++++++++++++++++-- Telegram/SourceFiles/api/api_global_privacy.h | 14 +++- .../SourceFiles/api/api_send_progress.cpp | 2 +- Telegram/SourceFiles/api/api_updates.cpp | 28 ++++---- Telegram/SourceFiles/apiwrap.cpp | 38 +++-------- Telegram/SourceFiles/apiwrap.h | 4 -- .../SourceFiles/data/data_peer_values.cpp | 12 ++-- Telegram/SourceFiles/data/data_session.cpp | 23 +++++-- Telegram/SourceFiles/data/data_user.cpp | 64 ++++++++++++++++++- Telegram/SourceFiles/data/data_user.h | 21 ++++++ Telegram/SourceFiles/mtproto/scheme/api.tl | 13 ++-- .../touchbar/items/mac_pinned_chats_item.mm | 2 +- Telegram/SourceFiles/settings/settings.style | 6 +- .../settings/settings_privacy_controllers.cpp | 56 +++++++++++++++- .../settings/settings_privacy_controllers.h | 5 ++ 16 files changed, 295 insertions(+), 81 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 357838992e..bf186e1f7d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -141,6 +141,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_status_last_week" = "last seen within a week"; "lng_status_last_month" = "last seen within a month"; "lng_status_lastseen_now" = "last seen just now"; +"lng_status_lastseen_hidden" = "last seen hidden"; +"lng_status_lastseen_show" = "show"; "lng_status_lastseen_minutes#one" = "last seen {count} minute ago"; "lng_status_lastseen_minutes#other" = "last seen {count} minutes ago"; "lng_status_lastseen_hours#one" = "last seen {count} hour ago"; @@ -168,6 +170,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_remember" = "Remember this choice"; +"lng_lastseen_show_title" = "Show your last seen"; +"lng_lastseen_show_about" = "To see **{user}'s** Last Seen time, either start showing your own Last Seen time..."; +"lng_lastseen_show_button" = "Show my Last Seen"; +"lng_lastseen_or" = "or"; +"lng_lastseen_premium_title" = "Upgrade to Premium"; +"lng_lastseen_premium_about" = "Subscription will let you see **{user}'s** Last Seen status without showing yours."; +"lng_lastseen_premium_button" = "Subscribe to Telegram Premium"; +"lng_lastseen_shown_toast" = "Your last seen time is now visible."; + +"lng_readtime_show_title" = "Show your read date"; +"lng_readtime_show_about" = "To see when **{user}** read the message, either start showing your own read time..."; +"lng_readtime_show_button" = "Show my Read Time"; +"lng_readtime_or" = "or"; +"lng_readtime_premium_title" = "Upgrade to Premium"; +"lng_readtime_premium_about" = "Subscription will let you see **{user}'s** read time without showing yours."; +"lng_readtime_premium_button" = "Subscribe to Telegram Premium"; +"lng_readtime_shown_toast" = "Your read times are now visible."; + "lng_channels_limit_title" = "Too Many Communities"; "lng_channels_limit1#one" = "You are a member of **{count}** groups and channels."; "lng_channels_limit1#other" = "You are a member of **{count}** groups and channels."; @@ -1086,6 +1106,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_lastseen_exceptions" = "These settings will override the values above."; "lng_edit_privacy_lastseen_always_title" = "Always share with"; "lng_edit_privacy_lastseen_never_title" = "Never share with"; +"lng_edit_lastseen_hide_read_time" = "Hide read time"; +"lng_edit_lastseen_hide_read_time_about" = "Do not show the time when you read a message to people you hid your last seen from. If you turn this on, their read time will also be hidden from you. This does not affect group chats."; +"lng_edit_lastseen_subscribe" = "Subscribe to Telegram Premium"; +"lng_edit_lastseen_subscribe_about" = "If you subscribe to Premium, you will see other users' last seen and read time even if you hid yours from them (unless they specifically restricted it)."; "lng_edit_privacy_groups_title" = "Group invite settings"; "lng_edit_privacy_groups_header" = "Who can invite you to groups and channels"; @@ -2764,6 +2788,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_save_custom_sound" = "Save for notifications"; "lng_context_translate" = "Translate"; "lng_context_translate_selected" = "Translate Selected Text"; +"lng_context_read_hidden" = "read"; +"lng_context_read_show" = "show when"; "lng_context_animated_emoji" = "This message contains emoji from **{name} pack**."; "lng_context_animated_emoji_many#one" = "This message contains emoji from **{count} pack**."; diff --git a/Telegram/SourceFiles/api/api_global_privacy.cpp b/Telegram/SourceFiles/api/api_global_privacy.cpp index 219d335f15..f958bdfe80 100644 --- a/Telegram/SourceFiles/api/api_global_privacy.cpp +++ b/Telegram/SourceFiles/api/api_global_privacy.cpp @@ -84,18 +84,61 @@ void GlobalPrivacy::dismissArchiveAndMuteSuggestion() { u"AUTOARCHIVE_POPULAR"_q); } +void GlobalPrivacy::updateHideReadTime(bool hide) { + update( + archiveAndMuteCurrent(), + unarchiveOnNewMessageCurrent(), + hide, + newRequirePremiumCurrent()); +} + +bool GlobalPrivacy::hideReadTimeCurrent() const { + return _hideReadTime.current(); +} + +rpl::producer GlobalPrivacy::hideReadTime() const { + return _hideReadTime.value(); +} + +void GlobalPrivacy::updateNewRequirePremium(bool value) { + update( + archiveAndMuteCurrent(), + unarchiveOnNewMessageCurrent(), + hideReadTimeCurrent(), + value); +} + +bool GlobalPrivacy::newRequirePremiumCurrent() const { + return _newRequirePremium.current(); +} + +rpl::producer GlobalPrivacy::newRequirePremium() const { + return _newRequirePremium.value(); +} + + void GlobalPrivacy::updateArchiveAndMute(bool value) { - update(value, unarchiveOnNewMessageCurrent()); + update( + value, + unarchiveOnNewMessageCurrent(), + hideReadTimeCurrent(), + newRequirePremiumCurrent()); } void GlobalPrivacy::updateUnarchiveOnNewMessage( UnarchiveOnNewMessage value) { - update(archiveAndMuteCurrent(), value); + update( + archiveAndMuteCurrent(), + value, + hideReadTimeCurrent(), + newRequirePremiumCurrent()); } void GlobalPrivacy::update( bool archiveAndMute, - UnarchiveOnNewMessage unarchiveOnNewMessage) { + UnarchiveOnNewMessage unarchiveOnNewMessage, + bool hideReadTime, + bool newRequirePremium) { using Flag = MTPDglobalPrivacySettings::Flag; _api.request(_requestId).cancel(); @@ -108,17 +151,26 @@ void GlobalPrivacy::update( : Flag()) | (unarchiveOnNewMessage != UnarchiveOnNewMessage::AnyUnmuted ? Flag::f_keep_archived_folders + : Flag()) + | (hideReadTime ? Flag::f_hide_read_marks : Flag()) + | ((newRequirePremium && _session->premium()) + ? Flag::f_new_noncontact_peers_require_premium : Flag()); _requestId = _api.request(MTPaccount_SetGlobalPrivacySettings( MTP_globalPrivacySettings(MTP_flags(flags)) )).done([=](const MTPGlobalPrivacySettings &result) { _requestId = 0; apply(result); - }).fail([=] { + }).fail([=](const MTP::Error &error) { _requestId = 0; + if (error.type() == u"PREMIUM_ACCOUNT_REQUIRED"_q) { + update(archiveAndMute, unarchiveOnNewMessage, hideReadTime, {}); + } }).send(); _archiveAndMute = archiveAndMute; _unarchiveOnNewMessage = unarchiveOnNewMessage; + _hideReadTime = hideReadTime; + _newRequirePremium = newRequirePremium; } void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) { @@ -129,6 +181,8 @@ void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) { : data.is_keep_archived_folders() ? UnarchiveOnNewMessage::NotInFoldersUnmuted : UnarchiveOnNewMessage::AnyUnmuted; + _hideReadTime = data.is_hide_read_marks(); + _newRequirePremium = data.is_new_noncontact_peers_require_premium(); }); } diff --git a/Telegram/SourceFiles/api/api_global_privacy.h b/Telegram/SourceFiles/api/api_global_privacy.h index 9e4b8e1215..de70ba9b97 100644 --- a/Telegram/SourceFiles/api/api_global_privacy.h +++ b/Telegram/SourceFiles/api/api_global_privacy.h @@ -41,12 +41,22 @@ public: [[nodiscard]] rpl::producer<> suggestArchiveAndMute() const; void dismissArchiveAndMuteSuggestion(); + void updateHideReadTime(bool hide); + [[nodiscard]] bool hideReadTimeCurrent() const; + [[nodiscard]] rpl::producer hideReadTime() const; + + void updateNewRequirePremium(bool value); + [[nodiscard]] bool newRequirePremiumCurrent() const; + [[nodiscard]] rpl::producer newRequirePremium() const; + private: void apply(const MTPGlobalPrivacySettings &data); void update( bool archiveAndMute, - UnarchiveOnNewMessage unarchiveOnNewMessage); + UnarchiveOnNewMessage unarchiveOnNewMessage, + bool hideReadTime, + bool newRequirePremium); const not_null _session; MTP::Sender _api; @@ -55,6 +65,8 @@ private: rpl::variable _unarchiveOnNewMessage = UnarchiveOnNewMessage::None; rpl::variable _showArchiveAndMute = false; + rpl::variable _hideReadTime = false; + rpl::variable _newRequirePremium = false; std::vector> _callbacks; }; diff --git a/Telegram/SourceFiles/api/api_send_progress.cpp b/Telegram/SourceFiles/api/api_send_progress.cpp index 54d3c4e00e..d3e277261c 100644 --- a/Telegram/SourceFiles/api/api_send_progress.cpp +++ b/Telegram/SourceFiles/api/api_send_progress.cpp @@ -162,7 +162,7 @@ bool SendProgressManager::skipRequest(const Key &key) const { } const auto recently = base::unixtime::now() - kSendTypingsToOfflineFor; const auto online = user->onlineTill; - if (online == -2) { // last seen recently + if (online == kOnlineRecently) { return false; } else if (online < 0) { return (-online < recently); diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index dde1a68cd5..54e2a37b28 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -944,7 +944,8 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) { } const auto self = session().user(); - self->onlineTill = base::unixtime::now() + (isOnline ? (config.onlineUpdatePeriod / 1000) : -1); + self->onlineTill = base::unixtime::now() + + (isOnline ? (config.onlineUpdatePeriod / 1000) : -1); session().changes().peerUpdated( self, Data::PeerUpdate::Flag::OnlineStatus); @@ -1850,23 +1851,16 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateUserStatus: { auto &d = update.c_updateUserStatus(); - if (auto user = session().data().userLoaded(d.vuser_id())) { - switch (d.vstatus().type()) { - case mtpc_userStatusEmpty: user->onlineTill = 0; break; - case mtpc_userStatusRecently: - if (user->onlineTill > -10) { // don't modify pseudo-online - user->onlineTill = -2; - } - break; - case mtpc_userStatusLastWeek: user->onlineTill = -3; break; - case mtpc_userStatusLastMonth: user->onlineTill = -4; break; - case mtpc_userStatusOffline: user->onlineTill = d.vstatus().c_userStatusOffline().vwas_online().v; break; - case mtpc_userStatusOnline: user->onlineTill = d.vstatus().c_userStatusOnline().vexpires().v; break; + if (const auto user = session().data().userLoaded(d.vuser_id())) { + const auto value = OnlineTillFromMTP( + d.vstatus(), + user->onlineTill); + if (user->onlineTill != value) { + session().changes().peerUpdated( + user, + Data::PeerUpdate::Flag::OnlineStatus); + session().data().maybeStopWatchForOffline(user); } - session().changes().peerUpdated( - user, - Data::PeerUpdate::Flag::OnlineStatus); - session().data().maybeStopWatchForOffline(user); } if (UserId(d.vuser_id()) == session().userId()) { if (d.vstatus().type() == mtpc_userStatusOffline diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index a247ad1964..b3940a5b0d 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1923,21 +1923,18 @@ void ApiWrap::saveDraftToCloudDelayed(not_null thread) { void ApiWrap::updatePrivacyLastSeens() { const auto now = base::unixtime::now(); _session->data().enumerateUsers([&](UserData *user) { - if (user->isSelf() || !user->isLoaded()) { - return; - } - if (user->onlineTill <= 0) { + if (user->isSelf() || !user->isLoaded() || user->onlineTill <= 0) { return; } if (user->onlineTill + 3 * 86400 >= now) { - user->onlineTill = -2; // recently + user->onlineTill = kOnlineRecently; } else if (user->onlineTill + 7 * 86400 >= now) { - user->onlineTill = -3; // last week + user->onlineTill = kOnlineLastWeek; } else if (user->onlineTill + 30 * 86400 >= now) { - user->onlineTill = -4; // last month + user->onlineTill = kOnlineLastMonth; } else { - user->onlineTill = 0; + user->onlineTill = kOnlineEmpty; } session().changes().peerUpdated( user, @@ -1955,12 +1952,11 @@ void ApiWrap::updatePrivacyLastSeens() { Assert(item.type() == mtpc_contactStatus); auto &data = item.c_contactStatus(); if (auto user = _session->data().userLoaded(data.vuser_id())) { - auto oldOnlineTill = user->onlineTill; - auto newOnlineTill = OnlineTillFromStatus( + auto value = OnlineTillFromMTP( data.vstatus(), - oldOnlineTill); - if (oldOnlineTill != newOnlineTill) { - user->onlineTill = newOnlineTill; + user->onlineTill); + if (user->onlineTill != value) { + user->onlineTill = value; session().changes().peerUpdated( user, Data::PeerUpdate::Flag::OnlineStatus); @@ -1973,22 +1969,6 @@ void ApiWrap::updatePrivacyLastSeens() { }).send(); } -int ApiWrap::OnlineTillFromStatus( - const MTPUserStatus &status, - int currentOnlineTill) { - switch (status.type()) { - case mtpc_userStatusEmpty: return 0; - case mtpc_userStatusRecently: - // Don't modify pseudo-online. - return (currentOnlineTill > -10) ? -2 : currentOnlineTill; - case mtpc_userStatusLastWeek: return -3; - case mtpc_userStatusLastMonth: return -4; - case mtpc_userStatusOffline: return status.c_userStatusOffline().vwas_online().v; - case mtpc_userStatusOnline: return status.c_userStatusOnline().vexpires().v; - } - Unexpected("Bad UserStatus type."); -} - void ApiWrap::clearHistory(not_null peer, bool revoke) { deleteHistory(peer, true, revoke); } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 7e44d460c3..192e14f28a 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -258,10 +258,6 @@ public: void updateNotifySettingsDelayed(Data::DefaultNotify type); void saveDraftToCloudDelayed(not_null thread); - static int OnlineTillFromStatus( - const MTPUserStatus &status, - int currentOnlineTill); - void clearHistory(not_null peer, bool revoke); void deleteConversation(not_null peer, bool revoke); diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 1670afa8f4..c6458fc1d8 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -67,13 +67,13 @@ std::optional OnlineTextSpecial(not_null user) { std::optional OnlineTextCommon(TimeId online, TimeId now) { if (online <= 0) { switch (online) { - case 0: - case -1: return tr::lng_status_offline(tr::now); - case -2: return tr::lng_status_recently(tr::now); - case -3: return tr::lng_status_last_week(tr::now); - case -4: return tr::lng_status_last_month(tr::now); + case kOnlineEmpty: return tr::lng_status_offline(tr::now); + case kOnlineRecently: return tr::lng_status_recently(tr::now); + case kOnlineLastWeek: return tr::lng_status_last_week(tr::now); + case kOnlineLastMonth: return tr::lng_status_last_month(tr::now); + case kOnlineHidden: return tr::lng_status_lastseen_hidden(tr::now); } - return (-online > now) + return IsRecentOnline(online, now) ? tr::lng_status_online(tr::now) : tr::lng_status_recently(tr::now); } else if (online > now) { diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index de0a3c62d1..8d235bc444 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -519,6 +519,8 @@ not_null Session::processUser(const MTPUser &data) { | Flag::BotInlineGeo | Flag::Premium | Flag::Support + | Flag::SomeRequirePremiumToWrite + | Flag::RequirePremiumToWriteKnown | (!minimal ? Flag::Contact | Flag::MutualContact @@ -539,10 +541,20 @@ not_null Session::processUser(const MTPUser &data) { | (data.is_bot_inline_geo() ? Flag::BotInlineGeo : Flag()) | (data.is_premium() ? Flag::Premium : Flag()) | (data.is_support() ? Flag::Support : Flag()) + | (data.is_contact_require_premium() + ? (Flag::SomeRequirePremiumToWrite + | (result->someRequirePremiumToWrite() + ? (result->requirePremiumToWriteKnown() + ? Flag::RequirePremiumToWriteKnown + : Flag()) + : Flag())) + : Flag()) | (!minimal ? (data.is_contact() ? Flag::Contact : Flag()) | (data.is_mutual_contact() ? Flag::MutualContact : Flag()) - | (data.is_apply_min_photo() ? Flag() : Flag::DiscardMinPhoto) + | (data.is_apply_min_photo() + ? Flag() + : Flag::DiscardMinPhoto) | (data.is_stories_hidden() ? Flag::StoriesHidden : Flag()) : Flag()); result->setFlags((result->flags() & ~flagsMask) | flagsSet); @@ -718,12 +730,9 @@ not_null Session::processUser(const MTPUser &data) { } if (status && !minimal) { - const auto oldOnlineTill = result->onlineTill; - const auto newOnlineTill = ApiWrap::OnlineTillFromStatus( - *status, - oldOnlineTill); - if (oldOnlineTill != newOnlineTill) { - result->onlineTill = newOnlineTill; + const auto value = OnlineTillFromMTP(*status, result->onlineTill); + if (result->onlineTill != value) { + result->onlineTill = value; flags |= UpdateFlag::OnlineStatus; session().data().maybeStopWatchForOffline(result); } diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 3f83a9e945..b455fba662 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -34,6 +34,40 @@ using UpdateFlag = Data::PeerUpdate::Flag; BotInfo::BotInfo() = default; +int RecentOnlineAfter(TimeId when) { + return (when > 0) ? (-when - kSetOnlineAfterActivity) : 0; +} + +bool IsRecentOnlineValue(int value) { + return (value < -kSetOnlineAfterActivity); +} + +bool IsRecentOnline(int value, TimeId now) { + return IsRecentOnlineValue(value) && (now < -value); +} + +int OnlineTillFromMTP( + const MTPUserStatus &status, + int currentOnlineTill) { + return status.match([](const MTPDuserStatusEmpty &) { + return kOnlineEmpty; + }, [&](const MTPDuserStatusRecently&) { + return IsRecentOnlineValue(currentOnlineTill) + ? currentOnlineTill + : kOnlineRecently; + }, [](const MTPDuserStatusLastWeek &) { + return kOnlineLastWeek; + }, [](const MTPDuserStatusLastMonth &) { + return kOnlineLastMonth; + }, [](const MTPDuserStatusHidden &) { + return kOnlineHidden; + }, [](const MTPDuserStatusOnline& data) { + return data.vexpires().v; + }, [](const MTPDuserStatusOffline &data) { + return data.vwas_online().v; + }); +} + UserData::UserData(not_null owner, PeerId id) : PeerData(owner, id) , _flags((id == owner->session().userPeerId()) ? Flag::Self : Flag(0)) { @@ -351,6 +385,22 @@ bool UserData::hasStoriesHidden() const { return (flags() & UserDataFlag::StoriesHidden); } +bool UserData::someRequirePremiumToWrite() const { + return (flags() & UserDataFlag::SomeRequirePremiumToWrite); +} + +bool UserData::meRequiresPremiumToWrite() const { + return (flags() & UserDataFlag::MeRequiresPremiumToWrite); +} + +bool UserData::requirePremiumToWriteKnown() const { + return (flags() & UserDataFlag::RequirePremiumToWriteKnown); +} + +bool UserData::readDatesPrivate() const { + return (flags() & UserDataFlag::ReadDatesPrivate); +} + bool UserData::canAddContact() const { return canShareThisContact() && !isContact(); } @@ -453,15 +503,25 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { | Flag::PhoneCallsPrivate | Flag::CanReceiveGifts | Flag::CanPinMessages - | Flag::VoiceMessagesForbidden; + | Flag::VoiceMessagesForbidden + | Flag::ReadDatesPrivate + | Flag::RequirePremiumToWriteKnown + | Flag::MeRequiresPremiumToWrite; user->setFlags((user->flags() & ~mask) - | (update.is_phone_calls_private() ? Flag::PhoneCallsPrivate : Flag()) + | (update.is_phone_calls_private() + ? Flag::PhoneCallsPrivate + : Flag()) | (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_voice_messages_forbidden() ? Flag::VoiceMessagesForbidden + : Flag()) + | (update.is_read_dates_private() ? Flag::ReadDatesPrivate : Flag()) + | Flag::RequirePremiumToWriteKnown + | (update.is_contact_require_premium() + ? Flag::MeRequiresPremiumToWrite : Flag())); user->setIsBlocked(update.is_blocked()); user->setCallsStatus(update.is_phone_calls_private() diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 3286612605..2951cd9ef0 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -65,10 +65,27 @@ enum class UserDataFlag { StoriesHidden = (1 << 18), HasActiveStories = (1 << 19), HasUnreadStories = (1 << 20), + MeRequiresPremiumToWrite = (1 << 21), + SomeRequirePremiumToWrite = (1 << 22), + RequirePremiumToWriteKnown = (1 << 23), + ReadDatesPrivate = (1 << 24), }; inline constexpr bool is_flag_type(UserDataFlag) { return true; }; using UserDataFlags = base::flags; +inline constexpr auto kOnlineEmpty = 0; +inline constexpr auto kOnlineRecently = -2; +inline constexpr auto kOnlineLastWeek = -3; +inline constexpr auto kOnlineLastMonth = -4; +inline constexpr auto kOnlineHidden = -5; + +[[nodiscard]] int RecentOnlineAfter(TimeId when); +[[nodiscard]] bool IsRecentOnlineValue(int value); +[[nodiscard]] bool IsRecentOnline(int value, TimeId now); +[[nodiscard]] int OnlineTillFromMTP( + const MTPUserStatus& status, + int currentOnlineTill); + class UserData final : public PeerData { public: using Flag = UserDataFlag; @@ -119,6 +136,10 @@ public: [[nodiscard]] bool applyMinPhoto() const; [[nodiscard]] bool hasPersonalPhoto() const; [[nodiscard]] bool hasStoriesHidden() const; + [[nodiscard]] bool someRequirePremiumToWrite() const; + [[nodiscard]] bool meRequiresPremiumToWrite() const; + [[nodiscard]] bool requirePremiumToWriteKnown() const; + [[nodiscard]] bool readDatesPrivate() const; [[nodiscard]] bool canShareThisContact() const; [[nodiscard]] bool canAddContact() const; diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl index fee427f9f0..74f938682d 100644 --- a/Telegram/SourceFiles/mtproto/scheme/api.tl +++ b/Telegram/SourceFiles/mtproto/scheme/api.tl @@ -82,7 +82,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType; storage.fileWebp#1081464c = storage.FileType; userEmpty#d3bc4b7a id:long = User; -user#215c4438 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor = User; +user#215c4438 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true contact_require_premium:flags2.6?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhoto#82d1f706 flags:# has_video:flags.0?true personal:flags.2?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto; @@ -93,6 +93,7 @@ userStatusOffline#8c703f was_online:int = UserStatus; userStatusRecently#e26f42f1 = UserStatus; userStatusLastWeek#7bf09fc = UserStatus; userStatusLastMonth#77ebc742 = UserStatus; +userStatusHidden#cf7d64b1 = UserStatus; chatEmpty#29562865 id:long = Chat; chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; @@ -226,7 +227,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason; inputReportReasonIllegalDrugs#a8eb2be = ReportReason; inputReportReasonPersonalDetails#9ec7863d = ReportReason; -userFull#b9b12c6c flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector wallpaper:flags.24?WallPaper stories:flags.25?PeerStories = UserFull; +userFull#b9b12c6c flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector wallpaper:flags.24?WallPaper stories:flags.25?PeerStories = UserFull; contact#145ade0b user_id:long mutual:Bool = Contact; @@ -1259,7 +1260,7 @@ statsGroupTopInviter#535f779d user_id:long invitations:int = StatsGroupTopInvite stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAndPrev messages:StatsAbsValueAndPrev viewers:StatsAbsValueAndPrev posters:StatsAbsValueAndPrev growth_graph:StatsGraph members_graph:StatsGraph new_members_by_source_graph:StatsGraph languages_graph:StatsGraph messages_graph:StatsGraph actions_graph:StatsGraph top_hours_graph:StatsGraph weekdays_graph:StatsGraph top_posters:Vector top_admins:Vector top_inviters:Vector users:Vector = stats.MegagroupStats; -globalPrivacySettings#734c4ccb flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true = GlobalPrivacySettings; +globalPrivacySettings#734c4ccb flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true = GlobalPrivacySettings; help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector patterns:flags.1?Vector = help.CountryCode; @@ -1649,6 +1650,8 @@ savedReactionTag#cb6ff828 flags:# reaction:Reaction title:flags.0?string count:i messages.savedReactionTagsNotModified#889b59ef = messages.SavedReactionTags; messages.savedReactionTags#3259950a tags:Vector hash:long = messages.SavedReactionTags; +outboxReadDate#3bb842ac date:int = OutboxReadDate; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1778,6 +1781,7 @@ account.getChannelRestrictedStatusEmojis#35a9e0d5 hash:long = EmojiList; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#b60f5918 id:InputUser = users.UserFull; users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector = Bool; +//users.getIsPremiumRequiredToContact#75c9db9c id:Vector = Vector; contacts.getContactIDs#7adc669d hash:long = Vector; contacts.getStatuses#c4a353ee = Vector; @@ -2002,6 +2006,7 @@ messages.reorderPinnedSavedDialogs#8b716587 flags:# force:flags.0?true order:Vec messages.getSavedReactionTags#761ddacf hash:long = messages.SavedReactionTags; messages.updateSavedReactionTag#60297dec flags:# reaction:Reaction title:flags.0?string = Bool; messages.getDefaultTagReactions#bdf93428 hash:long = messages.Reactions; +messages.getOutboxReadDate#8c4bfe5d peer:InputPeer msg_id:int = OutboxReadDate; updates.getState#edd4882a = updates.State; updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference; @@ -2244,4 +2249,4 @@ premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector peer:InputPeer = p premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus; premium.getUserBoosts#39854d1f peer:InputPeer user_id:InputUser = premium.BoostsList; -// LAYER 171 +// LAYER 172 diff --git a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm index 43c37c92e0..63ceb76fba 100644 --- a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm +++ b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm @@ -141,7 +141,7 @@ TimeId CalculateOnlineTill(not_null peer) { if (const auto user = peer->asUser()) { if (!user->isServiceUser() && !user->isBot()) { const auto onlineTill = user->onlineTill; - return (onlineTill <= -5) + return IsRecentOnlineValue(onlineTill) ? -onlineTill : (onlineTill <= 0) ? 0 diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 9304a3ebd8..6167097001 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -15,12 +15,14 @@ settingsButton: SettingsButton(infoProfileButton) { padding: margins(60px, 10px, 22px, 10px); iconLeft: 20px; } +settingsButtonNoIcon: SettingsButton(settingsButton) { + padding: margins(22px, 10px, 22px, 8px); +} settingsButtonLight: SettingsButton(settingsButton) { textFg: lightButtonFg; textFgOver: lightButtonFgOver; } -settingsButtonNoIcon: SettingsButton(settingsButton) { - padding: margins(22px, 10px, 22px, 8px); +settingsButtonLightNoIcon: SettingsButton(settingsButtonLight, settingsButtonNoIcon) { } settingsButtonNoIconLocked : SettingsButton(settingsButtonNoIcon) { toggle: Toggle(infoProfileToggle) { diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index cd4aee4336..c38afcd324 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/settings_privacy_controllers.h" +#include "api/api_global_privacy.h" #include "api/api_peer_photo.h" #include "apiwrap.h" #include "base/call_delayed.h" @@ -36,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_message.h" #include "lang/lang_keys.h" #include "main/main_session.h" +#include "settings/settings_premium.h" #include "settings/settings_privacy_security.h" #include "ui/boxes/confirm_box.h" #include "ui/cached_round_corners.h" @@ -665,16 +667,64 @@ auto LastSeenPrivacyController::exceptionsDescription() const return tr::lng_edit_privacy_lastseen_exceptions(); } +object_ptr LastSeenPrivacyController::setupBelowWidget( + not_null controller, + not_null parent) { + auto result = object_ptr(parent); + const auto content = result.data(); + + Ui::AddSkip(content); + + const auto privacy = &controller->session().api().globalPrivacy(); + content->add(object_ptr( + content, + tr::lng_edit_lastseen_hide_read_time(), + st::settingsButtonNoIcon + ))->toggleOn(privacy->hideReadTime())->toggledValue( + ) | rpl::start_with_next([=](bool value) { + _hideReadTime = value; + }, content->lifetime()); + + Ui::AddSkip(content); + Ui::AddDividerText( + content, + tr::lng_edit_lastseen_hide_read_time_about()); + if (!controller->session().premium()) { + Ui::AddSkip(content); + content->add(object_ptr( + content, + tr::lng_edit_lastseen_subscribe(), + st::settingsButtonLightNoIcon + ))->setClickedCallback([=] { + Settings::ShowPremium(controller, u"lastseen"_q); + }); + Ui::AddSkip(content); + Ui::AddDividerText( + content, + tr::lng_edit_lastseen_subscribe_about()); + } + + return result; +} + void LastSeenPrivacyController::confirmSave( bool someAreDisallowed, Fn saveCallback) { + const auto privacy = &_session->api().globalPrivacy(); + const auto hideReadTime = _hideReadTime; + const auto save = [=, saveCallback = std::move(saveCallback)] { + if (privacy->hideReadTimeCurrent() != hideReadTime) { + privacy->updateHideReadTime(hideReadTime); + } + saveCallback(); + }; if (someAreDisallowed && !Core::App().settings().lastSeenWarningSeen()) { auto callback = [ =, - saveCallback = std::move(saveCallback) + save = std::move(save) ](Fn &&close) { close(); - saveCallback(); + save(); Core::App().settings().setLastSeenWarningSeen(true); Core::App().saveSettingsDelayed(); }; @@ -685,7 +735,7 @@ void LastSeenPrivacyController::confirmSave( }); Ui::show(std::move(box), Ui::LayerOption::KeepOther); } else { - saveCallback(); + save(); } } diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.h b/Telegram/SourceFiles/settings/settings_privacy_controllers.h index e2c8c187fc..5211d07314 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.h +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.h @@ -109,12 +109,17 @@ public: Exception exception) const override; rpl::producer exceptionsDescription() const override; + object_ptr setupBelowWidget( + not_null controller, + not_null parent) override; + void confirmSave( bool someAreDisallowed, Fn saveCallback) override; private: const not_null<::Main::Session*> _session; + bool _hideReadTime = false; };