data_message_reactions.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #pragma once
  8. #include "base/timer.h"
  9. #include "data/data_message_reaction_id.h"
  10. #include "data/stickers/data_custom_emoji.h"
  11. namespace Ui {
  12. class AnimatedIcon;
  13. } // namespace Ui
  14. namespace Ui::Text {
  15. class CustomEmoji;
  16. } // namespace Ui::Text
  17. namespace Data {
  18. class SavedSublist;
  19. class DocumentMedia;
  20. class Session;
  21. struct Reaction {
  22. ReactionId id;
  23. QString title;
  24. //not_null<DocumentData*> staticIcon;
  25. not_null<DocumentData*> appearAnimation;
  26. not_null<DocumentData*> selectAnimation;
  27. //not_null<DocumentData*> activateAnimation;
  28. //not_null<DocumentData*> activateEffects;
  29. DocumentData *centerIcon = nullptr;
  30. DocumentData *aroundAnimation = nullptr;
  31. int count = 0;
  32. bool active = false;
  33. bool effect = false;
  34. bool premium = false;
  35. };
  36. struct PossibleItemReactionsRef {
  37. std::vector<not_null<const Reaction*>> recent;
  38. std::vector<not_null<const Reaction*>> stickers;
  39. bool customAllowed = false;
  40. bool tags = false;
  41. };
  42. struct PossibleItemReactions {
  43. PossibleItemReactions() = default;
  44. explicit PossibleItemReactions(const PossibleItemReactionsRef &other);
  45. std::vector<Reaction> recent;
  46. std::vector<Reaction> stickers;
  47. bool customAllowed = false;
  48. bool tags = false;
  49. };
  50. [[nodiscard]] PossibleItemReactionsRef LookupPossibleReactions(
  51. not_null<HistoryItem*> item,
  52. bool paidInFront = false);
  53. struct MyTagInfo {
  54. ReactionId id;
  55. QString title;
  56. int count = 0;
  57. };
  58. struct PaidReactionSend {
  59. int count = 0;
  60. bool valid = false;
  61. std::optional<PeerId> shownPeer = PeerId();
  62. };
  63. class Reactions final : private CustomEmojiManager::Listener {
  64. public:
  65. explicit Reactions(not_null<Session*> owner);
  66. ~Reactions();
  67. [[nodiscard]] Session &owner() const {
  68. return *_owner;
  69. }
  70. [[nodiscard]] Main::Session &session() const;
  71. void refreshTop();
  72. void refreshRecent();
  73. void refreshRecentDelayed();
  74. void refreshDefault();
  75. void refreshMyTags(SavedSublist *sublist = nullptr);
  76. void refreshMyTagsDelayed();
  77. void refreshTags();
  78. void refreshEffects();
  79. enum class Type {
  80. Active,
  81. Recent,
  82. Top,
  83. All,
  84. MyTags,
  85. Tags,
  86. Effects,
  87. };
  88. [[nodiscard]] const std::vector<Reaction> &list(Type type) const;
  89. [[nodiscard]] const std::vector<MyTagInfo> &myTagsInfo() const;
  90. [[nodiscard]] const QString &myTagTitle(const ReactionId &id) const;
  91. [[nodiscard]] ReactionId favoriteId() const;
  92. [[nodiscard]] const Reaction *favorite() const;
  93. void setFavorite(const ReactionId &id);
  94. void incrementMyTag(const ReactionId &id, SavedSublist *sublist);
  95. void decrementMyTag(const ReactionId &id, SavedSublist *sublist);
  96. void renameTag(const ReactionId &id, const QString &name);
  97. [[nodiscard]] DocumentData *chooseGenericAnimation(
  98. not_null<DocumentData*> custom) const;
  99. [[nodiscard]] DocumentData *choosePaidReactionAnimation() const;
  100. [[nodiscard]] rpl::producer<> topUpdates() const;
  101. [[nodiscard]] rpl::producer<> recentUpdates() const;
  102. [[nodiscard]] rpl::producer<> defaultUpdates() const;
  103. [[nodiscard]] rpl::producer<> favoriteUpdates() const;
  104. [[nodiscard]] rpl::producer<> myTagsUpdates() const;
  105. [[nodiscard]] rpl::producer<> tagsUpdates() const;
  106. [[nodiscard]] rpl::producer<ReactionId> myTagRenamed() const;
  107. [[nodiscard]] rpl::producer<> effectsUpdates() const;
  108. void preloadReactionImageFor(const ReactionId &emoji);
  109. [[nodiscard]] QImage resolveReactionImageFor(const ReactionId &emoji);
  110. // This is used to reserve space for the effect in BottomInfo but not
  111. // actually paint anything, used in case we want to paint icon ourselves.
  112. static constexpr auto kFakeEffectId = EffectId(1);
  113. void preloadEffectImageFor(EffectId id);
  114. [[nodiscard]] QImage resolveEffectImageFor(EffectId id);
  115. void preloadAnimationsFor(const ReactionId &emoji);
  116. void send(not_null<HistoryItem*> item, bool addToRecent);
  117. [[nodiscard]] bool sending(not_null<HistoryItem*> item) const;
  118. void poll(not_null<HistoryItem*> item, crl::time now);
  119. void updateAllInHistory(not_null<PeerData*> peer, bool enabled);
  120. void clearTemporary();
  121. [[nodiscard]] Reaction *lookupTemporary(const ReactionId &id);
  122. [[nodiscard]] not_null<Reaction*> lookupPaid();
  123. [[nodiscard]] not_null<DocumentData*> paidToastAnimation();
  124. [[nodiscard]] rpl::producer<std::vector<Reaction>> myTagsValue(
  125. SavedSublist *sublist = nullptr);
  126. [[nodiscard]] bool isQuitPrevent();
  127. void schedulePaid(not_null<HistoryItem*> item);
  128. void undoScheduledPaid(not_null<HistoryItem*> item);
  129. [[nodiscard]] crl::time sendingScheduledPaidAt(
  130. not_null<HistoryItem*> item) const;
  131. [[nodiscard]] static crl::time ScheduledPaidDelay();
  132. [[nodiscard]] static bool HasUnread(const MTPMessageReactions &data);
  133. static void CheckUnknownForUnread(
  134. not_null<Session*> owner,
  135. const MTPMessage &message);
  136. private:
  137. struct ImageSet {
  138. QImage image;
  139. std::shared_ptr<DocumentMedia> media;
  140. std::unique_ptr<Ui::AnimatedIcon> icon;
  141. bool fromSelectAnimation = false;
  142. bool effect = false;
  143. };
  144. struct TagsBySublist {
  145. TagsBySublist() = default;
  146. TagsBySublist(TagsBySublist&&) = default;
  147. TagsBySublist(const TagsBySublist&) = delete;
  148. TagsBySublist &operator=(TagsBySublist&&) = default;
  149. TagsBySublist &operator=(const TagsBySublist&) = delete;
  150. std::vector<Reaction> tags;
  151. std::vector<MyTagInfo> info;
  152. uint64 hash = 0;
  153. mtpRequestId requestId = 0;
  154. bool requestScheduled = false;
  155. bool updateScheduled = false;
  156. };
  157. [[nodiscard]] not_null<CustomEmojiManager::Listener*> resolveListener();
  158. void customEmojiResolveDone(not_null<DocumentData*> document) override;
  159. void requestTop();
  160. void requestRecent();
  161. void requestDefault();
  162. void requestGeneric();
  163. void requestMyTags(SavedSublist *sublist = nullptr);
  164. void requestTags();
  165. void requestEffects();
  166. void updateTop(const MTPDmessages_reactions &data);
  167. void updateRecent(const MTPDmessages_reactions &data);
  168. void updateDefault(const MTPDmessages_availableReactions &data);
  169. void updateGeneric(const MTPDmessages_stickerSet &data);
  170. void updateMyTags(
  171. SavedSublist *sublist,
  172. const MTPDmessages_savedReactionTags &data);
  173. void updateTags(const MTPDmessages_reactions &data);
  174. void updateEffects(const MTPDmessages_availableEffects &data);
  175. void recentUpdated();
  176. void defaultUpdated();
  177. void myTagsUpdated();
  178. void tagsUpdated();
  179. void effectsUpdated();
  180. [[nodiscard]] std::optional<Reaction> resolveById(const ReactionId &id);
  181. [[nodiscard]] std::vector<Reaction> resolveByIds(
  182. const std::vector<ReactionId> &ids,
  183. base::flat_set<ReactionId> &unresolved);
  184. [[nodiscard]] std::optional<Reaction> resolveByInfo(
  185. const MyTagInfo &info,
  186. SavedSublist *sublist);
  187. [[nodiscard]] std::vector<Reaction> resolveByInfos(
  188. const std::vector<MyTagInfo> &infos,
  189. base::flat_map<
  190. ReactionId,
  191. base::flat_set<SavedSublist*>> &unresolved,
  192. SavedSublist *sublist);
  193. void resolve(const ReactionId &id);
  194. void applyFavorite(const ReactionId &id);
  195. void scheduleMyTagsUpdate(SavedSublist *sublist);
  196. [[nodiscard]] std::optional<Reaction> parse(
  197. const MTPAvailableReaction &entry);
  198. [[nodiscard]] std::optional<Reaction> parse(
  199. const MTPAvailableEffect &entry);
  200. void preloadEffect(const Reaction &effect);
  201. void preloadImageFor(const ReactionId &id);
  202. [[nodiscard]] QImage resolveImageFor(const ReactionId &id);
  203. void loadImage(
  204. ImageSet &set,
  205. not_null<DocumentData*> document,
  206. bool fromSelectAnimation);
  207. void generateImage(ImageSet &set, const QString &emoji);
  208. void setAnimatedIcon(ImageSet &set);
  209. void resolveReactionImages();
  210. void resolveEffectImages();
  211. void downloadTaskFinished();
  212. void fillPaidReactionAnimations() const;
  213. [[nodiscard]] DocumentData *randomLoadedFrom(
  214. std::vector<not_null<DocumentData*>> list) const;
  215. void repaintCollected();
  216. void pollCollected();
  217. void sendPaid();
  218. bool sendPaid(not_null<HistoryItem*> item);
  219. void sendPaidRequest(
  220. not_null<HistoryItem*> item,
  221. PaidReactionSend send);
  222. void sendPaidPrivacyRequest(
  223. not_null<HistoryItem*> item,
  224. PaidReactionSend send);
  225. void sendPaidFinish(
  226. not_null<HistoryItem*> item,
  227. PaidReactionSend send,
  228. bool success);
  229. void checkQuitPreventFinished();
  230. const not_null<Session*> _owner;
  231. std::vector<Reaction> _active;
  232. std::vector<Reaction> _available;
  233. std::vector<Reaction> _recent;
  234. std::vector<ReactionId> _recentIds;
  235. base::flat_set<ReactionId> _unresolvedRecent;
  236. base::flat_map<SavedSublist*, TagsBySublist> _myTags;
  237. base::flat_map<
  238. ReactionId,
  239. base::flat_set<SavedSublist*>> _unresolvedMyTags;
  240. std::vector<Reaction> _tags;
  241. std::vector<ReactionId> _tagsIds;
  242. base::flat_set<ReactionId> _unresolvedTags;
  243. std::vector<Reaction> _top;
  244. std::vector<ReactionId> _topIds;
  245. base::flat_set<ReactionId> _unresolvedTop;
  246. std::vector<not_null<DocumentData*>> _genericAnimations;
  247. mutable std::vector<not_null<DocumentData*>> _paidReactionAnimations;
  248. std::vector<Reaction> _effects;
  249. ReactionId _favoriteId;
  250. ReactionId _unresolvedFavoriteId;
  251. std::optional<Reaction> _favorite;
  252. base::flat_map<
  253. not_null<DocumentData*>,
  254. std::shared_ptr<DocumentMedia>> _iconsCache;
  255. base::flat_map<
  256. not_null<DocumentData*>,
  257. std::shared_ptr<DocumentMedia>> _genericCache;
  258. mutable base::flat_map<
  259. not_null<DocumentData*>,
  260. std::shared_ptr<DocumentMedia>> _paidReactionCache;
  261. rpl::event_stream<> _topUpdated;
  262. rpl::event_stream<> _recentUpdated;
  263. rpl::event_stream<> _defaultUpdated;
  264. rpl::event_stream<> _favoriteUpdated;
  265. rpl::event_stream<SavedSublist*> _myTagsUpdated;
  266. rpl::event_stream<> _tagsUpdated;
  267. rpl::event_stream<ReactionId> _myTagRenamed;
  268. rpl::event_stream<> _effectsUpdated;
  269. // We need &i->second stay valid while inserting new items.
  270. // So we use std::map instead of base::flat_map here.
  271. // Otherwise we could use flat_map<DocumentId, unique_ptr<Reaction>>.
  272. std::map<DocumentId, Reaction> _temporary;
  273. std::optional<Reaction> _paid;
  274. DocumentData *_paidToastAnimation = nullptr;
  275. base::Timer _topRefreshTimer;
  276. mtpRequestId _topRequestId = 0;
  277. uint64 _topHash = 0;
  278. mtpRequestId _recentRequestId = 0;
  279. bool _recentRequestScheduled = false;
  280. uint64 _recentHash = 0;
  281. mtpRequestId _defaultRequestId = 0;
  282. int32 _defaultHash = 0;
  283. mtpRequestId _genericRequestId = 0;
  284. mtpRequestId _tagsRequestId = 0;
  285. uint64 _tagsHash = 0;
  286. mtpRequestId _effectsRequestId = 0;
  287. int32 _effectsHash = 0;
  288. base::flat_map<ReactionId, ImageSet> _images;
  289. rpl::lifetime _imagesLoadLifetime;
  290. bool _waitingForReactions = false;
  291. bool _waitingForEffects = false;
  292. base::flat_map<FullMsgId, mtpRequestId> _sentRequests;
  293. base::flat_map<not_null<HistoryItem*>, crl::time> _repaintItems;
  294. base::Timer _repaintTimer;
  295. base::flat_set<not_null<HistoryItem*>> _pollItems;
  296. base::flat_set<not_null<HistoryItem*>> _pollingItems;
  297. mtpRequestId _pollRequestId = 0;
  298. base::flat_map<not_null<HistoryItem*>, crl::time> _sendPaidItems;
  299. base::flat_map<not_null<HistoryItem*>, mtpRequestId> _sendingPaid;
  300. base::Timer _sendPaidTimer;
  301. mtpRequestId _saveFaveRequestId = 0;
  302. rpl::lifetime _lifetime;
  303. };
  304. struct RecentReaction {
  305. not_null<PeerData*> peer;
  306. bool unread = false;
  307. bool big = false;
  308. bool my = false;
  309. friend inline bool operator==(
  310. const RecentReaction &a,
  311. const RecentReaction &b) = default;
  312. };
  313. struct MessageReactionsTopPaid {
  314. PeerData *peer = nullptr;
  315. uint32 count : 30 = 0;
  316. uint32 top : 1 = 0;
  317. uint32 my : 1 = 0;
  318. friend inline bool operator==(
  319. const MessageReactionsTopPaid &a,
  320. const MessageReactionsTopPaid &b) = default;
  321. };
  322. class MessageReactions final {
  323. public:
  324. explicit MessageReactions(not_null<HistoryItem*> item);
  325. ~MessageReactions();
  326. using TopPaid = MessageReactionsTopPaid;
  327. void add(const ReactionId &id, bool addToRecent);
  328. void remove(const ReactionId &id);
  329. bool change(
  330. const QVector<MTPReactionCount> &list,
  331. const QVector<MTPMessagePeerReaction> &recent,
  332. const QVector<MTPMessageReactor> &top,
  333. bool min);
  334. [[nodiscard]] bool checkIfChanged(
  335. const QVector<MTPReactionCount> &list,
  336. const QVector<MTPMessagePeerReaction> &recent,
  337. bool min) const;
  338. [[nodiscard]] const std::vector<MessageReaction> &list() const;
  339. [[nodiscard]] auto recent() const
  340. -> const base::flat_map<ReactionId, std::vector<RecentReaction>> &;
  341. [[nodiscard]] const std::vector<TopPaid> &topPaid() const;
  342. [[nodiscard]] std::vector<ReactionId> chosen() const;
  343. [[nodiscard]] bool empty() const;
  344. [[nodiscard]] bool hasUnread() const;
  345. void markRead();
  346. void scheduleSendPaid(int count, std::optional<PeerId> shownPeer);
  347. [[nodiscard]] int scheduledPaid() const;
  348. void cancelScheduledPaid();
  349. [[nodiscard]] PaidReactionSend startPaidSending();
  350. void finishPaidSending(PaidReactionSend send, bool success);
  351. [[nodiscard]] bool localPaidData() const;
  352. [[nodiscard]] int localPaidCount() const;
  353. [[nodiscard]] PeerId localPaidShownPeer() const;
  354. bool clearCloudData();
  355. private:
  356. struct Paid {
  357. std::vector<TopPaid> top;
  358. PeerId scheduledShownPeer = 0;
  359. PeerId sendingShownPeer = 0;
  360. uint32 scheduled: 30 = 0;
  361. uint32 scheduledFlag : 1 = 0;
  362. uint32 scheduledPrivacySet : 1 = 0;
  363. uint32 sending : 30 = 0;
  364. uint32 sendingFlag : 1 = 0;
  365. uint32 sendingPrivacySet : 1 = 0;
  366. };
  367. const not_null<HistoryItem*> _item;
  368. std::vector<MessageReaction> _list;
  369. base::flat_map<ReactionId, std::vector<RecentReaction>> _recent;
  370. std::unique_ptr<Paid> _paid;
  371. };
  372. } // namespace Data