From 8f33d5903d6b8892c8cc08c483ed597babbcef54 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 27 Jan 2022 15:55:34 +0300 Subject: [PATCH] Display unread reactions badge in chats list. --- .../icons/history_unread_mention.png | Bin 708 -> 727 bytes .../icons/history_unread_mention@2x.png | Bin 1477 -> 1614 bytes .../icons/history_unread_mention@3x.png | Bin 2137 -> 2368 bytes .../icons/history_unread_reaction.png | Bin 977 -> 1032 bytes .../icons/history_unread_reaction@2x.png | Bin 1900 -> 2051 bytes .../icons/history_unread_reaction@3x.png | Bin 2806 -> 3051 bytes .../data/data_message_reactions.cpp | 19 +- .../SourceFiles/data/data_message_reactions.h | 18 +- Telegram/SourceFiles/dialogs/dialogs.style | 3 + .../SourceFiles/dialogs/ui/dialogs_layout.cpp | 57 ++- Telegram/SourceFiles/history/history.cpp | 2 +- .../history/history_inner_widget.cpp | 470 +++++++++--------- Telegram/SourceFiles/history/history_item.cpp | 10 +- Telegram/SourceFiles/history/history_item.h | 5 +- .../history/history_unread_things.h | 3 +- .../history/view/history_view_bottom_info.cpp | 4 +- .../history/view/history_view_list_widget.cpp | 4 +- .../history/view/history_view_message.cpp | 33 +- .../view/history_view_react_button.cpp | 18 +- .../history/view/history_view_react_button.h | 8 +- .../history/view/history_view_reactions.cpp | 13 +- Telegram/SourceFiles/ui/chat/chat.style | 8 +- Telegram/SourceFiles/ui/chat/chat_style.h | 9 +- 23 files changed, 377 insertions(+), 307 deletions(-) diff --git a/Telegram/Resources/icons/history_unread_mention.png b/Telegram/Resources/icons/history_unread_mention.png index 7f717b1c3b444a103ce7454882c821d43cbc06fb..5e06356cdb130b9106f2396c5d0bc1f2f656d242 100644 GIT binary patch literal 727 zcmV;|0x127P))~&QYS^oP+j~Hq+06e&|M1! z#a%m;PR^y6$qvnEDQFFyL{U&21kKXS39X6+t>(PpKq&2#_kYr(nKOrDwPyP(RF<`n+*noa=9FhMk%GcG>gSzqtOV~peRZp5ZHm1 z&1Sp4zD9}Vaw$ntI-L%O!|{0h^72xaWm7Si%WYfEG^l* z&ny;;Vb`0Ro4vigb!C*&`}=!bTA$Ci4Kqq9?z2jzVw#0Qp<1mr9*^JN-Xux#`~8^b zIIdo=13;_Q+J;;rk-)`pI-Quw=kwo2VYOOaTwLH?uh)yoFXE zv9~CSJkRqyFNz`n7>40?yRio6qgX6jE~h980HjhW4DqdrqWHB2{Uk}kuuv!fK(E)c zTu#?@00;(y7*3~C0N{E4YYory0Py7PZrW>O@aU>ms~AQik>3sk za(8zp2!g7r0MO}lESHN$quBWH@DMXsS64qjtS>Jw-!dmBCzi`mN^xKxA0N&C4nYv= z_4@n!`(!eaWmymeLP#>1?DzX0A0ORr_w4MnnB_x$|q z@pvrivi7t6N3z*$p-{k;lV!QlXbgu#RaFhcAcQQJ%XYgxpU?l9>8AL6zE-RCa~hD_ z+uNO)nfU$wVzJok^)yX038{ln3xNU( z-tQmJp53T2>AlGNndxcsW>!9*n>gcp|M};;W7heo8P7f&w)*PB0*U_PJ7dlzZ9MSh z-S55}v)d(Be}6x9wwUYp{oD`77(H{je&N?ud&Rn4=bukEk$PKJtsszLHaq7)fyJMb z=`)uGc`8k;nI-9_H$7ZSluM9@Z9d+Tg+Iu{dR4}g=j7{vF?+~LY)DMZz^}+{ZnTj z8LB1v(o~AKIh;{Lge%2)@`-6@)BgXd5m>PE{(G+0rkWM?CsQUJWIpihYt>1I&_;&| zevcN{HC@aQnewpUM1qjQ%db^NZ@yPMEU8gtcCws*-t%SIZpP`SRl^ko99-2|GU7AW z8~tuM!M-;@mdKI;Vst058-jU;qFB diff --git a/Telegram/Resources/icons/history_unread_mention@2x.png b/Telegram/Resources/icons/history_unread_mention@2x.png index 02555b8e4c20b01b6838392159de3c11a61dfc0e..fa7f3f65535350d48b64d0f2db7e8a1f8b048ecb 100644 GIT binary patch literal 1614 zcmV-U2C?~xP)!Q3@#)EOmzI1_Qm7$Z!mIo8|5V=k|LD&hPxr z-R9qWulDdCMeEA~vFb|BZtgNP{ruq5# z*fz|}%vdazW5ry zuu!V;OYauOYQ`SRsw&z>zWFY}MJTCHN1APxSkt*orn)zzh? zrETZ%BS((hzkh#qb(Kic+}tem$feZK&_EPCIXQXy^y!`EQLEJ-K71fj8I49E#w@*x zii&77ikIqeI5ILac9$(JEv>!19nTgF29wkZ3g8*x1O=|M>A^Jj`G)h*FmD78VvzRSOFX{FR5zX4`VskbC#; z5r?Z(s!%9|0tN>M#i~q5TCEl@`@w?;L_lR_B{4ONi;H%<-E20S&1SpZzPPxE_eLU- zD_5=%Njf?@P(U~w&dkgdt3JWh)z#r;YqeUum!c?!Veo+V_I8CrfyXEm3S#9kH8n+? zrMS2l4=5`u6TC2?y?y%@)$`-W52ClGriPfcM0tq0yMFyT-Yb{OH#RmKzyu*#AoYOYr5QMx#M(uh&cT z78Deq_Q1fv+S*#8JZo!f_;g&mc8v&N7$)vX1tR#wsZ=V|W?7c#&CAO}ZKu1Olji^5n^#j&r93pSVaQg4*fn=|pcR6xv-{{`TDJ>MCmg4HL(49BM0- zN}?C9_R^(G$x~gtcoDVvuLt-W{x&zM_CoN9^LRX{ou8ji^p1^-G3w zi0k+K`SW`k=t1LfIMBn+=kpQ0<>lqOTaLtHv9o8-qTVxS&fs0k%gaL5BN~gvf|rCp zMpIH!XqxtVy~NyYxd8s7ySp20BDJ=*;$5SoqoNfklnWOw;3e5?HX@+3w3OK3O#aG+ zb2^LN1K*Vju@>^}SY^(!iAX=#c7Y;9|6OEfjR@bl--qeqVlT~wUi!F@;5 zGj+3)wSudln^Zkx?^{P=OP3K9!V z)A-wdEEaqD@}<;+IwT4U3pY16@ww~l?EIhphxV{UG))Z;n~_#gAb z;qaqJkMK7=eeIuu3K*47fslx@0w z`?l1>mzqo_tEs6O8XCg?lOefZzkZ4ERbh)^OM7};W@ctlQBi4WsZOWUXfzs)My*!! zUoiZB|D8K`hK7bD86*Gz00000000000000000000000000Pt7)2dV^}cUR%JnE(I) M07*qoM6N<$g7ao5ZU6uP literal 1477 zcmV;$1v>hPP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91D4+uX1ONa40RR91C;$Ke0D9(TtN;K7FG)l}R9FeMS6L{2TNM9%W|;{u zW_gh^g$I!#LdaZR6mQCd7er)=ltP(`NTh^E5y@0$GF+w1NhI@JhI@aX^SA78pM&qZ z*Z070t@Yb$owN4dYptUuQ~76WYa1LK91;@Z>gsA`Wo2Y!^!)sMe}BKRvGLd2Z)$34 zX=zDif8P;}jg3o6N;Wq)|5o9*yu6&5nQ3BT@}1W&?S_Vi1qB6{mzOF;RH7gad3kx7 znwnp_|HK#-6?JxYrsVHm3Tta?zP`SnxRh|p`k%+;x&4DdObZo;&&Rk@be#A+b}qIougW)bE^s zZ*OmJXJgOK&dTL-rDcVMg)9RZmV8$%L&jQKTGrRsajK7xk5O1UIyw>)6V=t#l{~%) zT3TA;=ZEYnE zGOw$vD?@Q06#$-tU4zqv&0`6;B>MXLEC(WYbLQvgSpslSP!Q`RJUm?1)zw9+y}dnd zL415XSpg7$Baq0&4hRS!696jl)6)~Vs;Q}wp@)!+ddaeZJ3Bkcij;Ei%93GXb6#6p z%UXev26ENk-!I23K@9X9!U90sAr08Kx3?s6045Mf>F@7PSOC~E(jcfDM~-CF1i}Kq zEhG)PAz?|dxVT7qYin!50zhRXjisff9EXr(6dJ+;z%}4BDvILvjFv@f6@f9Pkc^5= zSO6^B%gc+ia-yfFCuu&))4Pi85GWOLRKa(X46_7b0ZdL#k|r`T^6SF|0+9i@{JFck zvsM|p5FIuYeI=Vr77-CaRshwn6UZ(uF67P5&W`niK}Cj{h*h!27|Spz;;6F(#OB@` zB6o=eKDrh)!K$h%*2%=ggv`UkgH@p{(_;!CEGWX&%tT$YQy1F`aWzIuZR+hr~ z%E}6{F+_5E0|E*S5&k>4xVSJdFi>=FquETNH^efZg5Q*w|Qsj7+i) ziHRDLoSaM!6@Z&BM@B}(T^TeS7TlTM-d=p9q0Bu#K4PNi?(W7jaib3g&5DYOnVFfB zlM~SJib#f;nVC{l2*@PcE%1!>_4PdImt=hBU;`*E0Bvn;c%WYjr{p#xBZCbLJT^Aw z;NYM{`XzzeY{bT-QdCsLHnCp{EyH&X_udfErZ6{Qg262DzXX=}5QSe1=*`W|l5dGN ziqC#@b=(acY__Wk4}^=<*VnhRv-88}Ll?srhxpZ1(cIkJI5|1}Fff+k*5bZ%*G!eb z;H0Fa{5-I*5@iyz=HTEU`z7*q`0MLyc9fHR0_i=Lq+$w35k|8@8sQf|lqYe4L#awl fO~r#sIw9phr*WNkh<&>t00000NkvXXu0mjf&<>ry diff --git a/Telegram/Resources/icons/history_unread_mention@3x.png b/Telegram/Resources/icons/history_unread_mention@3x.png index 198089ac00e94f7cb0c678420786431d0b318707..67c6286f97fade49799a78264cbff06486ad9b74 100644 GIT binary patch literal 2368 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 literal 2137 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 diff --git a/Telegram/Resources/icons/history_unread_reaction@2x.png b/Telegram/Resources/icons/history_unread_reaction@2x.png index b4581adb3edeafcac4910a9d3daa4cf81844800a..b5ca42b12d068871c863f5e5884d2ef0b21d6149 100644 GIT binary patch literal 2051 zcmZ`)`9ITt93S~+v6UkXiAjA`j+|+ebB+$zzKh6NzJ0YN6k%+0e${u*l$$w2jIJq@ z`D%qZhgb@IeRB*OnwcE&{qzTX-;ej}{rc(sx}VS2eOG4(sFaEn2n2$jb3~y59rRPc z;=ucWs3`)}zEDTcJ0Os>{7(@B6%@fhAW7Oelr<)*h+lBS5%Wd0vy=6E$8BuA@=4aa z@nglu@2LN#op8_QVwaANAo=&4NBZS8#!^%(1KV;}=_@)0N4$azp;!Jk<$USoVBs~ewRh3M<*28V=@cDADf z0;V{Pek>U5PZo>y?Afy^JcA_Z>+5S`Vsfp=Sk}(YF4p3U_I)dj6_F^16{wkYPhM%X zV{q^rGvz?r@!y=B`uYk_w~=<8$sP^f`KJ8cSN_f3#O29WbxWd^aDKSDCr9_|XzqJf zPtTUq$nzI3#;)(|?ELugBfO@*zISxg)W9H1Px+L&`5`&Ew^WlJLo+k*Ei#!*h>e|O z5Twt?#+ttr3Iq#FOONYTr&;o?zLsu-BUoB-v0BQg&b^cr$?v?Sr6w9}=35kl!Jt?W zuG$qlD;++(pFc4%fkYyIeS&du8Q5B1zHtsQ8&R(Zikjc_0OX4<|o{@3jq`iYf;>Y1(qf1JO1OmaoqNYY$ zS2ryHCVE;`^$T`xZfee%hb}=2HQj~PyRw!9HrK4E0U*D zk0eR#-Rm{n7@c{(${#p!gk!vq&-P$B3620_4R6;gHqSnSTloB0;T%39Tujh zLb`rV1v_N?wHO|aen8_DWF$)~CB!#0=&`f2v&-%RiN=3o&D@UKGmNDPS(=Ju91gb+ z9wXw3g{>`&WVQiO3WdUO_Uh`YZpa7m*ewy%yt1+~j&WzsnO#^=P0D^cvEC8z84@}9ejii!q{99!Q=7t{4~#l6*MqqATl(x6z73Pi>Iyg zKTG1hy~ov|D3a78R$zVJ!-o?8(r}#K8@)r0T5=U{zeSk?{{Ly;!*29#mEsQ%4MFxM zw)=y@;O`aj+@wO&;H&R}L6HG^(Z}skVdpLkRycUNX~Dk%z))RC6#nR>ObtYuBMQcj|7%sX0kbeZ=Vr9yf4ZzPyIhkne_c~N8eDgg{#2b4I z2?#DKDx%Zr+)rxeQJK(v$#1*6&$|c0Ivy%8Khbb-(xpoZTt1&)y0x`ci0$ase$<_n zB}>1XvS010txeg}Cr|#8F+?B$AY07C6A}~Q8vqcjsy(#o+^L3xU`dp{{g4L~KD{!< zyxG;?ucxP%PNYyI07}Z-5b-~kerdiVDKx>2=1s?+%+dF|g=snBke+vP<2_jk9(?PT z9=Fl2ZLd!)rT}M#lb4k>Fg8YU^o|^13SXjYA2JjG^Uvv48l~%zT5|X2uZ8`It~Gw5 zR_}Jq$=X^haFNeXhVt_AU~D7;aj^u+NlHn<(Y)w)JJZz)!<(S0rtI1W+1YT3jGH!& zzP=^OkNeLA1O(XAki_lpT7oD%9$?(fmsi_Ptwy~`N|HeAez-!uhzbhQZnbm*vwC~C zDO>Me$p+r~>lIJW8)A~ogR}^FGt0%9@(4_V##w%+mN7L?vfYUQX^c?OL#V z{pEo0uj&^(Je0PdzI#sECEgPWg(ws%)R@rVrhRa1Y)s#eQMnuhFyPG0%$25_%3B|4 zuU-vwccW`DKvbbBUMX$iZKE9>_L^CzAgha<#@h7_)&Jd*G7?ioyJAMmxv6hp^cY~P OgU;DGqZ(}d6aE9^aPRs6 literal 1900 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 Qq<0=$%S#qSbMNH;0W%Ec(EtDd literal 2806 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.erase(ranges::remove(j->second, self), end(j->second)); + j->second.erase( + ranges::remove(j->second, self, &RecentReaction::peer), + end(j->second)); if (j->second.empty() || removed) { _recent.erase(j); } @@ -502,7 +504,7 @@ void MessageReactions::add(const QString &reaction) { if (!reaction.isEmpty()) { if (_item->canViewReactions()) { auto &list = _recent[reaction]; - list.insert(begin(list), self); + list.insert(begin(list), RecentReaction{ self }); } ++_list[reaction]; } @@ -557,15 +559,16 @@ void MessageReactions::set( _chosen = QString(); } } - auto parsed = base::flat_map< - QString, - std::vector>>(); + auto parsed = base::flat_map>(); for (const auto &reaction : recent) { reaction.match([&](const MTPDmessagePeerReaction &data) { const auto emoji = qs(data.vreaction()); if (_list.contains(emoji)) { - parsed[emoji].push_back( - owner.peer(peerFromMTP(data.vpeer_id()))); + parsed[emoji].push_back(RecentReaction{ + .peer = owner.peer(peerFromMTP(data.vpeer_id())), + .unread = data.is_unread(), + .big = data.is_big(), + }); } }); } @@ -584,7 +587,7 @@ const base::flat_map &MessageReactions::list() const { } auto MessageReactions::recent() const --> const base::flat_map>> & { +-> const base::flat_map> & { return _recent; } diff --git a/Telegram/SourceFiles/data/data_message_reactions.h b/Telegram/SourceFiles/data/data_message_reactions.h index a83fe1018..50935f531 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.h +++ b/Telegram/SourceFiles/data/data_message_reactions.h @@ -125,6 +125,20 @@ private: }; +struct RecentReaction { + not_null peer; + bool unread = false; + bool big = false; + + inline friend constexpr bool operator==( + const RecentReaction &a, + const RecentReaction &b) noexcept { + return (a.peer.get() == b.peer.get()) + && (a.unread == b.unread) + && (a.big == b.big); + } +}; + class MessageReactions final { public: explicit MessageReactions(not_null item); @@ -137,7 +151,7 @@ public: bool ignoreChosen); [[nodiscard]] const base::flat_map &list() const; [[nodiscard]] auto recent() const - -> const base::flat_map>> &; + -> const base::flat_map> &; [[nodiscard]] QString chosen() const; [[nodiscard]] bool empty() const; @@ -146,7 +160,7 @@ private: QString _chosen; base::flat_map _list; - base::flat_map>> _recent; + base::flat_map> _recent; }; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 087c04fee..e02caaaf2 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -306,3 +306,6 @@ dialogsMiniPlay: icon{{ "dialogs/dialogs_mini_play", videoPlayIconFg }}; dialogsUnreadMention: icon{{ "dialogs/dialogs_mention", dialogsUnreadFg }}; dialogsUnreadMentionOver: icon{{ "dialogs/dialogs_mention", dialogsUnreadFgOver }}; dialogsUnreadMentionActive: icon{{ "dialogs/dialogs_mention", dialogsUnreadFgActive }}; +dialogsUnreadReaction: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFg }}; +dialogsUnreadReactionOver: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFgOver }}; +dialogsUnreadReactionActive: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFgActive }}; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp index 8fa508f5d..0c4837bf3 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp @@ -85,6 +85,7 @@ void PaintNarrowCounter( bool displayUnreadCounter, bool displayUnreadMark, bool displayMentionBadge, + bool displayReactionBadge, int unreadCount, bool selected, bool active, @@ -95,7 +96,10 @@ void PaintNarrowCounter( const auto counter = (unreadCount > 0) ? QString::number(unreadCount) : QString(); - const auto allowDigits = displayMentionBadge ? 1 : 3; + const auto allowDigits = (displayMentionBadge + || displayReactionBadge) + ? 1 + : 3; const auto unreadRight = st::dialogsPadding.x() + st::dialogsPhotoSize; const auto unreadTop = st::dialogsPadding.y() @@ -115,7 +119,7 @@ void PaintNarrowCounter( allowDigits); skipBeforeMention += badge.width() + st.padding; } - if (displayMentionBadge) { + if (displayMentionBadge || displayReactionBadge) { const auto counter = QString(); const auto unreadRight = st::dialogsPadding.x() + st::dialogsPhotoSize @@ -136,11 +140,17 @@ void PaintNarrowCounter( unreadRight, unreadTop, st); - (st.active - ? st::dialogsUnreadMentionActive - : st.selected - ? st::dialogsUnreadMentionOver - : st::dialogsUnreadMention).paintInCenter(p, badge); + (displayMentionBadge + ? (st.active + ? st::dialogsUnreadMentionActive + : st.selected + ? st::dialogsUnreadMentionOver + : st::dialogsUnreadMention) + : (st.active + ? st::dialogsUnreadReactionActive + : st.selected + ? st::dialogsUnreadReactionOver + : st::dialogsUnreadReaction)).paintInCenter(p, badge); } } @@ -152,6 +162,7 @@ int PaintWideCounter( bool displayUnreadCounter, bool displayUnreadMark, bool displayMentionBadge, + bool displayReactionBadge, bool displayPinnedIcon, int unreadCount, bool active, @@ -199,7 +210,7 @@ int PaintWideCounter( hadOneBadge = true; } - if (displayMentionBadge) { + if (displayMentionBadge || displayReactionBadge) { const auto counter = QString(); const auto unreadRight = fullWidth - st::dialogsPadding.x() @@ -221,11 +232,17 @@ int PaintWideCounter( unreadRight, unreadTop, st); - (st.active - ? st::dialogsUnreadMentionActive - : st.selected - ? st::dialogsUnreadMentionOver - : st::dialogsUnreadMention).paintInCenter(p, badge); + (displayMentionBadge + ? (st.active + ? st::dialogsUnreadMentionActive + : st.selected + ? st::dialogsUnreadMentionOver + : st::dialogsUnreadMention) + : (st.active + ? st::dialogsUnreadReactionActive + : st.selected + ? st::dialogsUnreadReactionOver + : st::dialogsUnreadReaction)).paintInCenter(p, badge); availableWidth -= badge.width() + st.padding + (hadOneBadge ? st::dialogsUnreadPadding : 0); @@ -779,8 +796,10 @@ void RowPainter::paint( : QDateTime(); }(); const auto displayMentionBadge = history - ? history->unreadMentions().has() - : false; + && history->unreadMentions().has(); + const auto displayReactionBadge = !displayMentionBadge + && history + && history->unreadReactions().has(); const auto displayUnreadCounter = [&] { if (displayMentionBadge && unreadCount == 1 @@ -796,6 +815,7 @@ void RowPainter::paint( && unreadMark; const auto displayPinnedIcon = !displayUnreadCounter && !displayMentionBadge + && !displayReactionBadge && !displayUnreadMark && entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex()); @@ -824,6 +844,7 @@ void RowPainter::paint( displayUnreadCounter, displayUnreadMark, displayMentionBadge, + displayReactionBadge, displayPinnedIcon, unreadCount, active, @@ -868,6 +889,7 @@ void RowPainter::paint( displayUnreadCounter, displayUnreadMark, displayMentionBadge, + displayReactionBadge, unreadCount, selected, active, @@ -943,6 +965,9 @@ void RowPainter::paint( const auto mentionMuted = (history->folder() != nullptr); const auto displayMentionBadge = displayUnreadInfo && history->unreadMentions().has(); + const auto displayReactionBadge = displayUnreadInfo + && !displayMentionBadge + && history->unreadReactions().has(); const auto displayUnreadCounter = (unreadCount > 0); const auto displayUnreadMark = !displayUnreadCounter && !displayMentionBadge @@ -961,6 +986,7 @@ void RowPainter::paint( displayUnreadCounter, displayUnreadMark, displayMentionBadge, + displayReactionBadge, displayPinnedIcon, unreadCount, active, @@ -987,6 +1013,7 @@ void RowPainter::paint( displayUnreadCounter, displayUnreadMark, displayMentionBadge, + displayReactionBadge, unreadCount, selected, active, diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index e3b4af378..1a44719e8 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -693,7 +693,7 @@ not_null History::addNewLocalMessage( } void History::setUnreadThingsKnown() { - _flags &= ~Flag::UnreadThingsKnown; + _flags |= Flag::UnreadThingsKnown; } HistoryUnreadThings::Proxy History::unreadMentions() { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 9eae92ee3..7dc5495f1 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -848,10 +848,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) { mouseActionUpdate(); } - const auto guard = gsl::finally([&] { - _userpicsCache.clear(); - }); - Painter p(this); auto clip = e->rect(); @@ -872,7 +868,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) { const auto now = crl::now(); const auto historyDisplayedEmpty = _history->isDisplayedEmpty() && (!_migrated || _migrated->isDisplayedEmpty()); - bool noHistoryDisplayed = historyDisplayedEmpty; if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) { const auto st = context.st; const auto stm = &st->messageStyle(false, false); @@ -899,254 +894,251 @@ void HistoryInner::paintEvent(QPaintEvent *e) { _emptyPainter = nullptr; } - _reactionsManager->startEffectsCollection(); - if (!noHistoryDisplayed) { - auto readMentions = base::flat_set>(); + const auto mtop = migratedTop(); + const auto htop = historyTop(); + if (historyDisplayedEmpty || (mtop < 0 && htop < 0)) { + return; + } - adjustCurrent(clip.top()); - - auto drawToY = clip.y() + clip.height(); - - auto selfromy = itemTop(_dragSelFrom); - auto seltoy = itemTop(_dragSelTo); - if (selfromy < 0 || seltoy < 0) { - selfromy = seltoy = -1; - } else { - seltoy += _dragSelTo->height(); + auto readTill = (HistoryItem*)nullptr; + auto readContents = base::flat_set>(); + const auto guard = gsl::finally([&] { + if (readTill && _widget->doWeReadServerHistory()) { + session().data().histories().readInboxTill(readTill); } + if (!readContents.empty() && _widget->doWeReadMentions()) { + session().api().markContentsRead(readContents); + } + _userpicsCache.clear(); + }); - auto mtop = migratedTop(); - auto htop = historyTop(); - auto hdrawtop = historyDrawTop(); - if (mtop >= 0) { - auto iBlock = (_curHistory == _migrated ? _curBlock : (_migrated->blocks.size() - 1)); - auto block = _migrated->blocks[iBlock].get(); - auto iItem = (_curHistory == _migrated ? _curItem : (block->messages.size() - 1)); - auto view = block->messages[iItem].get(); - auto item = view->data(); + const auto processPainted = [&]( + not_null view, + int top, + int height) { + const auto item = view->data(); + const auto isSponsored = item->isSponsored(); + const auto isUnread = !item->out() + && item->unread() + && (item->history() == _history); + const auto withReaction = item->hasUnreadReaction(); + const auto yShown = [&](int y) { + return (_visibleAreaBottom >= y && _visibleAreaTop <= y); + }; + const auto markShown = isSponsored + ? view->markSponsoredViewed(_visibleAreaBottom - top) + : withReaction + ? yShown(top + context.reactionInfo->position.y()) + : isUnread + ? yShown(top + height) + : yShown(top + height / 2); + if (markShown) { + if (isSponsored) { + session().data().sponsoredMessages().view( + item->fullId()); + } else if (isUnread) { + readTill = item; + } + if (item->hasViews()) { + session().api().views().scheduleIncrement(item); + } + if (withReaction) { + readContents.insert(item); + } else if (item->isUnreadMention() + && !item->isUnreadMedia()) { + readContents.insert(item); + _widget->enqueueMessageHighlight(view); + } + } + session().data().reactions().poll(item, now); + _reactionsManager->recordCurrentReactionEffect( + item->fullId(), + QPoint(0, top)); + }; - auto top = mtop + block->y() + view->y(); - context.translate(0, -top); - p.translate(0, top); - if (context.clip.y() < view->height()) while (top < drawToY) { - context.reactionEffects - = _reactionsManager->currentReactionEffect(); + adjustCurrent(clip.top()); + + const auto drawToY = clip.y() + clip.height(); + + auto selfromy = itemTop(_dragSelFrom); + auto seltoy = itemTop(_dragSelTo); + if (selfromy < 0 || seltoy < 0) { + selfromy = seltoy = -1; + } else { + seltoy += _dragSelTo->height(); + } + + const auto hdrawtop = historyDrawTop(); + if (mtop >= 0) { + auto iBlock = (_curHistory == _migrated ? _curBlock : (_migrated->blocks.size() - 1)); + auto block = _migrated->blocks[iBlock].get(); + auto iItem = (_curHistory == _migrated ? _curItem : (block->messages.size() - 1)); + auto view = block->messages[iItem].get(); + auto top = mtop + block->y() + view->y(); + context.translate(0, -top); + p.translate(0, top); + if (context.clip.y() < view->height()) while (top < drawToY) { + const auto height = view->height(); + context.reactionInfo + = _reactionsManager->currentReactionPaintInfo(); + context.outbg = view->hasOutLayout(); + context.selection = itemRenderSelection( + view, + selfromy - mtop, + seltoy - mtop); + view->draw(p, context); + processPainted(view, top, height); + + top += height; + context.translate(0, -height); + p.translate(0, height); + + ++iItem; + if (iItem == block->messages.size()) { + iItem = 0; + ++iBlock; + if (iBlock == _migrated->blocks.size()) { + break; + } + block = _migrated->blocks[iBlock].get(); + } + view = block->messages[iItem].get(); + } + context.translate(0, top); + p.translate(0, -top); + } + if (htop >= 0) { + auto iBlock = (_curHistory == _history ? _curBlock : 0); + auto block = _history->blocks[iBlock].get(); + auto iItem = (_curHistory == _history ? _curItem : 0); + auto view = block->messages[iItem].get(); + auto top = htop + block->y() + view->y(); + context.clip = clip.intersected( + QRect(0, hdrawtop, width(), clip.top() + clip.height())); + context.translate(0, -top); + p.translate(0, top); + while (top < drawToY) { + const auto height = view->height(); + if (context.clip.y() < height && hdrawtop < top + height) { + context.reactionInfo + = _reactionsManager->currentReactionPaintInfo(); context.outbg = view->hasOutLayout(); context.selection = itemRenderSelection( view, - selfromy - mtop, - seltoy - mtop); + selfromy - htop, + seltoy - htop); view->draw(p, context); - _reactionsManager->recordCurrentReactionEffect( - item->fullId(), - QPoint(0, top)); - - const auto height = view->height(); - const auto middle = top + height / 2; - if (_visibleAreaBottom >= middle - && _visibleAreaTop <= middle) { - if (item->hasViews()) { - session().api().views().scheduleIncrement(item); - } - if (item->isUnreadMention() && !item->isUnreadMedia()) { - readMentions.insert(item); - _widget->enqueueMessageHighlight(view); - } - session().data().reactions().poll(item, now); - } - - top += height; - context.translate(0, -height); - p.translate(0, height); - - ++iItem; - if (iItem == block->messages.size()) { - iItem = 0; - ++iBlock; - if (iBlock == _migrated->blocks.size()) { - break; - } - block = _migrated->blocks[iBlock].get(); - } - view = block->messages[iItem].get(); - item = view->data(); + processPainted(view, top, height); } - context.translate(0, top); - p.translate(0, -top); - } - if (htop >= 0) { - auto iBlock = (_curHistory == _history ? _curBlock : 0); - auto block = _history->blocks[iBlock].get(); - auto iItem = (_curHistory == _history ? _curItem : 0); - auto view = block->messages[iItem].get(); - auto item = view->data(); - auto readTill = (HistoryItem*)nullptr; - auto top = htop + block->y() + view->y(); - context.clip = clip.intersected( - QRect(0, hdrawtop, width(), clip.top() + clip.height())); - context.translate(0, -top); - p.translate(0, top); - while (top < drawToY) { - const auto height = view->height(); - if (context.clip.y() < height && hdrawtop < top + height) { - context.reactionEffects - = _reactionsManager->currentReactionEffect(); - context.outbg = view->hasOutLayout(); - context.selection = itemRenderSelection( - view, - selfromy - htop, - seltoy - htop); - view->draw(p, context); - _reactionsManager->recordCurrentReactionEffect( - item->fullId(), - QPoint(0, top)); + top += height; + context.translate(0, -height); + p.translate(0, height); - const auto middle = top + height / 2; - const auto bottom = top + height; - if (_visibleAreaBottom >= bottom) { - if (!item->out() && item->unread()) { - readTill = item; - } - } - if (item->isSponsored() - && view->markSponsoredViewed( - _visibleAreaBottom - top)) { - session().data().sponsoredMessages().view( - item->fullId()); - } - if (_visibleAreaBottom >= middle - && _visibleAreaTop <= middle) { - if (item->hasViews()) { - session().api().views().scheduleIncrement(item); - } - if (item->isUnreadMention() - && !item->isUnreadMedia()) { - readMentions.insert(item); - _widget->enqueueMessageHighlight(view); - } - } - session().data().reactions().poll(item, now); + ++iItem; + if (iItem == block->messages.size()) { + iItem = 0; + ++iBlock; + if (iBlock == _history->blocks.size()) { + break; } - top += height; - context.translate(0, -height); - p.translate(0, height); - - ++iItem; - if (iItem == block->messages.size()) { - iItem = 0; - ++iBlock; - if (iBlock == _history->blocks.size()) { - break; - } - block = _history->blocks[iBlock].get(); - } - view = block->messages[iItem].get(); - item = view->data(); - } - context.translate(0, top); - p.translate(0, -top); - - if (readTill && _widget->doWeReadServerHistory()) { - session().data().histories().readInboxTill(readTill); + block = _history->blocks[iBlock].get(); } + view = block->messages[iItem].get(); } - - if (!readMentions.empty() && _widget->doWeReadMentions()) { - session().api().markContentsRead(readMentions); - } - - if (mtop >= 0 || htop >= 0) { - enumerateUserpics([&](not_null view, int userpicTop) { - // stop the enumeration if the userpic is below the painted rect - if (userpicTop >= clip.top() + clip.height()) { - return false; - } - - // paint the userpic if it intersects the painted rect - if (userpicTop + st::msgPhotoSize > clip.top()) { - if (const auto from = view->data()->displayFrom()) { - from->paintUserpicLeft( - p, - _userpics[from], - st::historyPhotoLeft, - userpicTop, - width(), - st::msgPhotoSize); - } else if (const auto info = view->data()->hiddenSenderInfo()) { - info->userpic.paint( - p, - st::historyPhotoLeft, - userpicTop, - width(), - st::msgPhotoSize); - } else { - Unexpected("Corrupt forwarded information in message."); - } - } - return true; - }); - - int dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top(); - //QDate lastDate; - //if (!_history->isEmpty()) { - // lastDate = _history->blocks.back()->messages.back()->data()->date.date(); - //} - - //// if item top is before this value always show date as a floating date - //int showFloatingBefore = height() - 2 * (_visibleAreaBottom - _visibleAreaTop) - dateHeight; - - - auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.); - enumerateDates([&](not_null view, int itemtop, int dateTop) { - // stop the enumeration if the date is above the painted rect - if (dateTop + dateHeight <= clip.top()) { - return false; - } - - const auto displayDate = view->displayDate(); - auto dateInPlace = displayDate; - if (dateInPlace) { - const auto correctDateTop = itemtop + st::msgServiceMargin.top(); - dateInPlace = (dateTop < correctDateTop + dateHeight); - } - //bool noFloatingDate = (item->date.date() == lastDate && displayDate); - //if (noFloatingDate) { - // if (itemtop < showFloatingBefore) { - // noFloatingDate = false; - // } - //} - - // paint the date if it intersects the painted rect - if (dateTop < clip.top() + clip.height()) { - auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity; - if (opacity > 0.) { - p.setOpacity(opacity); - const auto dateY = false // noFloatingDate - ? itemtop - : (dateTop - st::msgServiceMargin.top()); - if (const auto date = view->Get()) { - date->paint(p, context.st, dateY, _contentWidth, _isChatWide); - } else { - HistoryView::ServiceMessagePainter::PaintDate( - p, - context.st, - view->dateTime(), - dateY, - _contentWidth, - _isChatWide); - } - } - } - return true; - }); - p.setOpacity(1.); - - _reactionsManager->paint(p, context); - - p.translate(0, _historyPaddingTop); - _emojiInteractions->paint(p); - } + context.translate(0, top); + p.translate(0, -top); } + + enumerateUserpics([&](not_null view, int userpicTop) { + // stop the enumeration if the userpic is below the painted rect + if (userpicTop >= clip.top() + clip.height()) { + return false; + } + + // paint the userpic if it intersects the painted rect + if (userpicTop + st::msgPhotoSize > clip.top()) { + if (const auto from = view->data()->displayFrom()) { + from->paintUserpicLeft( + p, + _userpics[from], + st::historyPhotoLeft, + userpicTop, + width(), + st::msgPhotoSize); + } else if (const auto info = view->data()->hiddenSenderInfo()) { + info->userpic.paint( + p, + st::historyPhotoLeft, + userpicTop, + width(), + st::msgPhotoSize); + } else { + Unexpected("Corrupt forwarded information in message."); + } + } + return true; + }); + + const auto dateHeight = st::msgServicePadding.bottom() + + st::msgServiceFont->height + + st::msgServicePadding.top(); + //QDate lastDate; + //if (!_history->isEmpty()) { + // lastDate = _history->blocks.back()->messages.back()->data()->date.date(); + //} + + //// if item top is before this value always show date as a floating date + //int showFloatingBefore = height() - 2 * (_visibleAreaBottom - _visibleAreaTop) - dateHeight; + + auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.); + enumerateDates([&](not_null view, int itemtop, int dateTop) { + // stop the enumeration if the date is above the painted rect + if (dateTop + dateHeight <= clip.top()) { + return false; + } + + const auto displayDate = view->displayDate(); + auto dateInPlace = displayDate; + if (dateInPlace) { + const auto correctDateTop = itemtop + st::msgServiceMargin.top(); + dateInPlace = (dateTop < correctDateTop + dateHeight); + } + //bool noFloatingDate = (item->date.date() == lastDate && displayDate); + //if (noFloatingDate) { + // if (itemtop < showFloatingBefore) { + // noFloatingDate = false; + // } + //} + + // paint the date if it intersects the painted rect + if (dateTop < clip.top() + clip.height()) { + auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity; + if (opacity > 0.) { + p.setOpacity(opacity); + const auto dateY = false // noFloatingDate + ? itemtop + : (dateTop - st::msgServiceMargin.top()); + if (const auto date = view->Get()) { + date->paint(p, context.st, dateY, _contentWidth, _isChatWide); + } else { + HistoryView::ServiceMessagePainter::PaintDate( + p, + context.st, + view->dateTime(), + dateY, + _contentWidth, + _isChatWide); + } + } + } + return true; + }); + p.setOpacity(1.); + + _reactionsManager->paint(p, context); + + p.translate(0, _historyPaddingTop); + _emojiInteractions->paint(p); } bool HistoryInner::eventHook(QEvent *e) { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 40a694c46..ef535048b 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -337,6 +337,12 @@ bool HistoryItem::isUnreadMention() const { } bool HistoryItem::hasUnreadReaction() const { + const auto &recent = recentReactions(); + for (const auto &[emoji, list] : recent) { + if (ranges::contains(list, true, &Data::RecentReaction::unread)) { + return true; + } + } return false; } @@ -901,10 +907,10 @@ const base::flat_map &HistoryItem::reactions() const { } auto HistoryItem::recentReactions() const --> const base::flat_map>> & { +-> const base::flat_map> & { static const auto kEmpty = base::flat_map< QString, - std::vector>>(); + std::vector>(); return _reactions ? _reactions->recent() : kEmpty; } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 83f13960c..8f1a285a4 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -41,6 +41,7 @@ struct RippleAnimation; namespace Data { struct MessagePosition; +struct RecentReaction; class Media; class MessageReactions; } // namespace Data @@ -367,7 +368,9 @@ public: void updateReactionsUnknown(); [[nodiscard]] const base::flat_map &reactions() const; [[nodiscard]] auto recentReactions() const - -> const base::flat_map>> &; + -> const base::flat_map< + QString, + std::vector> &; [[nodiscard]] bool canViewReactions() const; [[nodiscard]] QString chosenReaction() const; [[nodiscard]] QString lookupHisReaction() const; diff --git a/Telegram/SourceFiles/history/history_unread_things.h b/Telegram/SourceFiles/history/history_unread_things.h index 3c0d0ac05..387cb58a4 100644 --- a/Telegram/SourceFiles/history/history_unread_things.h +++ b/Telegram/SourceFiles/history/history_unread_things.h @@ -108,7 +108,8 @@ public: known) , _history(history) , _data(data) - , _type(type) { + , _type(type) + , _known(known) { } void setCount(int count); diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp index a6603f317..72a8d8661 100644 --- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp +++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp @@ -320,7 +320,7 @@ void BottomInfo::paintReactions( ? _reactionAnimation->playingAroundEmoji() : QString(); if (_reactionAnimation - && context.reactionEffects + && context.reactionInfo && animated.isEmpty()) { _reactionAnimation = nullptr; } @@ -354,7 +354,7 @@ void BottomInfo::paintReactions( p.drawImage(image.topLeft(), reaction.image); } if (animating) { - context.reactionEffects->paint = [=](QPainter &p) { + context.reactionInfo->effectPaint = [=](QPainter &p) { return _reactionAnimation->paintGetArea(p, origin, image); }; } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index b397d68eb..2079d2652 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1746,8 +1746,8 @@ void ListWidget::paintEvent(QPaintEvent *e) { p.translate(0, top); for (auto i = from; i != to; ++i) { const auto view = *i; - context.reactionEffects - = _reactionsManager->currentReactionEffect(); + context.reactionInfo + = _reactionsManager->currentReactionPaintInfo(); context.outbg = view->hasOutLayout(); context.selection = itemRenderSelection(view); view->draw(p, context); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 547e30282..7a28cd56d 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -729,8 +729,8 @@ void Message::draw(Painter &p, const PaintContext &context) const { const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip); p.translate(reactionsPosition); _reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition)); - if (context.reactionEffects && context.reactionEffects->paint) { - context.reactionEffects->offset += reactionsPosition; + if (context.reactionInfo) { + context.reactionInfo->position = reactionsPosition; } p.translate(-reactionsPosition); } @@ -784,8 +784,8 @@ void Message::draw(Painter &p, const PaintContext &context) const { const auto reactionsPosition = QPoint(trect.left(), trect.top() + trect.height() + reactionsTop); p.translate(reactionsPosition); _reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition)); - if (context.reactionEffects && context.reactionEffects->paint) { - context.reactionEffects->offset += reactionsPosition; + if (context.reactionInfo) { + context.reactionInfo->position = reactionsPosition; } p.translate(-reactionsPosition); } @@ -842,10 +842,12 @@ void Message::draw(Painter &p, const PaintContext &context) const { media->draw(p, context.translated( -mediaPosition ).withSelection(skipTextSelection(context.selection))); - if (context.reactionEffects - && context.reactionEffects->paint - && !_reactions) { - context.reactionEffects->offset += mediaPosition; + if (context.reactionInfo && !displayInfo && !_reactions) { + const auto add = QPoint(0, mediaHeight); + context.reactionInfo->position = mediaPosition + add; + if (context.reactionInfo->effectPaint) { + context.reactionInfo->effectOffset -= add; + } } p.translate(-mediaPosition); } @@ -876,6 +878,13 @@ void Message::draw(Painter &p, const PaintContext &context) const { inner.top() + inner.height(), 2 * inner.left() + inner.width(), InfoDisplayType::Default); + if (context.reactionInfo && !_reactions) { + const auto add = QPoint(0, inner.top() + inner.height()); + context.reactionInfo->position = add; + if (context.reactionInfo->effectPaint) { + context.reactionInfo->effectOffset -= add; + } + } if (_comments) { const auto o = p.opacity(); p.setOpacity(0.3); @@ -901,8 +910,12 @@ void Message::draw(Painter &p, const PaintContext &context) const { media->draw(p, context.translated( -g.topLeft() ).withSelection(skipTextSelection(context.selection))); - if (context.reactionEffects && context.reactionEffects->paint && !_reactions) { - context.reactionEffects->offset += g.topLeft(); + if (context.reactionInfo && !_reactions) { + const auto add = QPoint(0, g.height()); + context.reactionInfo->position = g.topLeft() + add; + if (context.reactionInfo->effectPaint) { + context.reactionInfo->effectOffset -= add; + } } p.translate(-g.topLeft()); } diff --git a/Telegram/SourceFiles/history/view/history_view_react_button.cpp b/Telegram/SourceFiles/history/view/history_view_react_button.cpp index dc4a47f91..4453ae48e 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_button.cpp +++ b/Telegram/SourceFiles/history/view/history_view_react_button.cpp @@ -781,9 +781,9 @@ void Manager::paint(Painter &p, const PaintContext &context) { } for (const auto &[id, effect] : _collectedEffects) { - const auto offset = effect.offset; + const auto offset = effect.effectOffset; p.translate(offset); - _activeEffectAreas[id] = effect.paint(p).translated(offset); + _activeEffectAreas[id] = effect.effectPaint(p).translated(offset); p.translate(-offset); } _collectedEffects.clear(); @@ -1543,17 +1543,19 @@ std::optional Manager::lookupEffectArea(FullMsgId itemId) const { void Manager::startEffectsCollection() { _collectedEffects.clear(); - _currentEffect = {}; + _currentReactionInfo = {}; } -not_null Manager::currentReactionEffect() { - return &_currentEffect; +auto Manager::currentReactionPaintInfo() +-> not_null { + return &_currentReactionInfo; } void Manager::recordCurrentReactionEffect(FullMsgId itemId, QPoint origin) { - if (_currentEffect.paint) { - _currentEffect.offset += origin; - _collectedEffects[itemId] = base::take(_currentEffect); + if (_currentReactionInfo.effectPaint) { + _currentReactionInfo.effectOffset += origin + + _currentReactionInfo.position; + _collectedEffects[itemId] = base::take(_currentReactionInfo); } else if (!_collectedEffects.empty()) { _collectedEffects.remove(itemId); } diff --git a/Telegram/SourceFiles/history/view/history_view_react_button.h b/Telegram/SourceFiles/history/view/history_view_react_button.h index cc9c056fd..f1a7fc05f 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_button.h +++ b/Telegram/SourceFiles/history/view/history_view_react_button.h @@ -173,8 +173,8 @@ public: [[nodiscard]] std::optional lookupEffectArea( FullMsgId itemId) const; void startEffectsCollection(); - [[nodiscard]] auto currentReactionEffect() - -> not_null; + [[nodiscard]] auto currentReactionPaintInfo() + -> not_null; void recordCurrentReactionEffect(FullMsgId itemId, QPoint origin); bool showContextMenu( @@ -354,8 +354,8 @@ private: base::flat_map _activeEffectAreas; - Ui::ReactionEffectPainter _currentEffect; - base::flat_map _collectedEffects; + Ui::ReactionPaintInfo _currentReactionInfo; + base::flat_map _collectedEffects; base::unique_qptr _menu; rpl::event_stream _faveRequests; diff --git a/Telegram/SourceFiles/history/view/history_view_reactions.cpp b/Telegram/SourceFiles/history/view/history_view_reactions.cpp index 0ad430e9f..f495bf3bc 100644 --- a/Telegram/SourceFiles/history/view/history_view_reactions.cpp +++ b/Telegram/SourceFiles/history/view/history_view_reactions.cpp @@ -274,11 +274,11 @@ void InlineList::paint( const auto size = st::reactionInlineSize; const auto skip = (size - st::reactionInlineImage) / 2; const auto inbubble = (_data.flags & InlineListData::Flag::InBubble); - const auto animated = (_animation && context.reactionEffects) + const auto animated = (_animation && context.reactionInfo) ? _animation->playingAroundEmoji() : QString(); const auto flipped = (_data.flags & Data::Flag::Flipped); - if (_animation && context.reactionEffects && animated.isEmpty()) { + if (_animation && context.reactionInfo && animated.isEmpty()) { _animation = nullptr; } p.setFont(st::semiboldFont); @@ -342,7 +342,7 @@ void InlineList::paint( p.drawImage(image.topLeft(), button.image); } if (animating) { - context.reactionEffects->paint = [=](QPainter &p) { + context.reactionInfo->effectPaint = [=](QPainter &p) { return _animation->paintGetArea(p, QPoint(), image); }; } @@ -480,7 +480,12 @@ InlineListData InlineListDataFromMessage(not_null message) { return true; }(); if (showUserpics) { - result.recent = recent; + result.recent.reserve(recent.size()); + for (const auto &[emoji, list] : recent) { + result.recent.emplace(emoji).first->second = list + | ranges::view::transform(&Data::RecentReaction::peer) + | ranges::to_vector; + } } result.chosenReaction = item->chosenReaction(); if (!result.chosenReaction.isEmpty()) { diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index b3f71d6de..b16f1849f 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -118,12 +118,12 @@ dialogsToUp: TwoIconButton(historyToDown) { } historyUnreadMentions: TwoIconButton(historyToDown) { - iconAbove: icon {{ "history_unread_mention", historyToDownFg, point(16px, 16px) }}; - iconAboveOver: icon {{ "history_unread_mention", historyToDownFgOver, point(16px, 16px) }}; + iconAbove: icon {{ "history_unread_mention", historyToDownFg }}; + iconAboveOver: icon {{ "history_unread_mention", historyToDownFgOver }}; } historyUnreadReactions: TwoIconButton(historyToDown) { - iconAbove: icon {{ "history_unread_reaction", historyToDownFg, point(16px, 16px) }}; - iconAboveOver: icon {{ "history_unread_reaction", historyToDownFgOver, point(16px, 16px) }}; + iconAbove: icon {{ "history_unread_reaction", historyToDownFg }}; + iconAboveOver: icon {{ "history_unread_reaction", historyToDownFgOver }}; } historyUnreadThingsSkip: 4px; diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 07755c250..72d2f891e 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -90,15 +90,16 @@ struct MessageImageStyle { style::icon historyVideoMessageMute = { Qt::Uninitialized }; }; -struct ReactionEffectPainter { - QPoint offset; - Fn paint; +struct ReactionPaintInfo { + QPoint position; + QPoint effectOffset; + Fn effectPaint; }; struct ChatPaintContext { not_null st; const BubblePattern *bubblesPattern = nullptr; - ReactionEffectPainter *reactionEffects = nullptr; + ReactionPaintInfo *reactionInfo = nullptr; QRect viewport; QRect clip; TextSelection selection;