Update API scheme to layer 172.

This commit is contained in:
John Preston 2024-01-09 13:13:30 +04:00
parent c364383cf0
commit ca25ad57b1
16 changed files with 295 additions and 81 deletions

View file

@ -141,6 +141,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_status_last_week" = "last seen within a week"; "lng_status_last_week" = "last seen within a week";
"lng_status_last_month" = "last seen within a month"; "lng_status_last_month" = "last seen within a month";
"lng_status_lastseen_now" = "last seen just now"; "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#one" = "last seen {count} minute ago";
"lng_status_lastseen_minutes#other" = "last seen {count} minutes ago"; "lng_status_lastseen_minutes#other" = "last seen {count} minutes ago";
"lng_status_lastseen_hours#one" = "last seen {count} hour 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_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_limit_title" = "Too Many Communities";
"lng_channels_limit1#one" = "You are a member of **{count}** groups and channels."; "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."; "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_exceptions" = "These settings will override the values above.";
"lng_edit_privacy_lastseen_always_title" = "Always share with"; "lng_edit_privacy_lastseen_always_title" = "Always share with";
"lng_edit_privacy_lastseen_never_title" = "Never 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_title" = "Group invite settings";
"lng_edit_privacy_groups_header" = "Who can invite you to groups and channels"; "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_save_custom_sound" = "Save for notifications";
"lng_context_translate" = "Translate"; "lng_context_translate" = "Translate";
"lng_context_translate_selected" = "Translate Selected Text"; "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" = "This message contains emoji from **{name} pack**.";
"lng_context_animated_emoji_many#one" = "This message contains emoji from **{count} pack**."; "lng_context_animated_emoji_many#one" = "This message contains emoji from **{count} pack**.";

View file

@ -84,18 +84,61 @@ void GlobalPrivacy::dismissArchiveAndMuteSuggestion() {
u"AUTOARCHIVE_POPULAR"_q); u"AUTOARCHIVE_POPULAR"_q);
} }
void GlobalPrivacy::updateHideReadTime(bool hide) {
update(
archiveAndMuteCurrent(),
unarchiveOnNewMessageCurrent(),
hide,
newRequirePremiumCurrent());
}
bool GlobalPrivacy::hideReadTimeCurrent() const {
return _hideReadTime.current();
}
rpl::producer<bool> 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<bool> GlobalPrivacy::newRequirePremium() const {
return _newRequirePremium.value();
}
void GlobalPrivacy::updateArchiveAndMute(bool value) { void GlobalPrivacy::updateArchiveAndMute(bool value) {
update(value, unarchiveOnNewMessageCurrent()); update(
value,
unarchiveOnNewMessageCurrent(),
hideReadTimeCurrent(),
newRequirePremiumCurrent());
} }
void GlobalPrivacy::updateUnarchiveOnNewMessage( void GlobalPrivacy::updateUnarchiveOnNewMessage(
UnarchiveOnNewMessage value) { UnarchiveOnNewMessage value) {
update(archiveAndMuteCurrent(), value); update(
archiveAndMuteCurrent(),
value,
hideReadTimeCurrent(),
newRequirePremiumCurrent());
} }
void GlobalPrivacy::update( void GlobalPrivacy::update(
bool archiveAndMute, bool archiveAndMute,
UnarchiveOnNewMessage unarchiveOnNewMessage) { UnarchiveOnNewMessage unarchiveOnNewMessage,
bool hideReadTime,
bool newRequirePremium) {
using Flag = MTPDglobalPrivacySettings::Flag; using Flag = MTPDglobalPrivacySettings::Flag;
_api.request(_requestId).cancel(); _api.request(_requestId).cancel();
@ -108,17 +151,26 @@ void GlobalPrivacy::update(
: Flag()) : Flag())
| (unarchiveOnNewMessage != UnarchiveOnNewMessage::AnyUnmuted | (unarchiveOnNewMessage != UnarchiveOnNewMessage::AnyUnmuted
? Flag::f_keep_archived_folders ? Flag::f_keep_archived_folders
: Flag())
| (hideReadTime ? Flag::f_hide_read_marks : Flag())
| ((newRequirePremium && _session->premium())
? Flag::f_new_noncontact_peers_require_premium
: Flag()); : Flag());
_requestId = _api.request(MTPaccount_SetGlobalPrivacySettings( _requestId = _api.request(MTPaccount_SetGlobalPrivacySettings(
MTP_globalPrivacySettings(MTP_flags(flags)) MTP_globalPrivacySettings(MTP_flags(flags))
)).done([=](const MTPGlobalPrivacySettings &result) { )).done([=](const MTPGlobalPrivacySettings &result) {
_requestId = 0; _requestId = 0;
apply(result); apply(result);
}).fail([=] { }).fail([=](const MTP::Error &error) {
_requestId = 0; _requestId = 0;
if (error.type() == u"PREMIUM_ACCOUNT_REQUIRED"_q) {
update(archiveAndMute, unarchiveOnNewMessage, hideReadTime, {});
}
}).send(); }).send();
_archiveAndMute = archiveAndMute; _archiveAndMute = archiveAndMute;
_unarchiveOnNewMessage = unarchiveOnNewMessage; _unarchiveOnNewMessage = unarchiveOnNewMessage;
_hideReadTime = hideReadTime;
_newRequirePremium = newRequirePremium;
} }
void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) { void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) {
@ -129,6 +181,8 @@ void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) {
: data.is_keep_archived_folders() : data.is_keep_archived_folders()
? UnarchiveOnNewMessage::NotInFoldersUnmuted ? UnarchiveOnNewMessage::NotInFoldersUnmuted
: UnarchiveOnNewMessage::AnyUnmuted; : UnarchiveOnNewMessage::AnyUnmuted;
_hideReadTime = data.is_hide_read_marks();
_newRequirePremium = data.is_new_noncontact_peers_require_premium();
}); });
} }

