From d59ab17ac594804cca6aaf1ed41bcffd46bac2a6 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 8 Oct 2025 19:12:45 +0300 Subject: [PATCH] Added last seen button and rating to status in profile top bar. --- Telegram/SourceFiles/info/info.style | 1 + .../info/profile/info_profile_top_bar.cpp | 135 +++++++++++++++++- .../info/profile/info_profile_top_bar.h | 9 ++ 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 3ef3e8f3b6..abb59c3041 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -252,6 +252,7 @@ infoLayerProfileTopBarTitleTop: 150px; infoLayerProfileTopBarStatusTop: 170px; infoLayerProfileTopBarPhotoTop: 30px; infoLayerProfileTopBarPhotoSize: 104px; +infoLayerProfileTopBarLastSeenSkip: 8px; infoLayerTopMinimal: 20px; infoLayerTopMaximal: 40px; diff --git a/Telegram/SourceFiles/info/profile/info_profile_top_bar.cpp b/Telegram/SourceFiles/info/profile/info_profile_top_bar.cpp index 010c9cf997..fb8d51f0b7 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_top_bar.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_top_bar.cpp @@ -7,10 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "info/profile/info_profile_top_bar.h" +#include "api/api_user_privacy.h" +#include "apiwrap.h" +#include "base/unixtime.h" #include "calls/calls_instance.h" #include "core/application.h" #include "core/shortcuts.h" #include "data/data_changes.h" +#include "data/data_peer_values.h" #include "data/data_peer.h" #include "data/data_session.h" #include "data/data_user.h" @@ -19,11 +23,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_memento.h" #include "info/profile/info_profile_status_label.h" #include "info/profile/info_profile_values.h" +#include "lang/lang_keys.h" #include "main/main_session.h" +#include "settings/settings_premium.h" +#include "ui/boxes/show_or_premium_box.h" +#include "ui/controls/stars_rating.h" #include "ui/effects/animations.h" +#include "ui/layers/generic_box.h" #include "ui/painter.h" #include "ui/rect.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/widgets/menu/menu_add_action_callback_factory.h" #include "ui/widgets/popup_menu.h" @@ -36,6 +46,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_layers.h" #include "styles/style_settings.h" +#include + namespace Info::Profile { TopBar::TopBar( @@ -45,12 +57,39 @@ TopBar::TopBar( , _peer(descriptor.controller->key().peer()) , _st(st::infoTopBar) , _title(this, Info::Profile::NameValue(_peer), _st.title) +, _starsRating(_peer->isUser() + ? std::make_unique( + this, + descriptor.controller->uiShow(), + _peer->isSelf() ? QString() : _peer->shortName(), + Data::StarsRatingValue(_peer), + (_peer->isSelf() + ? [=] { return _peer->owner().pendingStarsRating(); } + : Fn())) + : nullptr) , _status(this, QString(), _st.subtitle) , _statusLabel( - std::make_unique(_status.data(), _peer, rpl::single(0))) { + std::make_unique(_status.data(), _peer, rpl::single(0))) +, _showLastSeen( + this, + tr::lng_status_lastseen_when(), + st::infoProfileCover.showLastSeen) { QWidget::setMinimumHeight(st::infoLayerTopBarHeight); QWidget::setMaximumHeight(st::infoLayerProfileTopBarHeightMax); + if (!_peer->isMegagroup()) { + _status->setAttribute(Qt::WA_TransparentForMouseEvents); + if (const auto rating = _starsRating.get()) { + _statusShift = rating->widthValue(); + _statusShift.changes() | rpl::start_with_next([=] { + updateLabelsPosition(); + }, _status->lifetime()); + rating->raise(); + } + } + + setupShowLastSeen(descriptor.controller); + _peer->session().changes().peerFlagsValue( _peer, Data::PeerUpdate::Flag::OnlineStatus @@ -150,8 +189,27 @@ void TopBar::updateLabelsPosition() { (width() - _status->width()) / 2, progressCurrent); + if (const auto rating = _starsRating.get()) { + rating->moveTo(statusLeft - _statusShift.current(), statusTop); + rating->setOpacity(progressCurrent); + } + _title->moveToLeft(titleLeft, titleTop); _status->moveToLeft(statusLeft, statusTop); + + if (!_showLastSeen->isHidden()) { + _showLastSeen->moveToLeft( + statusLeft + + _status->textMaxWidth() + + st::infoLayerProfileTopBarLastSeenSkip, + statusTop); + if (_showLastSeenOpacity) { + _showLastSeenOpacity->setOpacity(progressCurrent); + } + _showLastSeen->setAttribute( + Qt::WA_TransparentForMouseEvents, + !progressCurrent); + } } void TopBar::resizeEvent(QResizeEvent *e) { @@ -376,4 +434,79 @@ void TopBar::addProfileCallsButton( } } +void TopBar::setupShowLastSeen(not_null controller) { + const auto user = _peer->asUser(); + if (!user + || user->isSelf() + || user->isBot() + || user->isServiceUser() + || !user->session().premiumPossible()) { + _showLastSeen->hide(); + return; + } + + if (user->session().premium()) { + if (user->lastseen().isHiddenByMe()) { + user->updateFullForced(); + } + _showLastSeen->hide(); + return; + } + + rpl::combine( + user->session().changes().peerFlagsValue( + user, + Data::PeerUpdate::Flag::OnlineStatus), + Data::AmPremiumValue(&user->session()) + ) | rpl::start_with_next([=](auto, bool premium) { + const auto wasShown = !_showLastSeen->isHidden(); + const auto hiddenByMe = user->lastseen().isHiddenByMe(); + const auto shown = hiddenByMe + && !user->lastseen().isOnline(base::unixtime::now()) + && !premium + && user->session().premiumPossible(); + _showLastSeen->setVisible(shown); + if (wasShown && premium && hiddenByMe) { + user->updateFullForced(); + } + }, _showLastSeen->lifetime()); + + controller->session().api().userPrivacy().value( + Api::UserPrivacy::Key::LastSeen + ) | rpl::filter([=](Api::UserPrivacy::Rule rule) { + return (rule.option == Api::UserPrivacy::Option::Everyone); + }) | rpl::start_with_next([=] { + if (user->lastseen().isHiddenByMe()) { + user->updateFullForced(); + } + }, _showLastSeen->lifetime()); + + _showLastSeenOpacity = Ui::CreateChild( + _showLastSeen.get()); + _showLastSeen->setGraphicsEffect(_showLastSeenOpacity); + _showLastSeenOpacity->setOpacity(0.); + + using TextTransform = Ui::RoundButton::TextTransform; + _showLastSeen->setTextTransform(TextTransform::NoTransform); + _showLastSeen->setFullRadius(true); + + _showLastSeen->setClickedCallback([=] { + const auto type = Ui::ShowOrPremium::LastSeen; + controller->parentController()->show(Box( + Ui::ShowOrPremiumBox, + type, + user->shortName(), + [=] { + controller->session().api().userPrivacy().save( + ::Api::UserPrivacy::Key::LastSeen, + {}); + }, + [=] { + ::Settings::ShowPremium( + controller->parentController(), + u"lastseen_hidden"_q); + })); + }); +} + } // namespace Info::Profile diff --git a/Telegram/SourceFiles/info/profile/info_profile_top_bar.h b/Telegram/SourceFiles/info/profile/info_profile_top_bar.h index 24af1f2570..e64262d97a 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_top_bar.h +++ b/Telegram/SourceFiles/info/profile/info_profile_top_bar.h @@ -17,10 +17,14 @@ namespace style { struct InfoTopBar; } //namespace style +class QGraphicsOpacityEffect; + namespace Ui { class FlatLabel; class IconButton; class PopupMenu; +class RoundButton; +class StarsRating; template class FadeWrap; } //namespace Ui @@ -71,13 +75,18 @@ private: void setupButtons( not_null controller, rpl::producer backToggles); + void setupShowLastSeen(not_null controller); const not_null _peer; const style::InfoTopBar &_st; object_ptr _title; + std::unique_ptr _starsRating; object_ptr _status; std::unique_ptr _statusLabel; + rpl::variable _statusShift = 0; + object_ptr _showLastSeen = { nullptr }; + QGraphicsOpacityEffect *_showLastSeenOpacity = nullptr; rpl::variable _onlineCount = 0; rpl::variable _progress = 0.;