Show emoji statuses in channels.

This commit is contained in:
John Preston 2023-12-16 22:43:35 +00:00
parent 6aaf841a73
commit 805a5d73b6
12 changed files with 81 additions and 67 deletions

View file

@ -104,19 +104,19 @@ rpl::producer<> EmojiStatuses::defaultUpdates() const {
} }
void EmojiStatuses::registerAutomaticClear( void EmojiStatuses::registerAutomaticClear(
not_null<UserData*> user, not_null<PeerData*> peer,
TimeId until) { TimeId until) {
if (!until) { if (!until) {
_clearing.remove(user); _clearing.remove(peer);
if (_clearing.empty()) { if (_clearing.empty()) {
_clearingTimer.cancel(); _clearingTimer.cancel();
} }
} else if (auto &already = _clearing[user]; already != until) { } else if (auto &already = _clearing[peer]; already != until) {
already = until; already = until;
const auto i = ranges::min_element(_clearing, {}, [](auto &&pair) { const auto i = ranges::min_element(_clearing, {}, [](auto &&pair) {
return pair.second; return pair.second;
}); });
if (i->first == user) { if (i->first == peer) {
const auto now = base::unixtime::now(); const auto now = base::unixtime::now();
if (now < until) { if (now < until) {
processClearingIn(until - now); processClearingIn(until - now);

View file

@ -51,7 +51,7 @@ public:
void set(DocumentId id, TimeId until = 0); void set(DocumentId id, TimeId until = 0);
[[nodiscard]] bool setting() const; [[nodiscard]] bool setting() const;
void registerAutomaticClear(not_null<UserData*> user, TimeId until); void registerAutomaticClear(not_null<PeerData*> peer, TimeId until);
using Groups = std::vector<Ui::EmojiGroup>; using Groups = std::vector<Ui::EmojiGroup>;
[[nodiscard]] rpl::producer<Groups> emojiGroupsValue() const; [[nodiscard]] rpl::producer<Groups> emojiGroupsValue() const;
@ -102,7 +102,7 @@ private:
mtpRequestId _sentRequestId = 0; mtpRequestId _sentRequestId = 0;
base::flat_map<not_null<UserData*>, TimeId> _clearing; base::flat_map<not_null<PeerData*>, TimeId> _clearing;
base::Timer _clearingTimer; base::Timer _clearingTimer;
GroupsType _emojiGroups; GroupsType _emojiGroups;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat_participant_status.h" #include "data/data_chat_participant_status.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_emoji_statuses.h"
#include "data/data_message_reaction_id.h" #include "data/data_message_reaction_id.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_folder.h" #include "data/data_folder.h"
@ -926,6 +927,24 @@ bool PeerData::changeBackgroundEmojiId(DocumentId id) {
_backgroundEmojiId = id; _backgroundEmojiId = id;
return true; return true;
} }
void PeerData::setEmojiStatus(const MTPEmojiStatus &status) {
const auto parsed = Data::ParseEmojiStatus(status);
setEmojiStatus(parsed.id, parsed.until);
}
void PeerData::setEmojiStatus(DocumentId emojiStatusId, TimeId until) {
if (_emojiStatusId != emojiStatusId) {
_emojiStatusId = emojiStatusId;
session().changes().peerUpdated(this, UpdateFlag::EmojiStatus);
}
owner().emojiStatuses().registerAutomaticClear(this, until);
}
DocumentId PeerData::emojiStatusId() const {
return _emojiStatusId;
}
bool PeerData::isSelf() const { bool PeerData::isSelf() const {
if (const auto user = asUser()) { if (const auto user = asUser()) {
return (user->flags() & UserDataFlag::Self); return (user->flags() & UserDataFlag::Self);

View file

@ -178,6 +178,10 @@ public:
[[nodiscard]] DocumentId backgroundEmojiId() const; [[nodiscard]] DocumentId backgroundEmojiId() const;
bool changeBackgroundEmojiId(DocumentId id); bool changeBackgroundEmojiId(DocumentId id);
void setEmojiStatus(const MTPEmojiStatus &status);
void setEmojiStatus(DocumentId emojiStatusId, TimeId until = 0);
[[nodiscard]] DocumentId emojiStatusId() const;
[[nodiscard]] bool isUser() const { [[nodiscard]] bool isUser() const {
return peerIsUser(id); return peerIsUser(id);
} }
@ -466,6 +470,7 @@ private:
base::flat_set<QString> _nameWords; // for filtering base::flat_set<QString> _nameWords; // for filtering
base::flat_set<QChar> _nameFirstLetters; base::flat_set<QChar> _nameFirstLetters;
DocumentId _emojiStatusId = 0;
uint64 _backgroundEmojiId = 0; uint64 _backgroundEmojiId = 0;
crl::time _lastFullUpdate = 0; crl::time _lastFullUpdate = 0;

View file

@ -854,6 +854,11 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
channel->setDefaultRestrictions(ChatRestrictions()); channel->setDefaultRestrictions(ChatRestrictions());
} }
if (const auto &status = data.vemoji_status()) {
channel->setEmojiStatus(*status);
} else {
channel->setEmojiStatus(0);
}
if (minimal) { if (minimal) {
if (channel->input.type() == mtpc_inputPeerEmpty if (channel->input.type() == mtpc_inputPeerEmpty
|| channel->inputChannel.type() == mtpc_inputChannelEmpty) { || channel->inputChannel.type() == mtpc_inputChannelEmpty) {

View file

@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_bot_command.h" #include "data/data_peer_bot_command.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_stories.h" #include "data/data_stories.h"
#include "data/data_emoji_statuses.h"
#include "data/data_wall_paper.h" #include "data/data_wall_paper.h"
#include "data/notify/data_notify_settings.h" #include "data/notify/data_notify_settings.h"
#include "history/history.h" #include "history/history.h"
@ -73,23 +72,6 @@ void UserData::setPhoto(const MTPUserProfilePhoto &photo) {
}); });
} }
void UserData::setEmojiStatus(const MTPEmojiStatus &status) {
const auto parsed = Data::ParseEmojiStatus(status);
setEmojiStatus(parsed.id, parsed.until);
}
void UserData::setEmojiStatus(DocumentId emojiStatusId, TimeId until) {
if (_emojiStatusId != emojiStatusId) {
_emojiStatusId = emojiStatusId;
session().changes().peerUpdated(this, UpdateFlag::EmojiStatus);
}
owner().emojiStatuses().registerAutomaticClear(this, until);
}
DocumentId UserData::emojiStatusId() const {
return _emojiStatusId;
}
auto UserData::unavailableReasons() const auto UserData::unavailableReasons() const
-> const std::vector<Data::UnavailableReason> & { -> const std::vector<Data::UnavailableReason> & {
return _unavailableReasons; return _unavailableReasons;

View file

@ -76,7 +76,6 @@ public:
UserData(not_null<Data::Session*> owner, PeerId id); UserData(not_null<Data::Session*> owner, PeerId id);
void setPhoto(const MTPUserProfilePhoto &photo); void setPhoto(const MTPUserProfilePhoto &photo);
void setEmojiStatus(const MTPEmojiStatus &status);
void setName( void setName(
const QString &newFirstName, const QString &newFirstName,
@ -85,9 +84,6 @@ public:
const QString &newUsername); const QString &newUsername);
void setUsernames(const Data::Usernames &newUsernames); void setUsernames(const Data::Usernames &newUsernames);
void setEmojiStatus(DocumentId emojiStatusId, TimeId until = 0);
[[nodiscard]] DocumentId emojiStatusId() const;
void setUsername(const QString &username); void setUsername(const QString &username);
void setPhone(const QString &newPhone); void setPhone(const QString &newPhone);
void setBotInfoVersion(int version); void setBotInfoVersion(int version);
@ -199,8 +195,6 @@ private:
static constexpr auto kInaccessibleAccessHashOld static constexpr auto kInaccessibleAccessHashOld
= 0xFFFFFFFFFFFFFFFFULL; = 0xFFFFFFFFFFFFFFFFULL;
DocumentId _emojiStatusId = 0;
}; };
namespace Data { namespace Data {

View file

@ -98,16 +98,15 @@ namespace {
[[nodiscard]] rpl::producer<TextWithEntities> PeerCustomStatus( [[nodiscard]] rpl::producer<TextWithEntities> PeerCustomStatus(
not_null<PeerData*> peer) { not_null<PeerData*> peer) {
const auto user = peer->asUser(); if (peer->isChat()) {
if (!user) {
return rpl::single(TextWithEntities()); return rpl::single(TextWithEntities());
} }
const auto owner = &user->owner(); const auto owner = &peer->owner();
return user->session().changes().peerFlagsValue( return peer->session().changes().peerFlagsValue(
user, peer,
Data::PeerUpdate::Flag::EmojiStatus Data::PeerUpdate::Flag::EmojiStatus
) | rpl::map([=] { ) | rpl::map([=] {
const auto id = user->emojiStatusId(); const auto id = peer->emojiStatusId();
return id return id
? ResolveIsCustom(owner, id) ? ResolveIsCustom(owner, id)
: rpl::single(TextWithEntities()); : rpl::single(TextWithEntities());

View file

@ -1410,13 +1410,12 @@ void Message::paintFromName(
const auto y = trect.top(); const auto y = trect.top();
auto color = nameFg; auto color = nameFg;
color.setAlpha(115); color.setAlpha(115);
const auto user = from->asUser(); const auto id = from ? from->emojiStatusId() : 0;
const auto id = user ? user->emojiStatusId() : 0;
if (_fromNameStatus->id != id) { if (_fromNameStatus->id != id) {
const auto that = const_cast<Message*>(this); const auto that = const_cast<Message*>(this);
_fromNameStatus->custom = id _fromNameStatus->custom = id
? std::make_unique<Ui::Text::LimitedLoopsEmoji>( ? std::make_unique<Ui::Text::LimitedLoopsEmoji>(
user->owner().customEmojiManager().create( history()->owner().customEmojiManager().create(
id, id,
[=] { that->customEmojiRepaint(); }), [=] { that->customEmojiRepaint(); }),
kPlayStatusLimit) kPlayStatusLimit)
@ -3017,7 +3016,8 @@ void Message::validateFromNameText(PeerData *from) const {
from->name(), from->name(),
Ui::NameTextOptions()); Ui::NameTextOptions());
} }
if (from->isPremium()) { if (from->isPremium()
|| (from->isChannel() && from != history()->peer)) {
if (!_fromNameStatus) { if (!_fromNameStatus) {
_fromNameStatus = std::make_unique<FromNameStatus>(); _fromNameStatus = std::make_unique<FromNameStatus>();
const auto size = st::emojiSize; const auto size = st::emojiSize;

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h" #include "data/stickers/data_custom_emoji.h"
#include "info/profile/info_profile_values.h" #include "info/profile/info_profile_values.h"
#include "info/profile/info_profile_emoji_status_panel.h" #include "info/profile/info_profile_emoji_status_panel.h"
@ -24,10 +25,16 @@ namespace {
[[nodiscard]] rpl::producer<Badge::Content> ContentForPeer( [[nodiscard]] rpl::producer<Badge::Content> ContentForPeer(
not_null<PeerData*> peer) { not_null<PeerData*> peer) {
const auto statusOnlyForPremium = peer->isUser();
return rpl::combine( return rpl::combine(
BadgeValue(peer), BadgeValue(peer),
EmojiStatusIdValue(peer) EmojiStatusIdValue(peer)
) | rpl::map([=](BadgeType badge, DocumentId emojiStatusId) { ) | rpl::map([=](BadgeType badge, DocumentId emojiStatusId) {
if (statusOnlyForPremium) {
emojiStatusId = 0;
} else if (emojiStatusId && badge == BadgeType::None) {
badge = BadgeType::Premium;
}
return Badge::Content{ badge, emojiStatusId }; return Badge::Content{ badge, emojiStatusId };
}); });
} }
@ -91,9 +98,6 @@ void Badge::setContent(Content content) {
if (!(_allowed & content.badge)) { if (!(_allowed & content.badge)) {
content.badge = BadgeType::None; content.badge = BadgeType::None;
} }
if (content.badge != BadgeType::Premium) {
content.emojiStatusId = 0;
}
if (_content == content) { if (_content == content) {
return; return;
} }

View file

@ -609,15 +609,14 @@ rpl::producer<BadgeType> BadgeValue(not_null<PeerData*> peer) {
} }
rpl::producer<DocumentId> EmojiStatusIdValue(not_null<PeerData*> peer) { rpl::producer<DocumentId> EmojiStatusIdValue(not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) { if (peer->isChat()) {
return user->session().changes().peerFlagsValue( return rpl::single(DocumentId(0));
peer,
Data::PeerUpdate::Flag::EmojiStatus
) | rpl::map([=] { return user->emojiStatusId(); });
} }
return rpl::single(DocumentId(0)); return peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::EmojiStatus
) | rpl::map([=] { return peer->emojiStatusId(); });
} }
} // namespace Profile } // namespace Profile
} // namespace Info } // namespace Info

View file

@ -132,7 +132,7 @@ int PeerBadge::drawGetWidth(
Expects(descriptor.customEmojiRepaint != nullptr); Expects(descriptor.customEmojiRepaint != nullptr);
const auto peer = descriptor.peer; const auto peer = descriptor.peer;
if ((peer->isScam() || peer->isFake()) && descriptor.scam) { if (descriptor.scam && (peer->isScam() || peer->isFake())) {
const auto phrase = peer->isScam() const auto phrase = peer->isScam()
? tr::lng_scam_badge(tr::now) ? tr::lng_scam_badge(tr::now)
: tr::lng_fake_badge(tr::now); : tr::lng_fake_badge(tr::now);
@ -159,27 +159,15 @@ int PeerBadge::drawGetWidth(
phrase, phrase,
phraseWidth); phraseWidth);
return st::dialogsScamSkip + width; return st::dialogsScamSkip + width;
} else if (peer->isVerified() && descriptor.verified) { } else if (descriptor.premium
const auto iconw = descriptor.verified->width(); && peer->emojiStatusId()
descriptor.verified->paint( && (peer->isPremium() || peer->isChannel())
p,
rectForName.x() + qMin(nameWidth, rectForName.width() - iconw),
rectForName.y(),
outerWidth);
return iconw;
} else if (peer->isPremium()
&& descriptor.premium
&& peer->session().premiumBadgesShown()) { && peer->session().premiumBadgesShown()) {
const auto id = peer->isUser() ? peer->asUser()->emojiStatusId() : 0; const auto id = peer->emojiStatusId();
const auto iconw = descriptor.premium->width(); const auto iconw = descriptor.premium->width();
const auto iconx = rectForName.x() const auto iconx = rectForName.x()
+ qMin(nameWidth, rectForName.width() - iconw); + qMin(nameWidth, rectForName.width() - iconw);
const auto icony = rectForName.y(); const auto icony = rectForName.y();
if (!id) {
_emojiStatus = nullptr;
descriptor.premium->paint(p, iconx, icony, outerWidth);
return iconw;
}
if (!_emojiStatus) { if (!_emojiStatus) {
_emojiStatus = std::make_unique<EmojiStatus>(); _emojiStatus = std::make_unique<EmojiStatus>();
const auto size = st::emojiSize; const auto size = st::emojiSize;
@ -205,6 +193,25 @@ int PeerBadge::drawGetWidth(
.paused = descriptor.paused || On(PowerSaving::kEmojiStatus), .paused = descriptor.paused || On(PowerSaving::kEmojiStatus),
}); });
return iconw - 4 * _emojiStatus->skip; return iconw - 4 * _emojiStatus->skip;
} else if (descriptor.verified && peer->isVerified()) {
const auto iconw = descriptor.verified->width();
descriptor.verified->paint(
p,
rectForName.x() + qMin(nameWidth, rectForName.width() - iconw),
rectForName.y(),
outerWidth);
return iconw;
} else if (descriptor.premium
&& peer->isPremium()
&& peer->session().premiumBadgesShown()) {
const auto id = peer->isChat() ? 0 : peer->emojiStatusId();
const auto iconw = descriptor.premium->width();
const auto iconx = rectForName.x()
+ qMin(nameWidth, rectForName.width() - iconw);
const auto icony = rectForName.y();
_emojiStatus = nullptr;
descriptor.premium->paint(p, iconx, icony, outerWidth);
return iconw;
} }
return 0; return 0;
} }