/* 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 "info/profile/info_profile_status_label.h" #include "data/data_changes.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_community.h" #include "data/data_peer.h" #include "data/data_peer_values.h" #include "data/data_session.h" #include "data/data_user.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/widgets/labels.h" #include "ui/text/text_utilities.h" #include "ui/basic_click_handlers.h" #include "base/unixtime.h" #include "styles/style_info.h" namespace Info::Profile { namespace { [[nodiscard]] auto MembersStatusText(int count) { return tr::lng_chat_status_members(tr::now, lt_count_decimal, count); }; [[nodiscard]] auto OnlineStatusText(int count) { return tr::lng_chat_status_online(tr::now, lt_count_decimal, count); }; [[nodiscard]] auto ChatStatusText( int fullCount, int onlineCount, bool isGroup) { if (onlineCount > 1 && onlineCount <= fullCount) { return tr::lng_chat_status_members_online( tr::now, lt_members_count, MembersStatusText(fullCount), lt_online_count, OnlineStatusText(onlineCount)); } else if (fullCount > 0) { return isGroup ? tr::lng_chat_status_members( tr::now, lt_count_decimal, fullCount) : tr::lng_chat_status_subscribers( tr::now, lt_count_decimal, fullCount); } return isGroup ? tr::lng_group_status(tr::now) : tr::lng_channel_status(tr::now); }; [[nodiscard]] auto ChannelTypeText(not_null channel) { const auto isPublic = channel->isPublic(); const auto isMegagroup = channel->isMegagroup(); return (isPublic ? (isMegagroup ? tr::lng_create_public_group_title(tr::now) : tr::lng_create_public_channel_title(tr::now)) : (isMegagroup ? tr::lng_create_private_group_title(tr::now) : tr::lng_create_private_channel_title(tr::now))).toLower(); }; [[nodiscard]] bool PeerHiddenInCommunity(not_null peer) { const auto communityId = Data::PeerLinkedCommunityId(peer); if (!communityId) { return false; } const auto community = peer->owner().channel(communityId); const auto info = community->communityInfo(); return info && info->isHidden(peer); } [[nodiscard]] TextWithEntities MaybeHiddenPrefixed( TextWithEntities body, bool hidden) { if (!hidden) { return body; } using namespace Ui::Text; return IconEmoji(&st::infoStatusHiddenIcon) .append(' ') .append(Link(tr::lng_community_hidden_status(tr::now), 2)) .append(QString::fromUtf8(" \xC2\xB7 ")) .append(std::move(body)); } } // namespace StatusLabel::StatusLabel( not_null label, not_null peer) : _label(label) , _peer(peer) , _refreshTimer([=] { refresh(); }) { if (const auto communityId = Data::PeerLinkedCommunityId(_peer)) { const auto community = _peer->owner().channel(communityId); community->session().changes().peerFlagsValue( community, Data::PeerUpdate::Flag::FullInfo ) | rpl::on_next([=] { refresh(); }, _lifetime); } } void StatusLabel::setOnlineCount(int count) { _onlineCount = count; refresh(); } void StatusLabel::refresh() { const auto hidden = PeerHiddenInCommunity(_peer); auto hasMembersLink = [&] { if (auto megagroup = _peer->asMegagroup()) { return megagroup->canViewMembers(); } return false; }(); auto statusText = [&]() -> TextWithEntities { using namespace Ui::Text; auto currentTime = base::unixtime::now(); if (auto user = _peer->asUser()) { const auto result = Data::OnlineTextFull(user, currentTime); const auto showOnline = Data::OnlineTextActive( user, currentTime); const auto updateIn = Data::OnlineChangeTimeout( user, currentTime); if (showOnline) { _refreshTimer.callOnce(updateIn); } return MaybeHiddenPrefixed( (showOnline && _colorized) ? Ui::Text::Colorized(result) : TextWithEntities{ .text = result }, hidden); } else if (auto chat = _peer->asChat()) { if (!chat->amIn()) { return tr::lng_chat_status_unaccessible( {}, WithEntities); } const auto onlineCount = _onlineCount; const auto fullCount = std::max( chat->count, int(chat->participants.size())); return MaybeHiddenPrefixed( { .text = ChatStatusText(fullCount, onlineCount, true) }, hidden); } else if (auto broadcast = _peer->monoforumBroadcast()) { if (!broadcast->membersCountKnown()) { return TextWithEntities{ .text = ChannelTypeText(broadcast) }; } auto result = ChatStatusText( broadcast->membersCount(), 0, false); return TextWithEntities{ .text = result }; } else if (auto channel = _peer->asChannel()) { if (!channel->membersCountKnown()) { auto result = ChannelTypeText(channel); return MaybeHiddenPrefixed( hasMembersLink ? tr::link(result) : TextWithEntities{ .text = result }, hidden); } const auto onlineCount = _onlineCount; const auto fullCount = channel->membersCount(); auto result = ChatStatusText( fullCount, onlineCount, channel->isMegagroup()); return MaybeHiddenPrefixed( hasMembersLink ? tr::link(result) : TextWithEntities{ .text = result }, hidden); } return tr::lng_chat_status_unaccessible(tr::now, WithEntities); }(); _label->setMarkedText(statusText); if (hasMembersLink && _membersLinkCallback) { _label->setLink( 1, std::make_shared(_membersLinkCallback)); } if (hidden && _hiddenLinkCallback) { _label->setLink( 2, std::make_shared(_hiddenLinkCallback)); } } void StatusLabel::setHiddenLinkCallback(Fn callback) { _hiddenLinkCallback = std::move(callback); } Fn StatusLabel::hiddenLinkCallback() const { return _hiddenLinkCallback; } void StatusLabel::setMembersLinkCallback(Fn callback) { _membersLinkCallback = std::move(callback); } Fn StatusLabel::membersLinkCallback() const { return _membersLinkCallback; } void StatusLabel::setColorized(bool enabled) { _colorized = enabled; refresh(); } } // namespace Info::Profile