View file

@ -41,12 +41,22 @@ public:
[[nodiscard]] rpl::producer<> suggestArchiveAndMute() const; [[nodiscard]] rpl::producer<> suggestArchiveAndMute() const;
void dismissArchiveAndMuteSuggestion(); void dismissArchiveAndMuteSuggestion();
void updateHideReadTime(bool hide);
[[nodiscard]] bool hideReadTimeCurrent() const;
[[nodiscard]] rpl::producer<bool> hideReadTime() const;
void updateNewRequirePremium(bool value);
[[nodiscard]] bool newRequirePremiumCurrent() const;
[[nodiscard]] rpl::producer<bool> newRequirePremium() const;
private: private:
void apply(const MTPGlobalPrivacySettings &data); void apply(const MTPGlobalPrivacySettings &data);
void update( void update(
bool archiveAndMute, bool archiveAndMute,
UnarchiveOnNewMessage unarchiveOnNewMessage); UnarchiveOnNewMessage unarchiveOnNewMessage,
bool hideReadTime,
bool newRequirePremium);
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
MTP::Sender _api; MTP::Sender _api;
@ -55,6 +65,8 @@ private:
rpl::variable<UnarchiveOnNewMessage> _unarchiveOnNewMessage rpl::variable<UnarchiveOnNewMessage> _unarchiveOnNewMessage
= UnarchiveOnNewMessage::None; = UnarchiveOnNewMessage::None;
rpl::variable<bool> _showArchiveAndMute = false; rpl::variable<bool> _showArchiveAndMute = false;
rpl::variable<bool> _hideReadTime = false;
rpl::variable<bool> _newRequirePremium = false;
std::vector<Fn<void()>> _callbacks; std::vector<Fn<void()>> _callbacks;
}; };

View file

