diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 4944ebba6..360755acc 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_location.h" #include "data/data_histories.h" #include "data/data_group_call.h" +#include "data/data_message_reactions.h" #include "main/main_session.h" #include "main/session/send_as_peers.h" #include "base/unixtime.h" @@ -760,6 +761,14 @@ PeerId ChannelData::groupCallDefaultJoinAs() const { return _callDefaultJoinAs; } +void ChannelData::setAllowedReactions(std::vector list) { + _allowedReactions = std::move(list); +} + +const std::vector &ChannelData::allowedReactions() const { + return _allowedReactions; +} + namespace Data { void ApplyMigration( @@ -903,6 +912,8 @@ void ApplyChannelUpdate( } } channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty())); + channel->setAllowedReactions( + Data::Reactions::ParseAllowed(update.vavailable_reactions())); channel->fullUpdated(); channel->setPendingRequestsCount( update.vrequests_pending().value_or_empty(), diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index e1444813d..9590f6b0e 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -410,6 +410,9 @@ public: void setGroupCallDefaultJoinAs(PeerId peerId); [[nodiscard]] PeerId groupCallDefaultJoinAs() const; + void setAllowedReactions(std::vector list); + [[nodiscard]] const std::vector &allowedReactions() const; + // Still public data members. uint64 access = 0; @@ -457,6 +460,8 @@ private: QString _inviteLink; std::optional _linkedChat; + std::vector _allowedReactions; + std::unique_ptr _call; PeerId _callDefaultJoinAs = 0; diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index 731b595bf..7d5d6bb61 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_changes.h" #include "data/data_group_call.h" +#include "data/data_message_reactions.h" #include "history/history.h" #include "main/main_session.h" #include "apiwrap.h" @@ -286,6 +287,14 @@ void ChatData::setPendingRequestsCount( } } +void ChatData::setAllowedReactions(std::vector list) { + _allowedReactions = std::move(list); +} + +const std::vector &ChatData::allowedReactions() const { + return _allowedReactions; +} + namespace Data { void ApplyChatUpdate( @@ -457,6 +466,8 @@ void ApplyChatUpdate(not_null chat, const MTPDchatFull &update) { } chat->checkFolder(update.vfolder_id().value_or_empty()); chat->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty())); + chat->setAllowedReactions( + Data::Reactions::ParseAllowed(update.vavailable_reactions())); chat->fullUpdated(); chat->setAbout(qs(update.vabout())); chat->setPendingRequestsCount( diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index 69cd51627..8893a5889 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -175,6 +175,9 @@ public: int count, std::vector recentRequesters); + void setAllowedReactions(std::vector list); + [[nodiscard]] const std::vector &allowedReactions() const; + // Still public data members. const MTPlong inputChat; @@ -199,6 +202,8 @@ private: int _pendingRequestsCount = 0; std::vector _recentRequesters; + std::vector _allowedReactions; + std::unique_ptr _call; PeerId _callDefaultJoinAs = 0; base::flat_map> _botCommands; diff --git a/Telegram/SourceFiles/data/data_message_reactions.cpp b/Telegram/SourceFiles/data/data_message_reactions.cpp index 1569b555d..75fc019c1 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.cpp +++ b/Telegram/SourceFiles/data/data_message_reactions.cpp @@ -11,9 +11,119 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "main/main_session.h" #include "data/data_session.h" +#include "data/data_channel.h" +#include "data/data_chat.h" +#include "base/timer_rpl.h" #include "apiwrap.h" namespace Data { +namespace { + +constexpr auto kRefreshEach = 60 * 60 * crl::time(1000); + +} // namespace + +Reactions::Reactions(not_null owner) : _owner(owner) { + request(); + + base::timer_each( + kRefreshEach + ) | rpl::start_with_next([=] { + request(); + }, _lifetime); +} + +const std::vector &Reactions::list() const { + return _available; +} + +std::vector Reactions::list(not_null peer) const { + if (const auto chat = peer->asChat()) { + return filtered(chat->allowedReactions()); + } else if (const auto channel = peer->asChannel()) { + return filtered(channel->allowedReactions()); + } else { + return list(); + } +} + +std::vector Reactions::Filtered( + const std::vector &reactions, + const std::vector &emoji) { + auto result = std::vector(); + result.reserve(emoji.size()); + for (const auto &single : emoji) { + const auto i = ranges::find(reactions, single, &Reaction::emoji); + if (i != end(reactions)) { + result.push_back(*i); + } + } + return result; +} + +std::vector Reactions::filtered( + const std::vector &emoji) const { + return Filtered(list(), emoji); +} + +std::vector Reactions::ParseAllowed( + const MTPVector *list) { + if (!list) { + return {}; + } + return list->v | ranges::view::transform([](const MTPstring &string) { + return qs(string); + }) | ranges::to_vector; +} + +void Reactions::request() { + auto &api = _owner->session().api(); + _requestId = api.request(MTPmessages_GetAvailableReactions( + MTP_int(_hash) + )).done([=](const MTPmessages_AvailableReactions &result) { + _requestId = 0; + result.match([&](const MTPDmessages_availableReactions &data) { + _hash = data.vhash().v; + + const auto &list = data.vreactions().v; + _available.clear(); + _available.reserve(data.vreactions().v.size()); + for (const auto &reaction : list) { + if (const auto parsed = parse(reaction)) { + _available.push_back(*parsed); + } + } + _updated.fire({}); + }, [&](const MTPDmessages_availableReactionsNotModified &) { + }); + }).fail([=] { + _requestId = 0; + _hash = 0; + }).send(); +} + +std::optional Reactions::parse(const MTPAvailableReaction &entry) { + return entry.match([&](const MTPDavailableReaction &data) { + const auto emoji = qs(data.vreaction()); + const auto known = (Ui::Emoji::Find(emoji) != nullptr); + if (!known) { + LOG(("API Error: Unknown emoji in reactions: %1").arg(emoji)); + } + return known + ? std::make_optional(Reaction{ + .emoji = emoji, + .title = qs(data.vtitle()), + .staticIcon = _owner->processDocument(data.vstatic_icon()), + .selectAnimation = _owner->processDocument( + data.vselect_animation()), + .activateAnimation = _owner->processDocument( + data.vactivate_animation()), + .activateEffects = _owner->processDocument( + data.veffect_animation()), + }) + : std::nullopt; + }); +} std::vector MessageReactions::SuggestList() { constexpr auto utf = [](const char *utf) { diff --git a/Telegram/SourceFiles/data/data_message_reactions.h b/Telegram/SourceFiles/data/data_message_reactions.h index e2f392c1b..af521df1f 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.h +++ b/Telegram/SourceFiles/data/data_message_reactions.h @@ -9,6 +9,53 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { +class Session; + +struct Reaction { + QString emoji; + QString title; + not_null staticIcon; + not_null selectAnimation; + not_null activateAnimation; + not_null activateEffects; +}; + +class Reactions final { +public: + explicit Reactions(not_null owner); + + [[nodiscard]] const std::vector &list() const; + [[nodiscard]] std::vector list(not_null peer) const; + + [[nodiscard]] static std::vector Filtered( + const std::vector &reactions, + const std::vector &emoji); + [[nodiscard]] std::vector filtered( + const std::vector &emoji) const; + + [[nodiscard]] static std::vector ParseAllowed( + const MTPVector *list); + + [[nodiscard]] rpl::producer<> updates() const; + +private: + void request(); + + [[nodiscard]] std::optional parse( + const MTPAvailableReaction &entry); + + const not_null _owner; + + std::vector _available; + rpl::event_stream<> _updated; + + mtpRequestId _requestId = 0; + int32 _hash = 0; + + rpl::lifetime _lifetime; + +}; + class MessageReactions final { public: static std::vector SuggestList(); diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index b82005903..b3777dee5 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_scheduled_messages.h" #include "data/data_send_action.h" #include "data/data_sponsored_messages.h" +#include "data/data_message_reactions.h" #include "data/data_cloud_themes.h" #include "data/data_streaming.h" #include "data/data_media_rotation.h" @@ -241,7 +242,8 @@ Session::Session(not_null session) , _mediaRotation(std::make_unique()) , _histories(std::make_unique(this)) , _stickers(std::make_unique(this)) -, _sponsoredMessages(std::make_unique(this)) { +, _sponsoredMessages(std::make_unique(this)) +, _reactions(std::make_unique(this)) { _cache->open(_session->local().cacheKey()); _bigFileCache->open(_session->local().cacheBigFileKey()); diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index f5f9ed779..aa28fd30d 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -52,6 +52,7 @@ class WallPaper; class ScheduledMessages; class SendActionManager; class SponsoredMessages; +class Reactions; class ChatFilters; class CloudThemes; class Streaming; @@ -113,6 +114,10 @@ public: [[nodiscard]] SponsoredMessages &sponsoredMessages() const { return *_sponsoredMessages; } + [[nodiscard]] Reactions &reactions() const { + return *_reactions; + } + [[nodiscard]] MsgId nextNonHistoryEntryId() { return ++_nonHistoryEntryId; } @@ -962,15 +967,17 @@ private: uint64 _wallpapersHash = 0; Groups _groups; - std::unique_ptr _chatsFilters; + const std::unique_ptr _chatsFilters; std::unique_ptr _scheduledMessages; - std::unique_ptr _cloudThemes; - std::unique_ptr _sendActionManager; - std::unique_ptr _streaming; - std::unique_ptr _mediaRotation; - std::unique_ptr _histories; - std::unique_ptr _stickers; + const std::unique_ptr _cloudThemes; + const std::unique_ptr _sendActionManager; + const std::unique_ptr _streaming; + const std::unique_ptr _mediaRotation; + const std::unique_ptr _histories; + const std::unique_ptr _stickers; std::unique_ptr _sponsoredMessages; + const std::unique_ptr _reactions; + MsgId _nonHistoryEntryId = ServerMaxMsgId; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 9a664f70a..953f55ea7 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1698,9 +1698,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { auto reactionMenu = std::make_unique( this, st::reactionMenu); - for (const auto &text : Data::MessageReactions::SuggestList()) { - reactionMenu->addAction(text, [=] { - item->addReaction(text); + auto &reactions = item->history()->owner().reactions(); + for (const auto &entry : reactions.list(item->history()->peer)) { + reactionMenu->addAction(entry.emoji, [=] { + item->addReaction(entry.emoji); }); } _menu->addAction("Reaction", std::move(reactionMenu)); @@ -3789,7 +3790,7 @@ not_null HistoryInner::ElementDelegate() { Instance->elementShowReactions(view); } } - + }; static Result result; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 821066edb..acb67cf39 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -745,7 +745,7 @@ bool HistoryItem::suggestDeleteAllReport() const { } bool HistoryItem::canReact() const { - return IsServerMsgId(id) && !out() && !_history->peer->isSelf(); + return isRegular(); } void HistoryItem::addReaction(const QString &reaction) { diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp index e8beb806b..b75f5023a 100644 --- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp +++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp @@ -213,11 +213,6 @@ int BottomInfo::resizeToWidth(int newWidth) { } void BottomInfo::layout() { - //const auto good = ::Data::MessageReactions::SuggestList(); - //for (const auto &item : good) { - // _data.reactions.emplace(item, rand_value() + 1); - //} - layoutDateText(); layoutViewsText(); layoutRepliesText();