@ -162,7 +162,7 @@ bool SendProgressManager::skipRequest(const Key &key) const {
} }
const auto recently = base::unixtime::now() - kSendTypingsToOfflineFor; const auto recently = base::unixtime::now() - kSendTypingsToOfflineFor;
const auto online = user->onlineTill; const auto online = user->onlineTill;
if (online == -2) { // last seen recently if (online == kOnlineRecently) {
return false; return false;
} else if (online < 0) { } else if (online < 0) {
return (-online < recently); return (-online < recently);

View file

@ -944,7 +944,8 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
} }
const auto self = session().user(); 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( session().changes().peerUpdated(
self, self,
Data::PeerUpdate::Flag::OnlineStatus); Data::PeerUpdate::Flag::OnlineStatus);
@ -1850,23 +1851,16 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateUserStatus: { case mtpc_updateUserStatus: {
auto &d = update.c_updateUserStatus(); auto &d = update.c_updateUserStatus();
if (auto user = session().data().userLoaded(d.vuser_id())) { if (const auto user = session().data().userLoaded(d.vuser_id())) {
switch (d.vstatus().type()) { const auto value = OnlineTillFromMTP(
case mtpc_userStatusEmpty: user->onlineTill = 0; break; d.vstatus(),
case mtpc_userStatusRecently: user->onlineTill);
if (user->onlineTill > -10) { // don't modify pseudo-online if (user->onlineTill != value) {
user->onlineTill = -2; session().changes().peerUpdated(
} user,
break; Data::PeerUpdate::Flag::OnlineStatus);
case mtpc_userStatusLastWeek: user->onlineTill = -3; break; session().data().maybeStopWatchForOffline(user);
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;
} }
session().changes().peerUpdated(
user,
Data::PeerUpdate::Flag::OnlineStatus);
session().data().maybeStopWatchForOffline(user);
} }
if (UserId(d.vuser_id()) == session().userId()) { if (UserId(d.vuser_id()) == session().userId()) {
if (d.vstatus().type() == mtpc_userStatusOffline if (d.vstatus().type() == mtpc_userStatusOffline

View file

@ -1923,21 +1923,18 @@ void ApiWrap::saveDraftToCloudDelayed(not_null<Data::Thread*> thread) {
void ApiWrap::updatePrivacyLastSeens() { void ApiWrap::updatePrivacyLastSeens() {
const auto now = base::unixtime::now(); const auto now = base::unixtime::now();
_session->data().enumerateUsers([&](UserData *user) { _session->data().enumerateUsers([&](UserData *user) {
if (user->isSelf() || !user->isLoaded()) { if (user->isSelf() || !user->isLoaded() || user->onlineTill <= 0) {
return;
}
if (user->onlineTill <= 0) {
return; return;
} }
if (user->onlineTill + 3 * 86400 >= now) { if (user->onlineTill + 3 * 86400 >= now) {
user->onlineTill = -2; // recently user->onlineTill = kOnlineRecently;
} else if (user->onlineTill + 7 * 86400 >= now) { } else if (user->onlineTill + 7 * 86400 >= now) {
user->onlineTill = -3; // last week user->onlineTill = kOnlineLastWeek;
} else if (user->onlineTill + 30 * 86400 >= now) { } else if (user->onlineTill + 30 * 86400 >= now) {
user->onlineTill = -4; // last month user->onlineTill = kOnlineLastMonth;
} else { } else {
user->onlineTill = 0; user->onlineTill = kOnlineEmpty;
} }
session().changes().peerUpdated( session().changes().peerUpdated(
user, user,
@ -1955,12 +1952,11 @@ void ApiWrap::updatePrivacyLastSeens() {
Assert(item.type() == mtpc_contactStatus); Assert(item.type() == mtpc_contactStatus);
auto &data = item.c_contactStatus(); auto &data = item.c_contactStatus();
if (auto user = _session->data().userLoaded(data.vuser_id())) { if (auto user = _session->data().userLoaded(data.vuser_id())) {
auto oldOnlineTill = user->onlineTill; auto value = OnlineTillFromMTP(
auto newOnlineTill = OnlineTillFromStatus(
data.vstatus(), data.vstatus(),
oldOnlineTill); user->onlineTill);
if (oldOnlineTill != newOnlineTill) { if (user->onlineTill != value) {
user->onlineTill = newOnlineTill; user->onlineTill = value;
session().changes().peerUpdated( session().changes().peerUpdated(
user, user,
Data::PeerUpdate::Flag::OnlineStatus); Data::PeerUpdate::Flag::OnlineStatus);
@ -1973,22 +1969,6 @@ void ApiWrap::updatePrivacyLastSeens() {
}).send(); }).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<PeerData*> peer, bool revoke) { void ApiWrap::clearHistory(not_null<PeerData*> peer, bool revoke) {
deleteHistory(peer, true, revoke); deleteHistory(peer, true, revoke);
} }

View file

@ -258,10 +258,6 @@ public:
void updateNotifySettingsDelayed(Data::DefaultNotify type); void updateNotifySettingsDelayed(Data::DefaultNotify type);
void saveDraftToCloudDelayed(not_null<Data::Thread*> thread); void saveDraftToCloudDelayed(not_null<Data::Thread*> thread);
static int OnlineTillFromStatus(
const MTPUserStatus &status,
int currentOnlineTill);
void clearHistory(not_null<PeerData*> peer, bool revoke); void clearHistory(not_null<PeerData*> peer, bool revoke);
void deleteConversation(not_null<PeerData*> peer, bool revoke); void deleteConversation(not_null<PeerData*> peer, bool revoke);

View file

@ -67,13 +67,13 @@ std::optional<QString> OnlineTextSpecial(not_null<UserData*> user) {
std::optional<QString> OnlineTextCommon(TimeId online, TimeId now) { std::optional<QString> OnlineTextCommon(TimeId online, TimeId now) {
if (online <= 0) { if (online <= 0) {
switch (online) { switch (online) {
case 0: case kOnlineEmpty: return tr::lng_status_offline(tr::now);
case -1: return tr::lng_status_offline(tr::now); case kOnlineRecently: return tr::lng_status_recently(tr::now);
case -2: return tr::lng_status_recently(tr::now); case kOnlineLastWeek: return tr::lng_status_last_week(tr::now);
case -3: return tr::lng_status_last_week(tr::now); case kOnlineLastMonth: return tr::lng_status_last_month(tr::now);
case -4: 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_online(tr::now)
: tr::lng_status_recently(tr::now); : tr::lng_status_recently(tr::now);
} else if (online > now) { } else if (online > now) {

View file

@ -519,6 +519,8 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
| Flag::BotInlineGeo | Flag::BotInlineGeo
| Flag::Premium | Flag::Premium
| Flag::Support | Flag::Support
| Flag::SomeRequirePremiumToWrite
| Flag::RequirePremiumToWriteKnown
| (!minimal | (!minimal
? Flag::Contact ? Flag::Contact
| Flag::MutualContact | Flag::MutualContact
@ -539,10 +541,20 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
| (data.is_bot_inline_geo() ? Flag::BotInlineGeo : Flag()) | (data.is_bot_inline_geo() ? Flag::BotInlineGeo : Flag())
| (data.is_premium() ? Flag::Premium : Flag()) | (data.is_premium() ? Flag::Premium : Flag())
| (data.is_support() ? Flag::Support : Flag()) | (data.is_support() ? Flag::Support : Flag())
| (data.is_contact_require_premium()
? (Flag::SomeRequirePremiumToWrite
| (result->someRequirePremiumToWrite()
? (result->requirePremiumToWriteKnown()
? Flag::RequirePremiumToWriteKnown
: Flag())
: Flag()))
: Flag())
| (!minimal | (!minimal
? (data.is_contact() ? Flag::Contact : Flag()) ? (data.is_contact() ? Flag::Contact : Flag())
| (data.is_mutual_contact() ? Flag::MutualContact : 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()) | (data.is_stories_hidden() ? Flag::StoriesHidden : Flag())
: Flag()); : Flag());
result->setFlags((result->flags() & ~flagsMask) | flagsSet); result->setFlags((result->flags() & ~flagsMask) | flagsSet);
@ -718,12 +730,9 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
} }
if (status && !minimal) { if (status && !minimal) {
const auto oldOnlineTill = result->onlineTill; const auto value = OnlineTillFromMTP(*status, result->onlineTill);
const auto newOnlineTill = ApiWrap::OnlineTillFromStatus( if (result->onlineTill != value) {
*status, result->onlineTill = value;
oldOnlineTill);
if (oldOnlineTill != newOnlineTill) {
result->onlineTill = newOnlineTill;
flags |= UpdateFlag::OnlineStatus; flags |= UpdateFlag::OnlineStatus;
session().data().maybeStopWatchForOffline(result); session().data().maybeStopWatchForOffline(result);
} }

View file

@ -34,6 +34,40 @@ using UpdateFlag = Data::PeerUpdate::Flag;
BotInfo::BotInfo() = default; 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<Data::Session*> owner, PeerId id) UserData::UserData(not_null<Data::Session*> owner, PeerId id)
: PeerData(owner, id) : PeerData(owner, id)
, _flags((id == owner->session().userPeerId()) ? Flag::Self : Flag(0)) { , _flags((id == owner->session().userPeerId()) ? Flag::Self : Flag(0)) {
@ -351,6 +385,22 @@ bool UserData::hasStoriesHidden() const {
return (flags() & UserDataFlag::StoriesHidden); 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 { bool UserData::canAddContact() const {
return canShareThisContact() && !isContact(); return canShareThisContact() && !isContact();
} }
@ -453,15 +503,25 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
| Flag::PhoneCallsPrivate | Flag::PhoneCallsPrivate
| Flag::CanReceiveGifts | Flag::CanReceiveGifts
| Flag::CanPinMessages | Flag::CanPinMessages
| Flag::VoiceMessagesForbidden; | Flag::VoiceMessagesForbidden
| Flag::ReadDatesPrivate
| Flag::RequirePremiumToWriteKnown
| Flag::MeRequiresPremiumToWrite;
user->setFlags((user->flags() & ~mask) 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()) | (update.is_phone_calls_available() ? Flag::HasPhoneCalls : Flag())
| (canReceiveGifts ? Flag::CanReceiveGifts : Flag()) | (canReceiveGifts ? Flag::CanReceiveGifts : Flag())
| (update.is_can_pin_message() ? Flag::CanPinMessages : 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() | (update.is_voice_messages_forbidden()
? Flag::VoiceMessagesForbidden ? Flag::VoiceMessagesForbidden
: Flag())
| (update.is_read_dates_private() ? Flag::ReadDatesPrivate : Flag())
| Flag::RequirePremiumToWriteKnown
| (update.is_contact_require_premium()
? Flag::MeRequiresPremiumToWrite
: Flag())); : Flag()));
user->setIsBlocked(update.is_blocked()); user->setIsBlocked(update.is_blocked());
user->setCallsStatus(update.is_phone_calls_private() user->setCallsStatus(update.is_phone_calls_private()

View file

@ -65,10 +65,27 @@ enum class UserDataFlag {
StoriesHidden = (1 << 18), StoriesHidden = (1 << 18),
HasActiveStories = (1 << 19), HasActiveStories = (1 << 19),
HasUnreadStories = (1 << 20), HasUnreadStories = (1 << 20),
MeRequiresPremiumToWrite = (1 << 21),
SomeRequirePremiumToWrite = (1 << 22),
RequirePremiumToWriteKnown = (1 << 23),
ReadDatesPrivate = (1 << 24),
}; };
inline constexpr bool is_flag_type(UserDataFlag) { return true; }; inline constexpr bool is_flag_type(UserDataFlag) { return true; };
using UserDataFlags = base::flags<UserDataFlag>; using UserDataFlags = base::flags<UserDataFlag>;
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 { class UserData final : public PeerData {
public: public:
using Flag = UserDataFlag; using Flag = UserDataFlag;
@ -119,6 +136,10 @@ public:
[[nodiscard]] bool applyMinPhoto() const; [[nodiscard]] bool applyMinPhoto() const;
[[nodiscard]] bool hasPersonalPhoto() const; [[nodiscard]] bool hasPersonalPhoto() const;
[[nodiscard]] bool hasStoriesHidden() 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 canShareThisContact() const;
[[nodiscard]] bool canAddContact() const; [[nodiscard]] bool canAddContact() const;

View file

@ -82,7 +82,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType; storage.fileWebp#1081464c = storage.FileType;
userEmpty#d3bc4b7a id:long = User; 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<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> 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<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; 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; 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; userStatusRecently#e26f42f1 = UserStatus;
userStatusLastWeek#7bf09fc = UserStatus; userStatusLastWeek#7bf09fc = UserStatus;
userStatusLastMonth#77ebc742 = UserStatus; userStatusLastMonth#77ebc742 = UserStatus;
userStatusHidden#cf7d64b1 = UserStatus;
chatEmpty#29562865 id:long = Chat; 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; 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; inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = 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<PremiumGiftOption> 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<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact; 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<StatsGroupTopPoster> top_admins:Vector<StatsGroupTopAdmin> top_inviters:Vector<StatsGroupTopInviter> users:Vector<User> = stats.MegagroupStats; 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<StatsGroupTopPoster> top_admins:Vector<StatsGroupTopAdmin> top_inviters:Vector<StatsGroupTopInviter> users:Vector<User> = 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<string> patterns:flags.1?Vector<string> = help.CountryCode; help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector<string> patterns:flags.1?Vector<string> = help.CountryCode;
@ -1649,6 +1650,8 @@ savedReactionTag#cb6ff828 flags:# reaction:Reaction title:flags.0?string count:i
messages.savedReactionTagsNotModified#889b59ef = messages.SavedReactionTags; messages.savedReactionTagsNotModified#889b59ef = messages.SavedReactionTags;
messages.savedReactionTags#3259950a tags:Vector<SavedReactionTag> hash:long = messages.SavedReactionTags; messages.savedReactionTags#3259950a tags:Vector<SavedReactionTag> hash:long = messages.SavedReactionTags;
outboxReadDate#3bb842ac date:int = OutboxReadDate;
---functions--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; 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<InputUser> = Vector<User>; users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull; users.getFullUser#b60f5918 id:InputUser = users.UserFull;
users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool; users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;
//users.getIsPremiumRequiredToContact#75c9db9c id:Vector<InputUser> = Vector<Bool>;
contacts.getContactIDs#7adc669d hash:long = Vector<int>; contacts.getContactIDs#7adc669d hash:long = Vector<int>;
contacts.getStatuses#c4a353ee = Vector<ContactStatus>; contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
@ -2002,6 +2006,7 @@ messages.reorderPinnedSavedDialogs#8b716587 flags:# force:flags.0?true order:Vec
messages.getSavedReactionTags#761ddacf hash:long = messages.SavedReactionTags; messages.getSavedReactionTags#761ddacf hash:long = messages.SavedReactionTags;
messages.updateSavedReactionTag#60297dec flags:# reaction:Reaction title:flags.0?string = Bool; messages.updateSavedReactionTag#60297dec flags:# reaction:Reaction title:flags.0?string = Bool;
messages.getDefaultTagReactions#bdf93428 hash:long = messages.Reactions; messages.getDefaultTagReactions#bdf93428 hash:long = messages.Reactions;
messages.getOutboxReadDate#8c4bfe5d peer:InputPeer msg_id:int = OutboxReadDate;
updates.getState#edd4882a = updates.State; 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; 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<int> peer:InputPeer = p
premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus; premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus;
premium.getUserBoosts#39854d1f peer:InputPeer user_id:InputUser = premium.BoostsList; premium.getUserBoosts#39854d1f peer:InputPeer user_id:InputUser = premium.BoostsList;
// LAYER 171 // LAYER 172

View file

@ -141,7 +141,7 @@ TimeId CalculateOnlineTill(not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) { if (const auto user = peer->asUser()) {
if (!user->isServiceUser() && !user->isBot()) { if (!user->isServiceUser() && !user->isBot()) {
const auto onlineTill = user->onlineTill; const auto onlineTill = user->onlineTill;
return (onlineTill <= -5) return IsRecentOnlineValue(onlineTill)
? -onlineTill ? -onlineTill
: (onlineTill <= 0) : (onlineTill <= 0)
? 0 ? 0

View file

@ -15,12 +15,14 @@ settingsButton: SettingsButton(infoProfileButton) {
padding: margins(60px, 10px, 22px, 10px); padding: margins(60px, 10px, 22px, 10px);
iconLeft: 20px; iconLeft: 20px;
} }
settingsButtonNoIcon: SettingsButton(settingsButton) {
padding: margins(22px, 10px, 22px, 8px);
}
settingsButtonLight: SettingsButton(settingsButton) { settingsButtonLight: SettingsButton(settingsButton) {
textFg: lightButtonFg; textFg: lightButtonFg;
textFgOver: lightButtonFgOver; textFgOver: lightButtonFgOver;
} }
settingsButtonNoIcon: SettingsButton(settingsButton) { settingsButtonLightNoIcon: SettingsButton(settingsButtonLight, settingsButtonNoIcon) {
padding: margins(22px, 10px, 22px, 8px);
} }
settingsButtonNoIconLocked : SettingsButton(settingsButtonNoIcon) { settingsButtonNoIconLocked : SettingsButton(settingsButtonNoIcon) {
toggle: Toggle(infoProfileToggle) { toggle: Toggle(infoProfileToggle) {

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "settings/settings_privacy_controllers.h" #include "settings/settings_privacy_controllers.h"
#include "api/api_global_privacy.h"
#include "api/api_peer_photo.h" #include "api/api_peer_photo.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "base/call_delayed.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 "history/view/history_view_message.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "settings/settings_premium.h"
#include "settings/settings_privacy_security.h" #include "settings/settings_privacy_security.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "ui/cached_round_corners.h" #include "ui/cached_round_corners.h"
@ -665,16 +667,64 @@ auto LastSeenPrivacyController::exceptionsDescription() const
return tr::lng_edit_privacy_lastseen_exceptions(); return tr::lng_edit_privacy_lastseen_exceptions();
} }
object_ptr<Ui::RpWidget> LastSeenPrivacyController::setupBelowWidget(
not_null<Window::SessionController*> controller,
not_null<QWidget*> parent) {
auto result = object_ptr<Ui::VerticalLayout>(parent);
const auto content = result.data();
Ui::AddSkip(content);
const auto privacy = &controller->session().api().globalPrivacy();
content->add(object_ptr<Ui::SettingsButton>(
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<Ui::SettingsButton>(
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( void LastSeenPrivacyController::confirmSave(
bool someAreDisallowed, bool someAreDisallowed,
Fn<void()> saveCallback) { Fn<void()> 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()) { if (someAreDisallowed && !Core::App().settings().lastSeenWarningSeen()) {
auto callback = [ auto callback = [
=, =,
saveCallback = std::move(saveCallback) save = std::move(save)
](Fn<void()> &&close) { ](Fn<void()> &&close) {
close(); close();
saveCallback(); save();
Core::App().settings().setLastSeenWarningSeen(true); Core::App().settings().setLastSeenWarningSeen(true);
Core::App().saveSettingsDelayed(); Core::App().saveSettingsDelayed();
}; };
@ -685,7 +735,7 @@ void LastSeenPrivacyController::confirmSave(
}); });
Ui::show(std::move(box), Ui::LayerOption::KeepOther); Ui::show(std::move(box), Ui::LayerOption::KeepOther);
} else { } else {
saveCallback(); save();
} }
} }

View file

@ -109,12 +109,17 @@ public:
Exception exception) const override; Exception exception) const override;
rpl::producer<QString> exceptionsDescription() const override; rpl::producer<QString> exceptionsDescription() const override;
object_ptr<Ui::RpWidget> setupBelowWidget(
not_null<Window::SessionController*> controller,
not_null<QWidget*> parent) override;
void confirmSave( void confirmSave(
bool someAreDisallowed, bool someAreDisallowed,
Fn<void()> saveCallback) override; Fn<void()> saveCallback) override;
private: private:
const not_null<::Main::Session*> _session; const not_null<::Main::Session*> _session;
bool _hideReadTime = false;
}; };