From 1cb762913c60842192e4e81cfbfb52073f3aa93c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 01:54:16 +0300 Subject: [PATCH 01/41] Fixed text of confirm box in giveaway box. --- .../info/boosts/create_giveaway_box.cpp | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index c1781ea03..c22904262 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -237,6 +237,11 @@ void CreateGiveawayBox( randomWrap->toggle(type == GiveawayType::Random, anim::type::instant); }, randomWrap->lifetime()); + randomWrap->toggleOn( + state->typeValue.value( + ) | rpl::map(rpl::mappers::_1 == GiveawayType::Random), + anim::type::instant); + const auto sliderContainer = randomWrap->entity()->add( object_ptr(randomWrap)); const auto fillSliderContainer = [=] { @@ -574,18 +579,19 @@ void CreateGiveawayBox( box->setStyle(stButton); auto button = object_ptr( box, - state->toAwardAmountChanged.events_starting_with( - rpl::empty_value() - ) | rpl::map([=] { - return (typeGroup->value() == GiveawayType::SpecificUsers) - ? tr::lng_giveaway_award() - : tr::lng_giveaway_start(); - }) | rpl::flatten_latest(), + rpl::conditional( + state->typeValue.value( + ) | rpl::map(rpl::mappers::_1 == GiveawayType::Random), + tr::lng_giveaway_start(), + tr::lng_giveaway_award()), st::giveawayGiftCodeStartButton); button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); - button->resizeToWidth(box->width() - - stButton.buttonPadding.left() - - stButton.buttonPadding.right()); + state->typeValue.value( + ) | rpl::start_with_next([=, raw = button.data()] { + raw->resizeToWidth(box->width() + - stButton.buttonPadding.left() + - stButton.buttonPadding.right()); + }, button->lifetime()); button->setClickedCallback([=] { if (state->confirmButtonBusy) { return; From 4592e4e4bb3ddfa8acbf6e3c7647224192f8602d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 01:23:52 +0300 Subject: [PATCH 02/41] Slightly improved style of boosts info. --- .../icons/boosts/boost_unclaimed.png | Bin 0 -> 613 bytes .../icons/boosts/boost_unclaimed@2x.png | Bin 0 -> 1050 bytes .../icons/boosts/boost_unclaimed@3x.png | Bin 0 -> 1484 bytes .../Resources/icons/boosts/boost_unknown.png | Bin 0 -> 621 bytes .../icons/boosts/boost_unknown@2x.png | Bin 0 -> 1187 bytes .../icons/boosts/boost_unknown@3x.png | Bin 0 -> 1760 bytes Telegram/Resources/icons/boosts/mini_gift.png | Bin 0 -> 386 bytes .../Resources/icons/boosts/mini_gift@2x.png | Bin 0 -> 630 bytes .../Resources/icons/boosts/mini_gift@3x.png | Bin 0 -> 841 bytes .../Resources/icons/boosts/mini_giveaway.png | Bin 0 -> 368 bytes .../icons/boosts/mini_giveaway@2x.png | Bin 0 -> 648 bytes .../icons/boosts/mini_giveaway@3x.png | Bin 0 -> 864 bytes .../info/boosts/info_boosts_inner_widget.cpp | 33 ++++++++++++++++-- .../info_statistics_list_controllers.cpp | 5 +++ .../SourceFiles/statistics/statistics.style | 12 ++++--- 15 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 Telegram/Resources/icons/boosts/boost_unclaimed.png create mode 100644 Telegram/Resources/icons/boosts/boost_unclaimed@2x.png create mode 100644 Telegram/Resources/icons/boosts/boost_unclaimed@3x.png create mode 100644 Telegram/Resources/icons/boosts/boost_unknown.png create mode 100644 Telegram/Resources/icons/boosts/boost_unknown@2x.png create mode 100644 Telegram/Resources/icons/boosts/boost_unknown@3x.png create mode 100644 Telegram/Resources/icons/boosts/mini_gift.png create mode 100644 Telegram/Resources/icons/boosts/mini_gift@2x.png create mode 100644 Telegram/Resources/icons/boosts/mini_gift@3x.png create mode 100644 Telegram/Resources/icons/boosts/mini_giveaway.png create mode 100644 Telegram/Resources/icons/boosts/mini_giveaway@2x.png create mode 100644 Telegram/Resources/icons/boosts/mini_giveaway@3x.png diff --git a/Telegram/Resources/icons/boosts/boost_unclaimed.png b/Telegram/Resources/icons/boosts/boost_unclaimed.png new file mode 100644 index 0000000000000000000000000000000000000000..fb093176f775d81894b166cff059cb05aa78bbd8 GIT binary patch literal 613 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgf?7XLoV~B;| z*%0e~#z2vIGZ###P|0vH`q?#8!t=#c|0hvNZWjKHH+g0febMdlL#HL zgFZ`xrk`fjjb0n(%He4>w{IHPLG7trUh{m^E@YWVRq!nMwCD9Cm5ret+e5S*yG8ch zew!ltRQzO$kyoYEwX)qudlp^P$Yb7RHd}YDpZ)P`uS?5ZcQh7Q^z59_moQoG?D5CTFH3gc-E}ulMTnDyDTIsrOxk9>>E3+@ kX3sqT{A<Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NFgh@m}R9Fe^SUX57Q4r1Z6H$ct zdx=6CMFa`4P!Un`QEY@%Drl`1X#$c$f{?;O1v|fuov4km608g&h~Ni=1S`Q0DO8>| ziX{H<9lWq??(W8&yHATpZV~3rojG$d_cwPPNyKKu-nkkP%@d6F-a4_((m^RgOeRc zMn=-&Yjj3ON6F9v4lbAL_4QSQLz?>S?X9!3Qy>J*Wq*G^Eo{cl;o%_-ooe0O+?;X(*tGSM>h^71k}JF7py+}s>o z^xKPA=H}+^?CfY{Bu%}$yPKDnClZ6|W3^gw1g8a8bdHaYZ8jSfS==5g19OMzP%!y` za$#YCy4xixh6=RG%F5y4;g*&bcKYE^Kw_4bmaea_r2tvszt4d98p6GX^8WW{tEi|z zrta?U{(Lr+`Y3RJD0j8BwV_aGdwZKJ@8#PKL(yn77z|o0mT#oh)YP=TzW)CHE{~l- z_IkbF#ipdBWNmFNLu}>H=H@12)8F5}y1MFiyS2zwRaMQ*%uG&B>aA^1s3#H$Wys8$ za5xMG<>lpgslgKOf}WlpB`3Sxes*@opausAl?J?~x3^d48-wFFJv|Mq{QP_zl59xH zNzMi~9UUEfc&^{q*C)mX=<#@*-K?moA0Ho;oH#ZU6BAr?zFSvU_w@9{fW&^s$Hy6{ zk`tK?YK((}12GX_aR^}nAO^Q?0IS@0JQdb&Z*Mn9LUPw)BUN^~ICXV(Nr*;- zjg1Y}++f`(_nmrfm=<5@9v&WyP=PPb!_v}H0CF=WtzsG{pU;OErx8wzi;L{VW_C6hPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS=Hc3Q5RA>e5T1hB&Q5gQs^ROV1 z4Pj@Bg_O)JEl8#Xl48L^VWm()iUqbLB4e@^B~eOIW-=#H#*p!U>ik`|^WEWm-#yo- zzxyq|-gC}-p7(v;bMBe{{4st;1dIq65ila~9RZtfQBTRn#>U^@9~LeyF226L^fdJ& z*lB5LqobqS+uJWMFW`e8gq@w8v9Ym?j0~{-5ihtJDJd!0+S(#yVPRohya7l^NC+1wKEyD6aq$Kq z!NI{?p!lrTo}QjuTwJ&~^!)sc`A|C)pBB5hxzX+lLEr_&huGoaAunP_M@L+s3}T95 zTv}REEH|T&Ba|6de)Jw4^Dn3d0`D?U6t3=9mIWmr;qaBvU- zk}R@XCgvZ|Q*IFDQZ&~u-Ie9$<|@0cTv||2V6|pZHQd$JrR=?2X?J(GD!4Tj?Ck7X zTU$RrKjjEW=67^-*xTD%({D!GSy@@Px3`iUs7wqM+1c5Qu)jhL$D@snjpTTgB14jw zmzT}W%>e-cI=Rbd5iIANgM$NFFCroW%YU*1z={d$ul4oy>FMd$*H^iUM!pdNBLYSQ ztP}ygMnxYVpSZX<>h6_R^rZV7RMgx>FMq5?W?P+ zjEfJbK^awWUB>IwgxOyPMJ(-mdwX%DBRLndWa5s7;~5@<%W5i7QBlLg!|(6!EZ)oM zpxw}K)lB_Vxwp5s#_JVK|5ZxPKBHC@n2jts<=eAZ)rA#|FWkvJf2|O^hWZ zB#1Rtxlyc8cXoDG+Hd7t08LFziEVR{Q&LihT?|4WA0J{&1#YISP_eJCPnm*5siUJK zmgHiqyyW`&dchtxI~1%WH%V4!XJ>(dfdUkhfF174%nW6cR}wEj#FJQCTN{N;ag$?Z z$yrcCLjz@uZ?dzqQygFgZai7V#KgS4z42v2phCcCIKrHU1?C@?5wnMJ2^R_ zZ0na~VTD$r9I&w6?(Xg=Af1wUd8*n2$!MW4cQbWDqO`EEpc4}cwx*^=a7*&L5|4o# zxK&qIQy%D%#E}j*i-edeE25^A8y*mjkB{p?MvPfiRV6eRZfnIwkkM9>?~o+9MMg#n zP$~kr<>lpJ!-6d{W>3m0mn8O=#EJ0ma0-xOMO~wra+{c#AW-VpZLoyL%lM%J?)BW< z+?<@8RDxk0*_9G$%e5Oo^RqHaDILs8xh7T mhMExpBLYSQj0pS>5%>$2^cAY3koxri0000xjhl>iA zzD&uv*J{**29Tkc-WeYcGg(PNc=51 znPKwn_h0>sQhe=dlRMv~Etu=)u2RIm`>tNI)097T|5tZ4urM(;9VpBaD&2ioh3~@C zqKT?e8GGX<-(`?gZCVtxa@pmVuivF@mh@t2WUREA_q0g!TGZOGxb@Q=19cAeWo`Xc z8ND^iH%I2#rrU3U5)=Q-b~2iIC&x_UTZN4n7wgtv$1~;~bZNi*^2^T}zINyJ*WZ7y zp3wNEYVX~=@RcD3fl3n_WZv1XWj6Y@>2BUpImOKDCe0-Rj5`pe1H4l&+*)ot!u(wSJ>2DIKg1;wfu6ESkjj#MV6fce2ebp zO-_-lu$!N2CY{3;xBj}RRIiJMQ0EB_uguLiTmDWzz4U6<$3K5}%=35o;J;|?ga>Jd zSY%?%X77z#|7l&)#uX+_Jo3G6OpzO6I_C2qPuiGbBq_7UZ~5eUmsy9!|7dwH4bl*q rb^iIKN3$ahB#wk0*na=X-_+mcmbW)8h|4qq#WaJbtDnm{r-UW|S}*(@ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/boosts/boost_unknown@2x.png b/Telegram/Resources/icons/boosts/boost_unknown@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ad22dc971e1d3320df4b69ab116419c245f16da5 GIT binary patch literal 1187 zcmV;U1YG-xP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NG4M{{nR9Fe^SW75wQ562YnRs8X zGVlm_ha{3hN*O2y%18`Qijv6GNK8CN1|pB7$iPJ2iAa)|F!0Xf5+(1~{j#s!Ir~57 z@2qo9cXFMHwfA1@`_^82@3q#?V)^-gB48{6cE-#t-rnBv@$n%cA;38}IM~|Sy1u?P zh^f;&CMKr8zyIm!=~MDOKR*u)48+C7=>*knR$gBI`1mOCL?*(^%F0UJ;AF$v+FBXE z%Ald4fefv~ATu-b_4QT72if%7+go;awhjuK$@22DjMnC0ZEcN)PSs9HNwMjG!eUxl z8i~c8q@9(OMbd{dj+kUZb0{=4l!OjtmZG#<(HwGfbJGsZG+kU=NG3Fg_V@Qm=ukd9 zJS3UW9NO90A)!NAhY5kg+1dI0{9NHJsZ3p5T)>1tV6o65^z!o3(b1uuptG|RCffD& zXgWAJ%+1ZIL_{{du(06h=%|N-YUAhUhZHO$w;HUkulxJ^Q<3%S<7VLOa2!^id}6Y@ zyPNvjxhsYmw19wsii(QV)Kr%JkQ5M@o}QkwvolU02-rzvWTc0O$L{X#<>jTY`cH>p zVPO*!6VguM`1rW0s>;d9>7V++$Z~RWgx->$HQEOq1;EfAe0+RPPfxiA5Z>S4rNi;@ zaZaHBSz=pg?|*%LzP`Rdyt=yj{q>>MM}x~ba4tbXK^T5~e4yLx>+5@Xct}V{Xl-p} z9b92yVPaz9=H@0hAqe5&;f;-r)6>&xM}VzkX=Y}ouC9*ZPEJm;cnl5>N|{_;UHA6( z*iu_tn{-K&oSd9IIyx#uTN-&mMv-B-v$L~osivkzU}|Y;VN1xO!ji3_r>E!8(2&5V zWr(~0+`PQJlamu%UR)-Drm3lk;Sic#95ddh7J6%)uC6Y@1L+XSb9;N6S@rhzN|#jS zqM{;c=CG!;J%d|}L*WHKI5;>qHkOgWo3XJmZl0f?uj&L}C@wBG;teOey}d;}MiM_d zI%0?D?(QzF(9&nm*+n5DBI5r3o`aequrMGtD=RA;5Vyz6%S)se#^^CtS68`>DZ=97 zB4a^S=796_^N5zft}rYqDd9F|31wwvU_wRX*I;UDN^4WFw6tWF9~=ia8V(_;vKb^# z#fC|tg+(#Q$jDHLo3Sa}NB~4dMKPF>kr8ysBmN(8yk0bcUs-otj96!?f;V6_Dlv`P*8whoP2Yp2Evzwb6lxhg=?d|pT^<*H^ z4S0^me<@7|*8_;(bj{7prKP2W3#5nV$L2qdz#l8be;Q-Yh&uoP002ovPDHLkV1n&* BG;sg` literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/boosts/boost_unknown@3x.png b/Telegram/Resources/icons/boosts/boost_unknown@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..bbf4532a27ff43e74bbbd832645f06699b68dd3f GIT binary patch literal 1760 zcmV<61|Ru}P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS>N=ZaPRA>e5TU#t#T@)TgDOwt& zC=Ye1wxk+Fq(M+Th>*CYt%fvdn()R`pV|tMRy_1U`a(Ro1a)Z;4-)Y}(9|uhR#oGY zR#6mnzx%)B{MmNSoO9;P%$|c}=3%n-+UtDlTYK-b&szJ;s8q&}aRiJbU>pJC2po@q z$+2ii$;8CO+1VK$W@l%Qj*blJN)ha+sHk`E-i?lqZf|cR9(*8t|NgzMt}Z4f2GJ_Q zf~k|oj~{>g_Kgch?0al%?CH~|Ox+q1%*Mv1rlv+L7BO@ahFJ3eZFTGK#s;b0z%h6*^p2qDYIXPJl zduf}rv^2ekmo-3lcegZYIlX;-eXM!uhZh(aD2KNko4a@K>c{2(tIsh(dGzQJ6C8j% zc<_J;6q~@VU%$?T4M03SJ()nU2@H=fCTsxW?(WV6icPk*xw(1a!UZOTwzjtLJk%eG z&5SKCFY8Z*Ltq8PCa}rLNmhYPO-(U@G6p6&jJ>_RlKonAGKA8?tC%VB*VWZkInQ5d zoAveeOP4N*dC^MGW+|?%t(BLTYvowbxU#Ym10>jWVoE%J5T5jeNcUp8PH9z^nwlz2 zUAp(-!-pp}EgBuadGkh^y;yH?aj{15lQb|jHGTE!)#2fxSO9|hqM{-*GqaQAEz@^G zLc+?*ieL;pCANyBq$HWJr@{=!qq(`c-0>(c7_Rx}&!4=!JQo)i1JPxZ1dEX~H#f)Z z-M)Pr=|5KiAY(%Q_4)JXrlzLd-CeN@)%89;KDTb&a&U0i+1Z(!n?tR=u&|(Rc4jn} zFJFHC{JG%FBesl;4BXXcCKy6ABqU^ddRlNL6XnN`9~qXp#I*s1;NW1Cc_aV{VSawz z&(F_*R2V{|*8KJBmqakUu8D~W-Z}XxU7*?7*}TWV-@kwHN}!>kp})T$#i)me$GLOI zi7swM3k!?3wl*@jhT0XeePJzKxpKwP(GfED4X=xfi`dP$Z|wLVK74>o*brf0V8Gws zpEO%rTXB~9@83V7!I=*Zo`Zsdh-PDBOfSHQ7HbaZrKVd3E5pv>e0W-!Jj zcwI)453ECarGc@%y}dd)D1ZL^Nn0i+CaRn1r9o={_U&7l_c~35mT^eVk-##e^_W#L zaxY)Lq%D!D63gRdN>o}2ckbM&uCB%_yv)o@JrAnLJ%jp%<}frgL^o*}OO$S6!P8A0 zWji}NJzuZTgAEzOPpFiYm2tgDCXsUxJx{oK^Ck^~_cc5Nt(*7m-P3A2rrnFlKYskc z&PJ%j#>V1BuA7@1(Wnbgo;;z^Dk>_}ZRBc@{$VN0+#6`Ga^%;qU(4m8Qenv;^Q2*Q ze}#ak-oJlOW3iPF4i0j1a>&g_f<&#r%L#FO)X~vFd*9pJ6Wlxrh^TP>{CTA0G?szn zuCA{5_;@@6!9FrFGCw~b86#cbV3WXOn&%y6qSPfi@Q`U~Y2jfh)5Nlmi;E-NQ2t;O z=DEfzZ2JBr#+n7x4m>Qyn)K5NxyA3_zj>~qxpCu$<`~p10s;cCf_ZStG;ySdjEtj% zIYE)a^NX@o$2U)wmX?@k9-LxL93PN@E+{DA`Tg?ci>s?EG0;xP%F5!wDcMAN#nHva zh@_ErjoHJFuU#;-_1CXoX*eZiR9S?{ix)3w*E2ISPEJmwS8E|6B7%lfSS~3kA&h%^ zdT8(I>FK0PP|%PBr=+9^UZ#u^(?T54#Xr_$ZEY>-5)?E9=I!k*c$qRvy4uGEMw~V` zH{%eFG<6Ypl^neo6p)c#5YoQBzQe=AaN60~sn^}bA)?Y&AOHy$p`oE9HVCB3rj@$L zNZTa5D$v#5-mVL8l}f`sR$pJQ8!oG>@K7dD_|cz^2_b}r7F#V(GaL`PvosW3NV@mPDkkG8J00!YM z4Y1~O2@v_(c|^%X$6=8`t89nsjfRMB!Rig-tlt(~(3qfcEdN}^V>{zXn(D>f(~Hm9 ze*ez$@cP14t1@SOl4DpPFWr&8PQz>7^U_%FNi4Oo;l7u1ZqHlRnRA|{HBsWQ!H<3M zp`nq}7#8eWH<{5v?U99_0L#4R3sWzfWXQKIe7$SNwNlw*lRmR<(z)HY<89#vUd^J< zQ*Uik6?G8#S@~Qf{9(tUj=9kytXVpz4|U&tWO2;oTAz4>{^>(plT;Xw@~&Z+f96ib zPcM~P_I%dQHt~1mA4NOPT2_0%|8d2AW(JW*TTjPp^7E=xcCU?1p9k{4r>mdKI;Vst E04`I7Gynhq literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/boosts/mini_gift@2x.png b/Telegram/Resources/icons/boosts/mini_gift@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..462d3876453503215953ecbac8a2d423af4b9df8 GIT binary patch literal 630 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSVF5FO4N|zKE$K5**#l1(#}E(R zw;|_OGX{#x`!294NJBdKm8a6CP_Z?-IcK~yr%f^a-|{K^hq;b#rq`#%Yg9aY*O>hL zcyD_2<{kTAd<|Q_v-{xi(V_1=(0F)OAl+a0!L%O>Z>iPB1d&QXD;i^ z&=66Y^S7>lTH;{^4-1(#MSHe|47cv&n4NyQ>Hd4?OdGl6FT3yZiH3%L{r&f7(#Gqr z8yTcdY}2|pt#)tR`x-m-;~us9=XEo6?vBu@v6GMfKDqVO(;}^@Nrje^eAK*`3Y~t) z!NSBi?K|Hg-zOXo=IsgYd%9<~1P|Lsmz~Yi<}e0LII*g3gMh%CVztSIyI)$TNw6)d zI3FTbHUDhdwA4V0n0*XPi!Yvdy6At%TtD`O4+}g*cin$qF;&DPX5oRaRjK6@JX9vM z$+LxWTYspq0g9eeEUw(oCfhiDv2b5U^>de}Dk%#Oq!v>hnUJzf1=);T3K0RYrv B0G|K= literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/boosts/mini_gift@3x.png b/Telegram/Resources/icons/boosts/mini_gift@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..8858b4fff5eadb41c06732391e2ca5a73845bd23 GIT binary patch literal 841 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@HYGy98CN@`TZfS1S zVA_820;#p00 zPqVkN*|TE@N2`EcDftOp;DJ}$Iae);9=*V;TfKqU=cOPxMGfByXbr3+ubnu=Q3*-aDI z5aD{#uJ~QK=a0gvzd6yT+n%m)`}~__f!qH3(|>QisiUjfJdMNIAV=wxCQo&H+pDiu zso#IreCn~t-ZF(J>et`8tk#aAE-N!L_tjSq`7ZwO^5x1YBFYVNygVm68CnkVbG_B# zOp=~snPMV!_~C}zZ?C;9@krTx(?*V;C#0&TrlzXuP^J_Q+l^-hY)p(m9~TrPY`;C% zPyOWO43mNv4SHhTWxMBksJJZ_oEE>QXs66w+sE(k-jz*lKggl1G5`GL=!b6<-!Pli zhhKU9b(7|tHXb(S+55FuU4MO3#LqeBQ~3h>wF^I1tXao%r$weUFE8&(uEtECO~OUb z1Wrge@wF${*Z+_2Y&)E|HHx*WtjtaS?%Ii~udcfMGQls;RaJezVoTOm9mUK83Oti1 zv@M)cdpKhKb?JHfdQ0!yMn9i-=wOG6*Qw2Uv7GT=B`ym12Q8cs(6O9>|NqSD45OJJ z@_PEUeHM8v{QbMS&M~g#kV=ZWe_f`sE2q N#-6TzF6*2UngAg8W&QvF literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/boosts/mini_giveaway.png b/Telegram/Resources/icons/boosts/mini_giveaway.png new file mode 100644 index 0000000000000000000000000000000000000000..79ffc0f1d7c46c2fdb90c4027c4b557d927a0a13 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uuz(rC1}QWNE&K$eZh5*mhG>YM z4Y1~FH4rfAZ&;u%;BB4FZOyOngz28r1I?T-0cN*2nlx@@E}!ZYb>G3|(Q}5p%`@lD zJo_%|*7n0DXSd~+t1<-E%$#Lt#&dX+PS=l$MK`|JO0->aOF8vT>$ZuqY$)T5N5SjG=GvpSGQwb*k@uiTGs_;I(>{5l^##(Rt$s lo_p;rp1<;Fc)v?O^#AUy^Nnl13M>Qp*3;F`Wt~$(69B-_fBXOd literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/boosts/mini_giveaway@2x.png b/Telegram/Resources/icons/boosts/mini_giveaway@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bb5e94a656716224f92d6e22d505d845b91f70a7 GIT binary patch literal 648 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSVF5FO4N|zKE$K5**(XmI#}E(R zw;{f#83RSq;&P_6COynvtn!od4@2r}@f%SalistRIOX-PWsQckl9(@B68vJ(JKbu zLjx1`#wiQen+BgUnd+Gns~~V-;*6RKNk$$v<~_D@{tE*b4m`UUD5qwmv;FkbrnVUy zj%Mt+{4!xm#jn40yYH$+{Bh$7>&u8*D|S07%}_pP;*E*ouKugM8KS@XvZZdmx#?z( z(aatfrH2JK-jk8l^wX`6I1ih@zK~<)tkrz50U>wg z_16HY-GTwrSQW(o{P?jT`r+r#!nx-h6dX>>n?FBrg5Zf1BbU|!i=LJbstc@nKQtXw z2x3|hw)$hmo*2E!&0h>TggadpUsUkk6Q|$Y=%N(3F+%0_&Z9eH`VN|~h~A4@6My!~ z<>i+%ExZimIs`1{o{L($?QY)6>I)|irEd&cdE;%_t3U(!>%omdD_br%IW25BpJ+CF z@6Mf;PZN|4T8=+{`Th6lr$r`G2VV+$@7DV90$blze}DhGdF6KV<$If^bsuHoIGHl(uH>AX zXOAQqUiqQa;kNk9DsGmZ|ISu(`|9}j#`(`bGbwXpMBCwquK5iNCJLK#4xg@IhroZ9 ZAH<7d=W6Dbq;r5$x2LP0%Q~loCIIZ!bz%Sj literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index 494df7a99..7de9f4908 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_common.h" #include "statistics/widgets/chart_header_widget.h" #include "ui/boxes/boost_box.h" -#include "ui/controls/invite_link_buttons.h" #include "ui/controls/invite_link_label.h" #include "ui/rect.h" #include "ui/widgets/buttons.h" @@ -181,8 +180,35 @@ void FillShareLink( ) | rpl::start_with_next(copyLink, label->lifetime()); const auto copyShareWrap = content->add( object_ptr(content)); - Ui::AddCopyShareLinkButtons(copyShareWrap, copyLink, shareLink); - copyShareWrap->widgetAt(0)->showChildren(); + { + const auto wrap = content->add( + object_ptr( + content, + st::inviteLinkButton.height), + st::inviteLinkButtonsPadding); + const auto copy = CreateChild( + wrap, + tr::lng_group_invite_context_copy(), + st::inviteLinkCopy); + copy->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); + copy->setClickedCallback(copyLink); + const auto share = CreateChild( + wrap, + tr::lng_group_invite_context_share(), + st::inviteLinkShare); + share->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); + share->setClickedCallback(shareLink); + + wrap->widthValue( + ) | rpl::start_with_next([=](int width) { + const auto buttonWidth = (width - st::inviteLinkButtonsSkip) / 2; + copy->setFullWidth(buttonWidth); + share->setFullWidth(buttonWidth); + copy->moveToLeft(0, 0, width); + share->moveToRight(0, 0, width); + }, wrap->lifetime()); + wrap->showChildren(); + } ::Settings::AddSkip(content, st::boostsLinkFieldPadding.bottom()); } @@ -366,6 +392,7 @@ void InnerWidget::fill() { ::Settings::AddDividerText(inner, tr::lng_boosts_list_subtext()); } + ::Settings::AddSkip(inner); ::Settings::AddSkip(inner); AddHeader(inner, tr::lng_boosts_link_title); ::Settings::AddSkip(inner, st::boostsLinkSkip); diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 6d8c4307e..43606f195 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -476,6 +476,11 @@ PaintRoundImageCallback BoostRow::generatePaintUserpicCallback(bool force) { } return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { _userpic.paintCircle(p, x, y, outerWidth, size); + (_boost.isUnclaimed + ? st::boostsListUnclaimedIcon + : st::boostsListUnknownIcon).paintInCenter( + p, + { x, y, size, size }); }; } diff --git a/Telegram/SourceFiles/statistics/statistics.style b/Telegram/SourceFiles/statistics/statistics.style index 036fb16e5..8fce0f2b9 100644 --- a/Telegram/SourceFiles/statistics/statistics.style +++ b/Telegram/SourceFiles/statistics/statistics.style @@ -159,9 +159,11 @@ boostsListBadgeHeight: 16px; boostsListRightBadgeTextStyle: TextStyle(defaultTextStyle) { font: font(12px semibold); } -boostsListRightBadgeTextPadding: margins(16px, 1px, 6px, 0px); -boostsListRightBadgePadding: margins(4px, 5px, 8px, 0px); +boostsListRightBadgeTextPadding: margins(22px, 1px, 8px, 0px); +boostsListRightBadgePadding: margins(4px, 5px, 12px, 0px); boostsListRightBadgeHeight: 20px; -boostsListGiftMiniIconPadding: margins(1px, 2px, 0px, 0px); -boostsListGiftMiniIcon: icon{{ "boosts/boost_mini2", historyPeer8UserpicBg2 }}; -boostsListGiveawayMiniIcon: icon{{ "boosts/boost_mini2", historyPeer4UserpicBg2 }}; +boostsListGiftMiniIconPadding: margins(4px, 2px, 0px, 0px); +boostsListGiftMiniIcon: icon{{ "boosts/mini_gift", historyPeer8UserpicBg2 }}; +boostsListGiveawayMiniIcon: icon{{ "boosts/mini_giveaway", historyPeer4UserpicBg2 }}; +boostsListUnclaimedIcon: icon{{ "boosts/boost_unknown", premiumButtonFg }}; +boostsListUnknownIcon: icon{{ "boosts/boost_unclaimed", premiumButtonFg }}; From 3dd415d0c2c89ba4a98420ac3f95cae72ad9a9a8 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 01:58:49 +0300 Subject: [PATCH 03/41] Removed wheel support from slider in giveaway box. --- .../info/boosts/create_giveaway_box.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index c22904262..466584e81 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -279,8 +279,20 @@ void CreateGiveawayBox( const auto &padding = st::giveawayGiftCodeSliderPadding; Settings::AddSkip(sliderContainer, padding.top()); + + class Slider : public Ui::MediaSlider { + public: + using Ui::MediaSlider::MediaSlider; + + protected: + void wheelEvent(QWheelEvent *e) override { + e->ignore(); + } + + }; + const auto slider = sliderContainer->add( - object_ptr(sliderContainer, st::settingsScale), + object_ptr(sliderContainer, st::settingsScale), st::boxRowPadding); Settings::AddSkip(sliderContainer, padding.bottom()); slider->resize(slider->width(), st::settingsScale.seekSize.height()); From c9cabfb084815b261124b8d03a01f2847f967230 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 02:09:37 +0300 Subject: [PATCH 04/41] Fixed API support for different boosts lists. --- Telegram/SourceFiles/api/api_statistics.cpp | 1 - .../SourceFiles/info/boosts/info_boosts_inner_widget.cpp | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 3069843c1..25b0473fb 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -540,7 +540,6 @@ rpl::producer Boosts::request() { _boostStatus.firstSliceGifts = std::move(s); consumer.put_done(); }); - consumer.put_done(); }); }).fail([=](const MTP::Error &error) { consumer.put_error_copy(error.type()); diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index 7de9f4908..d956a47f1 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -365,10 +365,9 @@ void InnerWidget::fill() { object_ptr>( inner, object_ptr(inner))); - boostsWrap->toggle(hasOneTab ? true : hasBoosts, anim::type::instant); - giftsWrap->toggle(hasOneTab ? false : hasGifts, anim::type::instant); - slider->entity()->sectionActivated( + rpl::single(hasGifts ? 1 : 0) | rpl::then( + slider->entity()->sectionActivated() ) | rpl::start_with_next([=](int index) { boostsWrap->toggle(!index, anim::type::instant); giftsWrap->toggle(index, anim::type::instant); @@ -403,7 +402,7 @@ void InnerWidget::fill() { FillGetBoostsButton(inner, _controller, _show, _peer); resizeToWidth(width()); - crl::on_main([=]{ fakeShowed->fire({}); }); + crl::on_main(this, [=]{ fakeShowed->fire({}); }); } void InnerWidget::saveState(not_null memento) { From ee33d139cdcbd3c8885c30458ce18e3ed4131447 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 03:01:12 +0300 Subject: [PATCH 05/41] Improved style of tabs in boosts info. --- Telegram/Resources/langs/lang.strings | 4 ++-- .../info/boosts/info_boosts_inner_widget.cpp | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f3d56b3d0..979b9c783 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4325,8 +4325,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boosts_existing" = "Existing boosts"; "lng_boosts_premium_audience" = "Premium subscribers"; "lng_boosts_next_level" = "Boosts to level up"; -"lng_boosts_list_title#one" = "{count} booster"; -"lng_boosts_list_title#other" = "{count} boosters"; +"lng_boosts_list_title#one" = "{count} Boost"; +"lng_boosts_list_title#other" = "{count} Boosts"; "lng_boosts_list_subtext" = "Your channel is currently boosted by these users."; "lng_boosts_show_more_boosts#one" = "Show {count} More Boosts"; "lng_boosts_show_more_boosts#other" = "Show {count} More Boosts"; diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index d956a47f1..4a85789e6 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -352,11 +352,26 @@ void InnerWidget::fill() { inner, object_ptr( inner, - st::defaultTabsSlider))); + st::defaultTabsSlider)), + st::boxRowPadding); slider->toggle(!hasOneTab, anim::type::instant); + slider->entity()->addSection(boostsTabText); slider->entity()->addSection(giftsTabText); + { + const auto &st = st::defaultTabsSlider; + const auto sliderWidth = st.labelStyle.font->width(boostsTabText) + + st.labelStyle.font->width(giftsTabText) + + rect::m::sum::h(st::boxRowPadding); + fakeShowed->events() | rpl::take(1) | rpl::map_to(-1) | rpl::then( + slider->entity()->widthValue() + ) | rpl::distinct_until_changed( + ) | rpl::start_with_next([=](int) { + slider->entity()->resizeToWidth(sliderWidth); + }, slider->lifetime()); + } + const auto boostsWrap = inner->add( object_ptr>( inner, From a76534b19f17fc19d48806c1aa8784a4d70d0f0d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 03:15:58 +0300 Subject: [PATCH 06/41] Fixed opening of same boosts info multiple times. --- Telegram/SourceFiles/info/boosts/info_boosts_widget.cpp | 2 +- Telegram/SourceFiles/info/info_controller.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_widget.cpp index fac44f16a..5e4d8ed32 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_widget.cpp @@ -71,7 +71,7 @@ not_null Widget::peer() const { } bool Widget::showInternal(not_null memento) { - return false; + return (memento->statisticsPeer() == peer()); } rpl::producer Widget::title() { diff --git a/Telegram/SourceFiles/info/info_controller.cpp b/Telegram/SourceFiles/info/info_controller.cpp index 85758a9d7..48426de06 100644 --- a/Telegram/SourceFiles/info/info_controller.cpp +++ b/Telegram/SourceFiles/info/info_controller.cpp @@ -284,7 +284,8 @@ bool Controller::validateMementoPeer( return memento->peer() == peer() && memento->migratedPeerId() == migratedPeerId() && memento->settingsSelf() == settingsSelf() - && memento->storiesPeer() == storiesPeer(); + && memento->storiesPeer() == storiesPeer() + && memento->statisticsPeer() == statisticsPeer(); } void Controller::setSection(not_null memento) { From ebcd4eb97b825d827bfb7d2af625b1d59d7c12ad Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 05:35:28 +0300 Subject: [PATCH 07/41] Added box for pending giftcodes. --- Telegram/Resources/langs/lang.strings | 3 + .../SourceFiles/boxes/gift_premium_box.cpp | 238 +++++++++++++----- Telegram/SourceFiles/boxes/gift_premium_box.h | 4 + .../info/boosts/info_boosts_inner_widget.cpp | 20 +- 4 files changed, 202 insertions(+), 63 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 979b9c783..6f61de92d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2199,6 +2199,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_link_used_about" = "This link was used to activate\na **Telegram Premium** subscription."; "lng_gift_link_used_footer" = "This link was used on {date}."; "lng_gift_link_expired" = "Gift code link expired"; +"lng_gift_link_pending_about" = "This link allows {user} to activate\na **Telegram Premium** subscription."; +"lng_gift_link_pending_toast" = "Only the recipient can see the link."; +"lng_gift_link_pending_footer" = "This link hasn't been activated yet."; "lng_accounts_limit_title" = "Limit Reached"; "lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected accounts."; diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index 9a87a58fc..e7f2dab7b 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -7,9 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/gift_premium_box.h" -#include "apiwrap.h" #include "api/api_premium.h" #include "api/api_premium_option.h" +#include "apiwrap.h" #include "base/unixtime.h" #include "base/weak_ptr.h" #include "boxes/peers/prepare_short_info_box.h" @@ -31,7 +31,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/premium_graphics.h" #include "ui/effects/premium_stars_colored.h" #include "ui/effects/premium_top_bar.h" +#include "ui/effects/spoiler_mess.h" #include "ui/layers/generic_box.h" +#include "ui/rect.h" #include "ui/text/text_utilities.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/gradient_round_button.h" @@ -358,6 +360,73 @@ void AddTableRow( st::giveawayGiftCodePeerMargin); } +void AddTable( + not_null container, + not_null controller, + const Api::GiftCode ¤t, + bool skipReason) { + auto table = container->add( + object_ptr( + container, + st::giveawayGiftCodeTable), + st::giveawayGiftCodeTableMargin); + AddTableRow( + table, + tr::lng_gift_link_label_from(), + controller, + current.from); + if (current.to) { + AddTableRow( + table, + tr::lng_gift_link_label_to(), + controller, + current.to); + } else { + AddTableRow( + table, + tr::lng_gift_link_label_to(), + tr::lng_gift_link_label_to_unclaimed(Ui::Text::WithEntities)); + } + AddTableRow( + table, + tr::lng_gift_link_label_gift(), + tr::lng_gift_link_gift_premium( + lt_duration, + GiftDurationValue(current.months) | Ui::Text::ToWithEntities(), + Ui::Text::WithEntities)); + if (!skipReason) { + const auto reason = AddTableRow( + table, + tr::lng_gift_link_label_reason(), + (current.giveawayId + ? ((current.to + ? tr::lng_gift_link_reason_giveaway + : tr::lng_gift_link_reason_unclaimed)( + ) | Ui::Text::ToLink()) + : current.giveaway + ? ((current.to + ? tr::lng_gift_link_reason_giveaway + : tr::lng_gift_link_reason_unclaimed)( + Ui::Text::WithEntities + ) | rpl::type_erased()) + : tr::lng_gift_link_reason_chosen(Ui::Text::WithEntities))); + reason->setClickHandlerFilter([=](const auto &...) { + controller->showPeerHistory( + current.from, + Window::SectionShow::Way::Forward, + current.giveawayId); + return false; + }); + } + if (current.date) { + AddTableRow( + table, + tr::lng_gift_link_label_date(), + rpl::single(Ui::Text::WithEntities( + langDateTime(base::unixtime::parse(current.date))))); + } +} + } // namespace GiftPremiumValidator::GiftPremiumValidator( @@ -462,65 +531,7 @@ void GiftCodeBox( MakeLinkCopyIcon(box)), st::giveawayGiftCodeLinkMargin); - auto table = box->addRow( - object_ptr( - box, - st::giveawayGiftCodeTable), - st::giveawayGiftCodeTableMargin); - const auto current = state->data.current(); - AddTableRow( - table, - tr::lng_gift_link_label_from(), - controller, - current.from); - if (current.to) { - AddTableRow( - table, - tr::lng_gift_link_label_to(), - controller, - current.to); - } else { - AddTableRow( - table, - tr::lng_gift_link_label_to(), - tr::lng_gift_link_label_to_unclaimed(Ui::Text::WithEntities)); - } - AddTableRow( - table, - tr::lng_gift_link_label_gift(), - tr::lng_gift_link_gift_premium( - lt_duration, - GiftDurationValue(current.months) | Ui::Text::ToWithEntities(), - Ui::Text::WithEntities)); - const auto reason = AddTableRow( - table, - tr::lng_gift_link_label_reason(), - (current.giveawayId - ? ((current.to - ? tr::lng_gift_link_reason_giveaway - : tr::lng_gift_link_reason_unclaimed)( - ) | Ui::Text::ToLink()) - : current.giveaway - ? ((current.to - ? tr::lng_gift_link_reason_giveaway - : tr::lng_gift_link_reason_unclaimed)( - Ui::Text::WithEntities - ) | rpl::type_erased()) - : tr::lng_gift_link_reason_chosen(Ui::Text::WithEntities))); - reason->setClickHandlerFilter([=](const auto &...) { - controller->showPeerHistory( - current.from, - Window::SectionShow::Way::Forward, - current.giveawayId); - return false; - }); - if (current.date) { - AddTableRow( - table, - tr::lng_gift_link_label_date(), - rpl::single(Ui::Text::WithEntities( - langDateTime(base::unixtime::parse(current.date))))); - } + AddTable(box->verticalLayout(), controller, state->data.current(), false); auto shareLink = tr::lng_gift_link_also_send_link( ) | rpl::map([](const QString &text) { @@ -603,6 +614,113 @@ void GiftCodeBox( }, button->lifetime()); } + +void GiftCodePendingBox( + not_null box, + not_null controller, + const Api::GiftCode &data) { + box->setWidth(st::boxWideWidth); + box->setStyle(st::giveawayGiftCodeBox); + box->setNoContentMargin(true); + + { + const auto peerTo = controller->session().data().peer(data.to); + const auto clickContext = [=, weak = base::make_weak(controller)] { + if (const auto strong = weak.get()) { + strong->uiShow()->showBox( + PrepareShortInfoBox(peerTo, strong)); + } + return QVariant(); + }; + const auto &st = st::giveawayGiftCodeCover; + const auto resultToName = st.about.style.font->elided( + peerTo->shortName(), + st.about.minWidth / 2, + Qt::ElideMiddle); + const auto bar = box->setPinnedToTopContent( + object_ptr( + box, + st, + clickContext, + tr::lng_gift_link_title(), + tr::lng_gift_link_pending_about( + lt_user, + rpl::single(Ui::Text::Link(resultToName)), + Ui::Text::RichLangValue), + true)); + + const auto max = st::giveawayGiftCodeTopHeight; + bar->setMaximumHeight(max); + bar->setMinimumHeight(st::infoLayerTopBarHeight); + + bar->resize(bar->width(), bar->maximumHeight()); + } + + { + const auto linkLabel = box->addRow( + Ui::MakeLinkLabel(box, nullptr, nullptr, nullptr, nullptr), + st::giveawayGiftCodeLinkMargin); + const auto spoiler = Ui::CreateChild(linkLabel); + spoiler->lifetime().make_state([=] { + spoiler->update(); + })->start(); + linkLabel->sizeValue( + ) | rpl::start_with_next([=](const QSize &s) { + spoiler->setGeometry(Rect(s)); + }, spoiler->lifetime()); + const auto spoilerCached = Ui::SpoilerMessCached( + Ui::DefaultTextSpoilerMask(), + st::giveawayGiftCodeLink.textFg->c); + const auto textHeight = st::giveawayGiftCodeLink.style.font->height; + spoiler->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(spoiler); + const auto rect = spoiler->rect(); + const auto r = rect + - QMargins( + st::boxRowPadding.left(), + (rect.height() - textHeight) / 2, + st::boxRowPadding.right(), + (rect.height() - textHeight) / 2); + Ui::FillSpoilerRect(p, r, spoilerCached.frame()); + }, spoiler->lifetime()); + spoiler->setClickedCallback([show = box->uiShow()] { + show->showToast(tr::lng_gift_link_pending_toast(tr::now)); + }); + spoiler->show(); + } + + AddTable(box->verticalLayout(), controller, data, true); + + const auto footer = box->addRow( + object_ptr( + box, + tr::lng_gift_link_pending_footer(), + st::giveawayGiftCodeFooter), + st::giveawayGiftCodeFooterMargin); + + const auto close = Ui::CreateChild( + box.get(), + st::boxTitleClose); + const auto closeCallback = [=] { box->closeBox(); }; + close->setClickedCallback(closeCallback); + box->widthValue( + ) | rpl::start_with_next([=](int width) { + close->moveToRight(0, 0); + }, box->lifetime()); + + const auto button = box->addButton(tr::lng_close(), closeCallback); + const auto buttonPadding = st::giveawayGiftCodeBox.buttonPadding; + const auto buttonWidth = st::boxWideWidth + - buttonPadding.left() + - buttonPadding.right(); + button->widthValue() | rpl::filter([=] { + return (button->widthNoMargins() != buttonWidth); + }) | rpl::start_with_next([=] { + button->resizeToWidth(buttonWidth); + }, button->lifetime()); +} + void ResolveGiftCode( not_null controller, const QString &slug) { diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.h b/Telegram/SourceFiles/boxes/gift_premium_box.h index a1062dead..1891ce5ff 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.h +++ b/Telegram/SourceFiles/boxes/gift_premium_box.h @@ -50,6 +50,10 @@ void GiftCodeBox( not_null box, not_null controller, const QString &slug); +void GiftCodePendingBox( + not_null box, + not_null controller, + const Api::GiftCode &data); void ResolveGiftCode( not_null controller, const QString &slug); diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index 4a85789e6..2f85dd9a7 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -315,9 +315,23 @@ void InnerWidget::fill() { ResolveGiftCode(_controller, boost.giftCodeLink.slug); } else if (boost.userId) { const auto user = _peer->owner().user(boost.userId); - crl::on_main(this, [=] { - _controller->showPeerInfo(user); - }); + if (boost.isGift || boost.isGiveaway) { + constexpr auto kMonthsDivider = int(30 * 86400); + const auto date = TimeId(boost.date.toSecsSinceEpoch()); + const auto months = (boost.expiresAt - date) + / kMonthsDivider; + const auto d = Api::GiftCode{ + .from = _peer->id, + .to = user->id, + .date = date, + .months = int(months), + }; + _show->showBox(Box(GiftCodePendingBox, _controller, d)); + } else { + crl::on_main(this, [=] { + _controller->showPeerInfo(user); + }); + } } else if (!boost.isUnclaimed) { _show->showToast(tr::lng_boosts_list_pending_about(tr::now)); } From a7090c5fbaf2d47216ab99f9bb0a3331ea2d6daf Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Nov 2023 08:35:35 +0400 Subject: [PATCH 08/41] Fix possible crash in topic replies. --- Telegram/SourceFiles/history/history_item.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 18235872c..1bd5d52c2 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -3355,6 +3355,7 @@ void HistoryItem::createComponentsHelper( : nullptr; if (!config.reply.externalPeerId && topic + && to && topic->rootId() != to->topicRootId()) { config.reply.externalPeerId = replyTo.messageId.peer; } From 642b0ed0320eef6f22dd2dcd46d144f56f1dde8f Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Mon, 6 Nov 2023 23:15:16 +0100 Subject: [PATCH 09/41] Install monochrome icon as symbolic app icon on Linux GNOME Shell uses the symbolic icon in the topbar and notifications. --- Telegram/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 3ad594344..03382f711 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1853,6 +1853,7 @@ if (LINUX AND DESKTOP_APP_USE_PACKAGED) install(FILES "Resources/art/icon128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "telegram.png") install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "telegram.png") install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "telegram.png") + install(FILES "Resources/icons/tray_monochrome.svg" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/symbolic/apps" RENAME "telegram-symbolic.svg") install(FILES "../lib/xdg/org.telegram.desktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.service" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/dbus-1/services") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo") From 3ac222db62d82f6065aee67502e0b793c6bc1cfc Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Nov 2023 09:08:08 +0400 Subject: [PATCH 10/41] Fix initial scroll to search result in bots. --- Telegram/SourceFiles/history/history_inner_widget.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index bc8b0b09b..101d8a0b4 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -3088,6 +3088,8 @@ void HistoryInner::recountHistoryGeometry() { accumulate_max(oldHistoryPaddingTop, _botAbout->height); } + updateBotInfo(false); + _history->resizeToWidth(_contentWidth); if (_migrated) { _migrated->resizeToWidth(_contentWidth); @@ -3111,7 +3113,6 @@ void HistoryInner::recountHistoryGeometry() { } } - updateBotInfo(false); if (const auto view = _botAbout ? _botAbout->view() : nullptr) { _botAbout->height = view->resizeGetHeight(_contentWidth); _botAbout->top = qMin( From ef685aa0da3d80f43301d99dcec6402c2f015078 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Nov 2023 09:14:32 +0400 Subject: [PATCH 11/41] Fix via @bot badge in unwrapped media. Fixes #27048. --- .../history/view/media/history_view_media_unwrapped.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp index e61202239..b0df17d48 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp @@ -325,7 +325,7 @@ void UnwrappedMedia::drawSurrounding( recty += skip; } else if (via) { p.setFont(st::msgDateFont); - p.drawTextLeft(rectx, recty + st::msgReplyPadding.top(), 2 * textx + textw, via->text); + p.drawTextLeft(textx, recty + st::msgReplyPadding.top(), 2 * textx + textw, via->text); const auto skip = st::msgServiceNameFont->height + (reply ? st::msgReplyPadding.top() : 0); From b1459e41d32dbf7128ca418383f637154ba9e95a Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Nov 2023 11:47:10 +0400 Subject: [PATCH 12/41] Fix system version check for dark taskbar support. Fixes #27056. --- Telegram/SourceFiles/platform/win/tray_win.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/win/tray_win.cpp b/Telegram/SourceFiles/platform/win/tray_win.cpp index fda59791e..272ad4f0d 100644 --- a/Telegram/SourceFiles/platform/win/tray_win.cpp +++ b/Telegram/SourceFiles/platform/win/tray_win.cpp @@ -38,7 +38,7 @@ constexpr auto kTooltipDelay = crl::time(10000); QOperatingSystemVersion::Windows, 10, 0, - 17763); + 18282); static const auto kSupported = (kSystemVersion >= kDarkModeAddedVersion); if (!kSupported) { return std::nullopt; From 524a7a6a23505eb344bc214a5931d985d2ec8e49 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Nov 2023 12:40:29 +0400 Subject: [PATCH 13/41] Fix wrong popup menu click-through on macOS 14.1. Fixes #27025. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 0837a6364..d28afffcb 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 0837a6364d85fc2b057d14b3548d7d7b644adab3 +Subproject commit d28afffcb9211228d6b5a4424da330637f12ca4a From 0293700e0f607cfcd0d766e845b2b650d94f6811 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 19:56:43 +0300 Subject: [PATCH 14/41] Added ability to ignore dark themes in premium top bar via styles. --- Telegram/SourceFiles/ui/effects/premium.style | 2 ++ Telegram/SourceFiles/ui/effects/premium_top_bar.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index 46305a2c7..684fab6c7 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -32,6 +32,7 @@ PremiumCover { titlePadding: margins; titleFont: font; about: FlatLabel; + additionalShadowForDarkThemes: bool; } premiumAboutTextStyle: TextStyle(defaultTextStyle) { @@ -53,6 +54,7 @@ defaultPremiumCover: PremiumCover { textFg: premiumButtonFg; minWidth: 190px; } + additionalShadowForDarkThemes: true; } userPremiumCoverAbout: FlatLabel(boxDividerLabel) { style: premiumAboutTextStyle; diff --git a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp index 951eb8f21..1d0b2cfbd 100644 --- a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp @@ -121,7 +121,7 @@ void TopBarAbstract::paintEdges(QPainter &p, const QBrush &brush) const { void TopBarAbstract::paintEdges(QPainter &p) const { paintEdges(p, st::boxBg); - if (isDark()) { + if (isDark() && st().additionalShadowForDarkThemes) { paintEdges(p, st::shadowFg); paintEdges(p, st::shadowFg); } From b56b9b1fb9ebac526075ca51fa9ff33cc53819f7 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 19:58:13 +0300 Subject: [PATCH 15/41] Added ability to set background color for premium top bar via styles. --- Telegram/SourceFiles/ui/effects/premium.style | 2 ++ Telegram/SourceFiles/ui/effects/premium_top_bar.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index 684fab6c7..ef364e0fe 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -27,6 +27,7 @@ PremiumBubble { font: font; } PremiumCover { + bg: color; starSize: size; starTopSkip: pixels; titlePadding: margins; @@ -41,6 +42,7 @@ premiumAboutTextStyle: TextStyle(defaultTextStyle) { lineHeight: 18px; } defaultPremiumCover: PremiumCover { + bg: boxBg; starSize: size(84px, 81px); starTopSkip: 37px; titlePadding: margins(0px, 18px, 0px, 11px); diff --git a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp index 1d0b2cfbd..2127d996c 100644 --- a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp @@ -120,7 +120,7 @@ void TopBarAbstract::paintEdges(QPainter &p, const QBrush &brush) const { } void TopBarAbstract::paintEdges(QPainter &p) const { - paintEdges(p, st::boxBg); + paintEdges(p, st().bg); if (isDark() && st().additionalShadowForDarkThemes) { paintEdges(p, st::shadowFg); paintEdges(p, st::shadowFg); @@ -144,7 +144,7 @@ bool TopBarAbstract::isDark() const { void TopBarAbstract::computeIsDark() { const auto contrast = CountContrast( - st::boxBg->c, + st().bg->c, st::premiumButtonFg->c); _isDark = (contrast > kMinAcceptableContrast); } From 49d5cf939c1cec920797bfffdebb11699f796b4b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 16:54:38 +0300 Subject: [PATCH 16/41] Added special fading-in top bar with title to giveaway box. --- .../info/boosts/create_giveaway_box.cpp | 209 +++++++++++++++--- .../info/boosts/giveaway/giveaway.style | 15 ++ 2 files changed, 189 insertions(+), 35 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index 466584e81..915e74e7c 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -27,6 +27,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/premium_graphics.h" #include "ui/effects/premium_top_bar.h" #include "ui/layers/generic_box.h" +#include "ui/painter.h" +#include "ui/rect.h" #include "ui/text/format_values.h" #include "ui/text/text_utilities.h" #include "ui/toast/toast.h" @@ -67,6 +69,159 @@ constexpr auto kDoneTooltipDuration = 5 * crl::time(1000); }; } +[[nodiscard]] QWidget *FindFirstShadowInBox(not_null box) { + for (const auto &child : box->children()) { + if (child && child->isWidgetType()) { + const auto w = static_cast(child); + if (w->height() == st::lineWidth) { + return w; + } + } + } + return nullptr; +} + +void AddPremiumTopBarWithDefaultTitleBar( + not_null box, + rpl::producer<> showFinished, + rpl::producer titleText) { + struct State final { + Ui::Animations::Simple animation; + Ui::Text::String title; + + Ui::RpWidget close; + }; + const auto state = box->lifetime().make_state(); + box->setNoContentMargin(true); + + std::move( + titleText + ) | rpl::start_with_next([=](const QString &s) { + state->title.setText(st::startGiveawayBox.title.style, s); + }, box->lifetime()); + + const auto hPadding = rect::m::sum::h(st::boxRowPadding); + const auto titlePaintContext = Ui::Text::PaintContext{ + .position = st::boxTitlePosition, + .outerWidth = (st::boxWideWidth - hPadding), + .availableWidth = (st::boxWideWidth - hPadding), + }; + + const auto isCloseBarShown = [=] { return box->scrollTop() > 0; }; + + const auto closeTopBar = box->setPinnedToTopContent( + object_ptr(box)); + closeTopBar->resize(box->width(), st::boxTitleHeight); + closeTopBar->paintRequest( + ) | rpl::start_with_next([=](const QRect &r) { + auto p = Painter(closeTopBar); + const auto radius = st::boxRadius; + const auto progress = state->animation.value(isCloseBarShown() + ? 1. + : 0.); + const auto resultRect = r + QMargins{ 0, 0, 0, radius }; + { + auto hq = PainterHighQualityEnabler(p); + + if (progress < 1.) { + auto path = QPainterPath(); + path.addRect(resultRect); + path.addRect( + st::boxRowPadding.left(), + 0, + resultRect.width() - hPadding, + resultRect.height()); + p.setClipPath(path); + PainterHighQualityEnabler hq(p); + p.setPen(Qt::NoPen); + p.setBrush(st::boxDividerBg); + p.drawRoundedRect(resultRect, radius, radius); + } + if (progress > 0.) { + p.setOpacity(progress); + + p.setClipping(false); + p.setPen(Qt::NoPen); + p.setBrush(st::boxBg); + p.drawRoundedRect(resultRect, radius, radius); + + p.setPen(st::startGiveawayBox.title.textFg); + p.setBrush(Qt::NoBrush); + state->title.draw(p, titlePaintContext); + } + } + }, closeTopBar->lifetime()); + + { + const auto close = Ui::CreateChild( + closeTopBar.get(), + st::startGiveawayBoxTitleClose); + close->setClickedCallback([=] { box->closeBox(); }); + closeTopBar->widthValue( + ) | rpl::start_with_next([=](int w) { + const auto &pos = st::giveawayGiftCodeCoverClosePosition; + close->moveToRight(pos.x(), pos.y()); + }, box->lifetime()); + close->show(); + } + + const auto bar = Ui::CreateChild( + box.get(), + st::startGiveawayCover, + nullptr, + tr::lng_giveaway_new_title(), + tr::lng_giveaway_new_about(Ui::Text::RichLangValue), + true); + bar->setAttribute(Qt::WA_TransparentForMouseEvents); + + box->addRow( + object_ptr( + box.get(), + st::giveawayGiftCodeTopHeight + - st::boxTitleHeight + + st::boxDividerHeight + + st::settingsSectionSkip, + st::boxDividerBg, + RectPart::Bottom), + {}); + bar->setPaused(true); + bar->setRoundEdges(false); + bar->setMaximumHeight(st::giveawayGiftCodeTopHeight); + bar->setMinimumHeight(st::infoLayerTopBarHeight); + bar->resize(bar->width(), bar->maximumHeight()); + box->widthValue( + ) | rpl::start_with_next([=](int w) { + bar->resizeToWidth(w - hPadding); + bar->moveToLeft(st::boxRowPadding.left(), bar->y()); + }, box->lifetime()); + + std::move( + showFinished + ) | rpl::take(1) | rpl::start_with_next([=] { + closeTopBar->raise(); + if (const auto shadow = FindFirstShadowInBox(box)) { + bar->stackUnder(shadow); + } + bar->setPaused(false); + box->scrolls( + ) | rpl::map(isCloseBarShown) | rpl::distinct_until_changed( + ) | rpl::start_with_next([=](bool showBar) { + state->animation.stop(); + state->animation.start( + [=] { closeTopBar->update(); }, + showBar ? 0. : 1., + showBar ? 1. : 0., + st::slideWrapDuration); + }, box->lifetime()); + box->scrolls( + ) | rpl::start_with_next([=] { + bar->moveToLeft(bar->x(), -box->scrollTop()); + }, box->lifetime()); + }, box->lifetime()); + + bar->show(); +} + } // namespace void CreateGiveawayBox( @@ -77,37 +232,6 @@ void CreateGiveawayBox( const auto weakWindow = base::make_weak(controller->parentController()); - const auto bar = box->verticalLayout()->add( - object_ptr( - box, - st::giveawayGiftCodeCover, - nullptr, - tr::lng_giveaway_new_title(), - tr::lng_giveaway_new_about(Ui::Text::RichLangValue), - true)); - { - bar->setPaused(true); - bar->setMaximumHeight(st::giveawayGiftCodeTopHeight); - bar->setMinimumHeight(st::infoLayerTopBarHeight); - bar->resize(bar->width(), bar->maximumHeight()); - - const auto container = box->verticalLayout(); - const auto &padding = st::giveawayGiftCodeCoverDividerPadding; - Settings::AddSkip(container, padding.top()); - Settings::AddDivider(container); - Settings::AddSkip(container, padding.bottom()); - - const auto close = Ui::CreateChild( - container.get(), - st::boxTitleClose); - close->setClickedCallback([=] { box->closeBox(); }); - box->widthValue( - ) | rpl::start_with_next([=](int) { - const auto &pos = st::giveawayGiftCodeCoverClosePosition; - close->moveToRight(pos.x(), pos.y()); - }, box->lifetime()); - } - using GiveawayType = Giveaway::GiveawayTypeRow::Type; using GiveawayGroup = Ui::RadioenumGroup; struct State final { @@ -132,6 +256,20 @@ void CreateGiveawayBox( const auto state = box->lifetime().make_state(peer); const auto typeGroup = std::make_shared(); + auto showFinished = Ui::BoxShowFinishes(box); + AddPremiumTopBarWithDefaultTitleBar( + box, + rpl::duplicate(showFinished), + rpl::conditional( + state->typeValue.value( + ) | rpl::map(rpl::mappers::_1 == GiveawayType::Random), + tr::lng_giveaway_start(), + tr::lng_giveaway_award())); + { + const auto &padding = st::giveawayGiftCodeCoverDividerPadding; + Settings::AddSkip(box->verticalLayout(), padding.bottom()); + } + const auto loading = box->addRow( object_ptr>( box, @@ -587,7 +725,7 @@ void CreateGiveawayBox( } { // TODO mini-icon. - const auto &stButton = st::premiumGiftBox; + const auto &stButton = st::startGiveawayBox; box->setStyle(stButton); auto button = object_ptr( box, @@ -691,11 +829,12 @@ void CreateGiveawayBox( } state->typeValue.force_assign(GiveawayType::Random); - box->setShowFinishedCallback([=] { + std::move( + showFinished + ) | rpl::take(1) | rpl::start_with_next([=] { if (!loading->toggled()) { return; } - bar->setPaused(false); state->lifetimeApi = state->apiOptions.request( ) | rpl::start_with_error_done([=](const QString &error) { }, [=] { @@ -706,5 +845,5 @@ void CreateGiveawayBox( contentWrap->toggle(true, anim::type::instant); contentWrap->resizeToWidth(box->width()); }); - }); + }, box->lifetime()); } diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style b/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style index db9ad6f04..211087975 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style @@ -163,3 +163,18 @@ giveawayRefundedLabel: FlatLabel(boxLabel) { textFg: attentionButtonFg; } giveawayRefundedPadding: margins(8px, 10px, 8px, 10px); + +startGiveawayBox: Box(premiumGiftBox) { + shadowIgnoreTopSkip: true; +} +startGiveawayScrollArea: ScrollArea(boxScroll) { + deltax: 3px; + deltat: 50px; +} +startGiveawayBoxTitleClose: IconButton(boxTitleClose) { + ripple: universalRippleAnimation; +} +startGiveawayCover: PremiumCover(giveawayGiftCodeCover) { + bg: boxDividerBg; + additionalShadowForDarkThemes: false; +} From 6ca777102c15b63f2a1d1680d93ef394d377386e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 20:06:52 +0300 Subject: [PATCH 17/41] Added ability to paint non-optimized ministars to fix possible glitches. --- Telegram/SourceFiles/boxes/gift_premium_box.cpp | 2 +- .../info/boosts/create_giveaway_box.cpp | 3 ++- Telegram/SourceFiles/settings/settings_premium.cpp | 2 +- .../ui/effects/premium_stars_colored.cpp | 14 ++++++++++---- .../SourceFiles/ui/effects/premium_stars_colored.h | 3 ++- .../SourceFiles/ui/effects/premium_top_bar.cpp | 5 +++-- Telegram/SourceFiles/ui/effects/premium_top_bar.h | 3 ++- 7 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index e7f2dab7b..96196bc11 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -95,7 +95,7 @@ void GiftBox( + st::defaultUserpicButton.size.height())); using ColoredMiniStars = Ui::Premium::ColoredMiniStars; - const auto stars = box->lifetime().make_state(top); + const auto stars = box->lifetime().make_state(top, true); const auto userpic = Ui::CreateChild( top, diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index 915e74e7c..f34e3f7da 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -171,7 +171,8 @@ void AddPremiumTopBarWithDefaultTitleBar( nullptr, tr::lng_giveaway_new_title(), tr::lng_giveaway_new_about(Ui::Text::RichLangValue), - true); + true, + false); bar->setAttribute(Qt::WA_TransparentForMouseEvents); box->addRow( diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index cb3eeccd8..b18e735f7 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -523,7 +523,7 @@ TopBarUser::TopBarUser( , _content(this) , _title(_content, st::settingsPremiumUserTitle) , _about(_content, st::userPremiumCover.about) -, _ministars(_content) +, _ministars(_content, true) , _smallTop({ .widget = object_ptr(this), .text = Ui::Text::String( diff --git a/Telegram/SourceFiles/ui/effects/premium_stars_colored.cpp b/Telegram/SourceFiles/ui/effects/premium_stars_colored.cpp index 5eeee3b89..18c9c9c24 100644 --- a/Telegram/SourceFiles/ui/effects/premium_stars_colored.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_stars_colored.cpp @@ -13,10 +13,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { namespace Premium { -ColoredMiniStars::ColoredMiniStars(not_null parent) -: _ministars([=](const QRect &r) { - parent->update(r.translated(_position)); -}, true) { +ColoredMiniStars::ColoredMiniStars( + not_null parent, + bool optimizeUpdate) +: _ministars( + optimizeUpdate + ? Fn([=](const QRect &r) { + parent->update(r.translated(_position)); + }) + : Fn([=](const QRect &) { parent->update(); }), + true) { } void ColoredMiniStars::setSize(const QSize &size) { diff --git a/Telegram/SourceFiles/ui/effects/premium_stars_colored.h b/Telegram/SourceFiles/ui/effects/premium_stars_colored.h index a8bdbd974..75660ab99 100644 --- a/Telegram/SourceFiles/ui/effects/premium_stars_colored.h +++ b/Telegram/SourceFiles/ui/effects/premium_stars_colored.h @@ -16,7 +16,8 @@ namespace Premium { class ColoredMiniStars final { public: - ColoredMiniStars(not_null parent); + // optimizeUpdate may cause paint glitch. + ColoredMiniStars(not_null parent, bool optimizeUpdate); void setSize(const QSize &size); void setPosition(QPoint position); diff --git a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp index 2127d996c..4094b584d 100644 --- a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp @@ -155,13 +155,14 @@ TopBar::TopBar( Fn clickContextOther, rpl::producer title, rpl::producer about, - bool light) + bool light, + bool optimizeMinistars) : TopBarAbstract(parent, st) , _light(light) , _titleFont(st.titleFont) , _titlePadding(st.titlePadding) , _about(this, std::move(about), st.about) -, _ministars(this) { +, _ministars(this, optimizeMinistars) { std::move( title ) | rpl::start_with_next([=](QString text) { diff --git a/Telegram/SourceFiles/ui/effects/premium_top_bar.h b/Telegram/SourceFiles/ui/effects/premium_top_bar.h index c0749df00..9ccc4fa30 100644 --- a/Telegram/SourceFiles/ui/effects/premium_top_bar.h +++ b/Telegram/SourceFiles/ui/effects/premium_top_bar.h @@ -72,7 +72,8 @@ public: Fn clickContextOther, rpl::producer title, rpl::producer about, - bool light = false); + bool light = false, + bool optimizeMinistars = true); ~TopBar(); void setPaused(bool paused) override; From 474fa56cc03c4773c3d86b9d7835ec83b8cdd79f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 20:54:58 +0300 Subject: [PATCH 18/41] Improved processing of giveaway creation after payment. --- .../info/boosts/create_giveaway_box.cpp | 18 ++++++++++++------ .../info/boosts/create_giveaway_box.h | 3 ++- .../info/boosts/info_boosts_inner_widget.cpp | 13 ++++++++++--- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index f34e3f7da..1c830c08f 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -228,7 +228,8 @@ void AddPremiumTopBarWithDefaultTitleBar( void CreateGiveawayBox( not_null box, not_null controller, - not_null peer) { + not_null peer, + Fn reloadOnDone) { box->setWidth(st::boxWideWidth); const auto weakWindow = base::make_weak(controller->parentController()); @@ -790,12 +791,15 @@ void CreateGiveawayBox( const auto show = box->uiShow(); const auto weak = Ui::MakeWeak(box.get()); const auto done = [=](Payments::CheckoutResult result) { - if (const auto strong = weak.data()) { - state->confirmButtonBusy = false; - strong->window()->setFocus(); - strong->closeBox(); + const auto isPaid = result == Payments::CheckoutResult::Paid; + if (result == Payments::CheckoutResult::Pending || isPaid) { + if (const auto strong = weak.data()) { + strong->window()->setFocus(); + strong->closeBox(); + } } - if (result == Payments::CheckoutResult::Paid) { + if (isPaid) { + reloadOnDone(); const auto filter = [=](const auto &...) { if (const auto window = weakWindow.get()) { window->showSection(Info::Boosts::Make(peer)); @@ -822,6 +826,8 @@ void CreateGiveawayBox( .adaptive = true, .filter = filter, }); + } else { + state->confirmButtonBusy = false; } }; Payments::CheckoutProcess::Start(std::move(invoice), done); diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.h b/Telegram/SourceFiles/info/boosts/create_giveaway_box.h index 7463732dc..d31383dc7 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.h +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.h @@ -20,4 +20,5 @@ class GenericBox; void CreateGiveawayBox( not_null box, not_null controller, - not_null peer); + not_null peer, + Fn reloadOnDone); diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index 2f85dd9a7..2cfebee08 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -216,7 +216,8 @@ void FillGetBoostsButton( not_null content, not_null controller, std::shared_ptr show, - not_null peer) { + not_null peer, + Fn reloadOnDone) { if (!Api::PremiumGiftCodeOptions(peer).giveawayGiftsPurchaseAvailable()) { return; } @@ -229,7 +230,7 @@ void FillGetBoostsButton( tr::lng_boosts_get_boosts(), st)); button->setClickedCallback([=] { - show->showBox(Box(CreateGiveawayBox, controller, peer)); + show->showBox(Box(CreateGiveawayBox, controller, peer, reloadOnDone)); }); Ui::CreateChild( button, @@ -428,7 +429,13 @@ void InnerWidget::fill() { ::Settings::AddSkip(inner); ::Settings::AddDividerText(inner, tr::lng_boosts_link_subtext()); - FillGetBoostsButton(inner, _controller, _show, _peer); + const auto reloadOnDone = crl::guard(this, [=] { + while (Ui::VerticalLayout::count()) { + delete Ui::VerticalLayout::widgetAt(0); + } + load(); + }); + FillGetBoostsButton(inner, _controller, _show, _peer, reloadOnDone); resizeToWidth(width()); crl::on_main(this, [=]{ fakeShowed->fire({}); }); From 4150cdff8620829b5a355afbca04870fa7ce205e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 22:33:57 +0300 Subject: [PATCH 19/41] Moved out badge for giveaway list to td_ui. --- .../info/boosts/giveaway/boost_badge.cpp | 67 +++++++++++++++++++ .../info/boosts/giveaway/boost_badge.h | 27 ++++++++ .../info_statistics_list_controllers.cpp | 57 +--------------- Telegram/cmake/td_ui.cmake | 2 + 4 files changed, 99 insertions(+), 54 deletions(-) create mode 100644 Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp create mode 100644 Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h diff --git a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp new file mode 100644 index 000000000..778257f55 --- /dev/null +++ b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp @@ -0,0 +1,67 @@ +/* +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/boosts/giveaway/boost_badge.h" + +#include "ui/painter.h" +#include "ui/rect.h" + +namespace Info::Statistics { + +QImage CreateBadge( + const style::TextStyle &textStyle, + const QString &text, + int badgeHeight, + const style::margins &textPadding, + const style::color &bg, + const style::color &fg, + float64 bgOpacity, + const style::margins &iconPadding, + const style::icon &icon) { + auto badgeText = Ui::Text::String(textStyle, text); + const auto badgeTextWidth = badgeText.maxWidth(); + const auto badgex = 0; + const auto badgey = 0; + const auto badgeh = 0 + badgeHeight; + const auto badgew = badgeTextWidth + + rect::m::sum::h(textPadding); + auto result = QImage( + QSize(badgew, badgeh) * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::transparent); + result.setDevicePixelRatio(style::DevicePixelRatio()); + { + auto p = Painter(&result); + + p.setPen(Qt::NoPen); + p.setBrush(bg); + + const auto r = QRect(badgex, badgey, badgew, badgeh); + { + auto hq = PainterHighQualityEnabler(p); + auto o = ScopedPainterOpacity(p, bgOpacity); + p.drawRoundedRect(r, badgeh / 2, badgeh / 2); + } + + p.setPen(fg); + p.setBrush(Qt::NoBrush); + badgeText.drawLeftElided( + p, + r.x() + textPadding.left(), + badgey + textPadding.top(), + badgew, + badgew * 2); + + icon.paint( + p, + QPoint(r.x() + iconPadding.left(), r.y() + iconPadding.top()), + badgew * 2); + } + return result; +} + +} // namespace Info::Statistics diff --git a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h new file mode 100644 index 000000000..3d31df608 --- /dev/null +++ b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h @@ -0,0 +1,27 @@ +/* +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 +*/ +#pragma once + +namespace style { +struct TextStyle; +} // namespace style + +namespace Info::Statistics { + +[[nodiscard]] QImage CreateBadge( + const style::TextStyle &textStyle, + const QString &text, + int badgeHeight, + const style::margins &textPadding, + const style::color &bg, + const style::color &fg, + float64 bgOpacity, + const style::margins &iconPadding, + const style::icon &icon); + +} // namespace Info::Statistics diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 43606f195..7dd49517b 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "history/history_item.h" +#include "info/boosts/giveaway/boost_badge.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "settings/settings_common.h" @@ -35,58 +36,6 @@ using BoostCallback = Fn; constexpr auto kColorIndexUnclaimed = int(3); constexpr auto kColorIndexPending = int(4); -[[nodiscard]] QImage Badge( - const style::TextStyle &textStyle, - const QString &text, - int badgeHeight, - const style::margins &textPadding, - const style::color &bg, - const style::color &fg, - float64 bgOpacity, - const style::margins &iconPadding, - const style::icon &icon) { - auto badgeText = Ui::Text::String(textStyle, text); - const auto badgeTextWidth = badgeText.maxWidth(); - const auto badgex = 0; - const auto badgey = 0; - const auto badgeh = 0 + badgeHeight; - const auto badgew = badgeTextWidth - + rect::m::sum::h(textPadding); - auto result = QImage( - QSize(badgew, badgeh) * style::DevicePixelRatio(), - QImage::Format_ARGB32_Premultiplied); - result.fill(Qt::transparent); - result.setDevicePixelRatio(style::DevicePixelRatio()); - { - auto p = Painter(&result); - - p.setPen(Qt::NoPen); - p.setBrush(bg); - - const auto r = QRect(badgex, badgey, badgew, badgeh); - { - auto hq = PainterHighQualityEnabler(p); - auto o = ScopedPainterOpacity(p, bgOpacity); - p.drawRoundedRect(r, badgeh / 2, badgeh / 2); - } - - p.setPen(fg); - p.setBrush(Qt::NoBrush); - badgeText.drawLeftElided( - p, - r.x() + textPadding.left(), - badgey + textPadding.top(), - badgew, - badgew * 2); - - icon.paint( - p, - QPoint(r.x() + iconPadding.left(), r.y() + iconPadding.top()), - badgew * 2); - } - return result; -} - void AddArrow(not_null parent) { const auto arrow = Ui::CreateChild(parent.get()); arrow->paintRequest( @@ -486,7 +435,7 @@ PaintRoundImageCallback BoostRow::generatePaintUserpicCallback(bool force) { void BoostRow::invalidateBadges() { _badge = _boost.multiplier - ? Badge( + ? CreateBadge( st::statisticsDetailsBottomCaptionStyle, QString::number(_boost.multiplier), st::boostsListBadgeHeight, @@ -506,7 +455,7 @@ void BoostRow::invalidateBadges() { ? st::boostsListGiveawayMiniIcon : st::boostsListGiftMiniIcon; _rightBadge = (_boost.isGift || _boost.isGiveaway) - ? Badge( + ? CreateBadge( st::boostsListRightBadgeTextStyle, _boost.isGiveaway ? tr::lng_gift_link_reason_giveaway(tr::now) diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index b9eab3397..cdf8cbf27 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -110,6 +110,8 @@ PRIVATE info/userpic/info_userpic_emoji_builder_layer.cpp info/userpic/info_userpic_emoji_builder_layer.h + info/boosts/giveaway/boost_badge.cpp + info/boosts/giveaway/boost_badge.h info/boosts/giveaway/giveaway_type_row.cpp info/boosts/giveaway/giveaway_type_row.h info/boosts/giveaway/select_countries_box.cpp From 43aa8825a5d7ea8208616593b96d8030a71aebd6 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 23:32:48 +0300 Subject: [PATCH 20/41] Added badge and loading state to confirm button in giveaway box. --- .../info/boosts/create_giveaway_box.cpp | 34 +++++- .../info/boosts/giveaway/boost_badge.cpp | 106 ++++++++++++++++++ .../info/boosts/giveaway/boost_badge.h | 14 +++ .../info/boosts/giveaway/giveaway.style | 16 +++ 4 files changed, 166 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index 1c830c08f..ac22dc934 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "countries/countries_instance.h" #include "data/data_peer.h" +#include "info/boosts/giveaway/boost_badge.h" #include "info/boosts/giveaway/giveaway_list_controllers.h" #include "info/boosts/giveaway/giveaway_type_row.h" #include "info/boosts/giveaway/select_countries_box.h" @@ -253,7 +254,7 @@ void CreateGiveawayBox( rpl::variable dateValue; rpl::variable> countriesValue; - bool confirmButtonBusy = false; + rpl::variable confirmButtonBusy = true; }; const auto state = box->lifetime().make_state(peer); const auto typeGroup = std::make_shared(); @@ -726,17 +727,41 @@ void CreateGiveawayBox( }, box->lifetime()); } { - // TODO mini-icon. + using namespace Info::Statistics; const auto &stButton = st::startGiveawayBox; box->setStyle(stButton); auto button = object_ptr( box, + rpl::never(), + st::giveawayGiftCodeStartButton); + + AddLabelWithBadgeToButton( + button, rpl::conditional( state->typeValue.value( ) | rpl::map(rpl::mappers::_1 == GiveawayType::Random), tr::lng_giveaway_start(), tr::lng_giveaway_award()), - st::giveawayGiftCodeStartButton); + state->sliderValue.value( + ) | rpl::map([=](int v) -> int { + return state->apiOptions.giveawayBoostsPerPremium() * v; + }), + state->confirmButtonBusy.value() | rpl::map(!rpl::mappers::_1)); + + { + const auto loadingAnimation = InfiniteRadialAnimationWidget( + button, + st::giveawayGiftCodeStartButton.height / 2); + button->sizeValue( + ) | rpl::start_with_next([=](const QSize &s) { + const auto size = loadingAnimation->size(); + loadingAnimation->moveToLeft( + (s.width() - size.width()) / 2, + (s.height() - size.height()) / 2); + }, loadingAnimation->lifetime()); + loadingAnimation->showOn(state->confirmButtonBusy.value()); + } + button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); state->typeValue.value( ) | rpl::start_with_next([=, raw = button.data()] { @@ -745,7 +770,7 @@ void CreateGiveawayBox( - stButton.buttonPadding.right()); }, button->lifetime()); button->setClickedCallback([=] { - if (state->confirmButtonBusy) { + if (state->confirmButtonBusy.current()) { return; } const auto type = typeGroup->value(); @@ -847,6 +872,7 @@ void CreateGiveawayBox( }, [=] { state->lifetimeApi.destroy(); loading->toggle(false, anim::type::instant); + state->confirmButtonBusy = false; fillSliderContainer(); rebuildListOptions(1); contentWrap->toggle(true, anim::type::instant); diff --git a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp index 778257f55..13e8fb192 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp +++ b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.cpp @@ -7,11 +7,51 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "info/boosts/giveaway/boost_badge.h" +#include "ui/effects/radial_animation.h" #include "ui/painter.h" #include "ui/rect.h" +#include "ui/rp_widget.h" +#include "ui/widgets/labels.h" +#include "styles/style_giveaway.h" +#include "styles/style_statistics.h" +#include "styles/style_widgets.h" namespace Info::Statistics { +not_null InfiniteRadialAnimationWidget( + not_null parent, + int size) { + class Widget final : public Ui::RpWidget { + public: + Widget(not_null p, int size) + : Ui::RpWidget(p) + , _animation([=] { update(); }, st::startGiveawayButtonLoading) { + resize(size, size); + shownValue() | rpl::start_with_next([=](bool v) { + return v + ? _animation.start() + : _animation.stop(anim::type::instant); + }, lifetime()); + } + + protected: + void paintEvent(QPaintEvent *e) override { + auto p = QPainter(this); + p.setPen(st::activeButtonFg); + p.setBrush(st::activeButtonFg); + const auto r = rect() + - Margins(st::startGiveawayButtonLoading.thickness); + _animation.draw(p, r.topLeft(), r.size(), width()); + } + + private: + Ui::InfiniteRadialAnimation _animation; + + }; + + return Ui::CreateChild(parent.get(), size); +} + QImage CreateBadge( const style::TextStyle &textStyle, const QString &text, @@ -64,4 +104,70 @@ QImage CreateBadge( return result; } +void AddLabelWithBadgeToButton( + not_null parent, + rpl::producer text, + rpl::producer number, + rpl::producer shown) { + struct State { + QImage badge; + }; + const auto state = parent->lifetime().make_state(); + const auto label = Ui::CreateChild( + parent.get(), + st::startGiveawayButtonLabelSimple); + std::move( + text + ) | rpl::start_with_next([=](const QString &s) { + label->setText(s); + }, label->lifetime()); + const auto count = Ui::CreateChild(parent.get()); + count->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(count); + p.drawImage(0, 0, state->badge); + }, count->lifetime()); + std::move( + number + ) | rpl::start_with_next([=](int c) { + state->badge = Info::Statistics::CreateBadge( + st::startGiveawayButtonTextStyle, + QString::number(c), + st::boostsListBadgeHeight, + st::startGiveawayButtonBadgeTextPadding, + st::activeButtonFg, + st::activeButtonBg, + 1., + st::boostsListMiniIconPadding, + st::startGiveawayButtonMiniIcon); + count->resize(state->badge.size() / style::DevicePixelRatio()); + count->update(); + }, count->lifetime()); + + std::move( + shown + ) | rpl::start_with_next([=](bool shown) { + count->setVisible(shown); + label->setVisible(shown); + }, count->lifetime()); + + rpl::combine( + parent->sizeValue(), + label->sizeValue(), + count->sizeValue() + ) | rpl::start_with_next([=]( + const QSize &s, + const QSize &s1, + const QSize &s2) { + const auto sum = st::startGiveawayButtonMiniIconSkip + + s1.width() + + s2.width(); + const auto contentLeft = (s.width() - sum) / 2; + label->moveToLeft(contentLeft, (s.height() - s1.height()) / 2); + count->moveToLeft( + contentLeft + sum - s2.width(), + (s.height() - s2.height()) / 2 + st::boostsListMiniIconSkip); + }, parent->lifetime()); +} + } // namespace Info::Statistics diff --git a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h index 3d31df608..fe2427e9c 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h +++ b/Telegram/SourceFiles/info/boosts/giveaway/boost_badge.h @@ -11,6 +11,10 @@ namespace style { struct TextStyle; } // namespace style +namespace Ui { +class RpWidget; +} // namespace Ui + namespace Info::Statistics { [[nodiscard]] QImage CreateBadge( @@ -24,4 +28,14 @@ namespace Info::Statistics { const style::margins &iconPadding, const style::icon &icon); +[[nodiscard]] not_null InfiniteRadialAnimationWidget( + not_null parent, + int size); + +void AddLabelWithBadgeToButton( + not_null parent, + rpl::producer text, + rpl::producer number, + rpl::producer shown); + } // namespace Info::Statistics diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style b/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style index 211087975..4f04fdabb 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style @@ -178,3 +178,19 @@ startGiveawayCover: PremiumCover(giveawayGiftCodeCover) { bg: boxDividerBg; additionalShadowForDarkThemes: false; } + +startGiveawayButtonLabelSimple: LabelSimple { + font: semiboldFont; + textFg: activeButtonFg; +} +startGiveawayButtonMiniIcon: icon{{ "boosts/boost_mini2", activeButtonBg }}; +startGiveawayButtonMiniIconSkip: 5px; +startGiveawayButtonBadgeTextPadding: margins(16px, -1px, 6px, 0px); +startGiveawayButtonTextStyle: TextStyle(defaultTextStyle) { + font: semiboldFont; +} + +startGiveawayButtonLoading: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { + color: activeButtonFg; + thickness: 2px; +} From 3522d9c62e1c428df84fb2bfcc416bfef3aa723b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 8 Nov 2023 14:54:45 +0300 Subject: [PATCH 21/41] Added API support for prepaid giveaways. --- Telegram/SourceFiles/api/api_premium.cpp | 36 +++++++++-- Telegram/SourceFiles/api/api_premium.h | 6 +- Telegram/SourceFiles/api/api_statistics.cpp | 14 +++++ Telegram/SourceFiles/data/data_boosts.h | 8 +++ .../SourceFiles/payments/payments_form.cpp | 63 ++++++++++--------- Telegram/SourceFiles/payments/payments_form.h | 3 + 6 files changed, 95 insertions(+), 35 deletions(-) diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index 629abc0d0..58950f6f4 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -390,18 +390,44 @@ rpl::producer PremiumGiftCodeOptions::request() { }; } +rpl::producer PremiumGiftCodeOptions::applyPrepaid( + const Payments::InvoicePremiumGiftCode &invoice, + uint64 prepaidId) { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + const auto channel = _peer->asChannel(); + if (!channel) { + return lifetime; + } + + _api.request(MTPpayments_LaunchPrepaidGiveaway( + _peer->input, + MTP_long(prepaidId), + Payments::InvoicePremiumGiftCodeGiveawayToTL(invoice) + )).done([=](const MTPUpdates &result) { + _peer->session().api().applyUpdates(result); + consumer.put_done(); + }).fail([=](const MTP::Error &error) { + consumer.put_error_copy(error.type()); + }).send(); + + return lifetime; + }; +} + const std::vector &PremiumGiftCodeOptions::availablePresets() const { return _availablePresets; } +[[nodiscard]] int PremiumGiftCodeOptions::monthsFromPreset(int monthsIndex) { + return _optionsForOnePerson.months[monthsIndex]; +} + Payments::InvoicePremiumGiftCode PremiumGiftCodeOptions::invoice( int users, - int monthsIndex) { + int months) { const auto randomId = base::RandomValue(); - const auto token = Token{ - users, - _optionsForOnePerson.months[monthsIndex], - }; + const auto token = Token{ users, months }; const auto &store = _stores[token]; return Payments::InvoicePremiumGiftCode{ .randomId = randomId, diff --git a/Telegram/SourceFiles/api/api_premium.h b/Telegram/SourceFiles/api/api_premium.h index deb66df80..1199b4819 100644 --- a/Telegram/SourceFiles/api/api_premium.h +++ b/Telegram/SourceFiles/api/api_premium.h @@ -152,9 +152,13 @@ public: [[nodiscard]] rpl::producer request(); [[nodiscard]] Data::SubscriptionOptions options(int amount); [[nodiscard]] const std::vector &availablePresets() const; + [[nodiscard]] int monthsFromPreset(int monthsIndex); [[nodiscard]] Payments::InvoicePremiumGiftCode invoice( int users, - int monthsIndex); + int months); + [[nodiscard]] rpl::producer applyPrepaid( + const Payments::InvoicePremiumGiftCode &invoice, + uint64 prepaidId); [[nodiscard]] int giveawayBoostsPerPremium() const; [[nodiscard]] int giveawayCountriesMax() const; diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 25b0473fb..c5fb3ca76 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -533,6 +533,20 @@ rpl::producer Boosts::request() { }; _boostStatus.link = qs(data.vboost_url()); + if (data.vprepaid_giveaways()) { + _boostStatus.prepaidGiveaway = ranges::views::all( + data.vprepaid_giveaways()->v + ) | ranges::views::transform([](const MTPPrepaidGiveaway &r) { + return Data::BoostPrepaidGiveaway{ + .months = r.data().vmonths().v, + .id = r.data().vid().v, + .quantity = r.data().vquantity().v, + .date = QDateTime::fromSecsSinceEpoch( + r.data().vdate().v), + }; + }) | ranges::to_vector; + } + using namespace Data; requestBoosts({ .gifts = false }, [=](BoostsListSlice &&slice) { _boostStatus.firstSliceBoosts = std::move(slice); diff --git a/Telegram/SourceFiles/data/data_boosts.h b/Telegram/SourceFiles/data/data_boosts.h index a62c70c92..0dad4b008 100644 --- a/Telegram/SourceFiles/data/data_boosts.h +++ b/Telegram/SourceFiles/data/data_boosts.h @@ -50,10 +50,18 @@ struct BoostsListSlice final { OffsetToken token; }; +struct BoostPrepaidGiveaway final { + int months = 0; + uint64 id = 0; + int quantity = 0; + QDateTime date; +}; + struct BoostStatus final { BoostsOverview overview; BoostsListSlice firstSliceBoosts; BoostsListSlice firstSliceGifts; + std::vector prepaidGiveaway; QString link; }; diff --git a/Telegram/SourceFiles/payments/payments_form.cpp b/Telegram/SourceFiles/payments/payments_form.cpp index 9d58ad590..0ad303669 100644 --- a/Telegram/SourceFiles/payments/payments_form.cpp +++ b/Telegram/SourceFiles/payments/payments_form.cpp @@ -130,6 +130,39 @@ not_null SessionFromId(const InvoiceId &id) { return &giveaway.boostPeer->session(); } +MTPinputStorePaymentPurpose InvoicePremiumGiftCodeGiveawayToTL( + const InvoicePremiumGiftCode &invoice) { + const auto &giveaway = v::get( + invoice.purpose); + using Flag = MTPDinputStorePaymentPremiumGiveaway::Flag; + return MTP_inputStorePaymentPremiumGiveaway( + MTP_flags(Flag() + | (giveaway.onlyNewSubscribers + ? Flag::f_only_new_subscribers + : Flag()) + | (giveaway.additionalChannels.empty() + ? Flag() + : Flag::f_additional_peers) + | (giveaway.countries.empty() + ? Flag() + : Flag::f_countries_iso2)), + giveaway.boostPeer->input, + MTP_vector_from_range(ranges::views::all( + giveaway.additionalChannels + ) | ranges::views::transform([](not_null c) { + return MTPInputPeer(c->input); + })), + MTP_vector_from_range(ranges::views::all( + giveaway.countries + ) | ranges::views::transform([](QString value) { + return MTP_string(value); + })), + MTP_long(invoice.randomId), + MTP_int(giveaway.untilDate), + MTP_string(invoice.currency), + MTP_long(invoice.amount)); +} + Form::Form(InvoiceId id, bool receipt) : _id(id) , _session(SessionFromId(id)) @@ -305,36 +338,8 @@ MTPInputInvoice Form::inputInvoice() const { MTP_long(giftCode.amount)), option); } else { - const auto &giveaway = v::get( - giftCode.purpose); - using Flag = MTPDinputStorePaymentPremiumGiveaway::Flag; return MTP_inputInvoicePremiumGiftCode( - MTP_inputStorePaymentPremiumGiveaway( - MTP_flags(Flag() - | (giveaway.onlyNewSubscribers - ? Flag::f_only_new_subscribers - : Flag()) - | (giveaway.additionalChannels.empty() - ? Flag() - : Flag::f_additional_peers) - | (giveaway.countries.empty() - ? Flag() - : Flag::f_countries_iso2)), - giveaway.boostPeer->input, - MTP_vector_from_range(ranges::views::all( - giveaway.additionalChannels - ) | ranges::views::transform([](not_null c) { - return MTPInputPeer(c->input); - })), - MTP_vector_from_range(ranges::views::all( - giveaway.countries - ) | ranges::views::transform([](QString value) { - return MTP_string(value); - })), - MTP_long(giftCode.randomId), - MTP_int(giveaway.untilDate), - MTP_string(giftCode.currency), - MTP_long(giftCode.amount)), + InvoicePremiumGiftCodeGiveawayToTL(giftCode), option); } } diff --git a/Telegram/SourceFiles/payments/payments_form.h b/Telegram/SourceFiles/payments/payments_form.h index 706f9b81d..acf375043 100644 --- a/Telegram/SourceFiles/payments/payments_form.h +++ b/Telegram/SourceFiles/payments/payments_form.h @@ -219,6 +219,9 @@ struct InvoiceId { [[nodiscard]] not_null SessionFromId(const InvoiceId &id); +[[nodiscard]] MTPinputStorePaymentPurpose InvoicePremiumGiftCodeGiveawayToTL( + const InvoicePremiumGiftCode &invoice); + class Form final : public base::has_weak_ptr { public: Form(InvoiceId id, bool receipt); From c9cad4186b8c3f4e87bf92d8d86b73a9ee92aa89 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 18 Oct 2023 01:35:09 +0300 Subject: [PATCH 22/41] Added initial list of prepaid giveaway to boosts info. --- Telegram/Resources/langs/lang.strings | 9 +++ .../info/boosts/create_giveaway_box.cpp | 70 ++++++++++++++++--- .../info/boosts/create_giveaway_box.h | 7 +- .../boosts/giveaway/giveaway_type_row.cpp | 49 +++++++++---- .../info/boosts/giveaway/giveaway_type_row.h | 9 +++ .../info/boosts/info_boosts_inner_widget.cpp | 56 +++++++++++++-- 6 files changed, 166 insertions(+), 34 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6f61de92d..44576f62f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4346,6 +4346,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boosts_list_tab_gifts#one" = "{count} Gifts"; "lng_boosts_list_tab_gifts#other" = "{count} Gifts"; +"lng_boosts_prepaid_giveaway_title" = "Prepaid giveaways"; +"lng_boosts_prepaid_giveaway_single" = "Prepaid giveaway"; +"lng_boosts_prepaid_giveaway_quantity#one" = "{count} Telegram Premium"; +"lng_boosts_prepaid_giveaway_quantity#other" = "{count} Telegram Premium"; +"lng_boosts_prepaid_giveaway_moths#one" = "{count}-month subscriptions"; +"lng_boosts_prepaid_giveaway_moths#other" = "{count}-month subscriptions"; +"lng_boosts_prepaid_giveaway_status#one" = "{count} subscription {duration}"; +"lng_boosts_prepaid_giveaway_status#other" = "{count} subscriptions {duration}"; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index ac22dc934..cd833d1e2 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -230,7 +230,8 @@ void CreateGiveawayBox( not_null box, not_null controller, not_null peer, - Fn reloadOnDone) { + Fn reloadOnDone, + std::optional prepaid) { box->setWidth(st::boxWideWidth); const auto weakWindow = base::make_weak(controller->parentController()); @@ -298,7 +299,23 @@ void CreateGiveawayBox( object_ptr(box))); contentWrap->toggle(false, anim::type::instant); - { + if (prepaid) { + contentWrap->entity()->add( + object_ptr( + box, + GiveawayType::Prepaid, + prepaid->id, + tr::lng_boosts_prepaid_giveaway_single(), + tr::lng_boosts_prepaid_giveaway_status( + lt_count, + rpl::single(prepaid->quantity) | tr::to_count(), + lt_duration, + tr::lng_premium_gift_duration_months( + lt_count, + rpl::single(prepaid->months) | tr::to_count()))) + )->setAttribute(Qt::WA_TransparentForMouseEvents); + } + if (!prepaid) { const auto row = contentWrap->entity()->add( object_ptr( box, @@ -309,7 +326,7 @@ void CreateGiveawayBox( state->typeValue.force_assign(GiveawayType::Random); }); } - { + if (!prepaid) { const auto row = contentWrap->entity()->add( object_ptr( box, @@ -387,6 +404,10 @@ void CreateGiveawayBox( object_ptr(randomWrap)); const auto fillSliderContainer = [=] { const auto availablePresets = state->apiOptions.availablePresets(); + if (prepaid) { + state->sliderValue = prepaid->quantity; + return; + } if (availablePresets.empty()) { return; } @@ -675,6 +696,9 @@ void CreateGiveawayBox( const auto listOptions = contentWrap->entity()->add( object_ptr(box)); const auto rebuildListOptions = [=](int amountUsers) { + if (prepaid) { + return; + } while (listOptions->count()) { delete listOptions->widgetAt(0); } @@ -714,8 +738,7 @@ void CreateGiveawayBox( box->verticalLayout()->resizeToWidth(box->width()); }; - { - + if (!prepaid) { rpl::combine( state->sliderValue.value(), state->typeValue.value() @@ -725,6 +748,8 @@ void CreateGiveawayBox( ? state->selectedToAward.size() : users); }, box->lifetime()); + } else { + typeGroup->setValue(GiveawayType::Random); } { using namespace Info::Statistics; @@ -783,7 +808,10 @@ void CreateGiveawayBox( isSpecific ? state->selectedToAward.size() : state->sliderValue.current(), - durationGroup->value()); + prepaid + ? prepaid->months + : state->apiOptions.monthsFromPreset( + durationGroup->value())); if (isSpecific) { if (state->selectedToAward.empty()) { return; @@ -855,7 +883,21 @@ void CreateGiveawayBox( state->confirmButtonBusy = false; } }; - Payments::CheckoutProcess::Start(std::move(invoice), done); + if (prepaid) { + state->apiOptions.applyPrepaid( + invoice, + prepaid->id + ) | rpl::start_with_error_done([=](const QString &error) { + if (const auto window = weakWindow.get()) { + window->uiShow()->showToast(error); + done(Payments::CheckoutResult::Cancelled); + } + }, [=] { + done(Payments::CheckoutResult::Paid); + }, box->lifetime()); + } else { + Payments::CheckoutProcess::Start(std::move(invoice), done); + } }); box->addButton(std::move(button)); } @@ -867,9 +909,7 @@ void CreateGiveawayBox( if (!loading->toggled()) { return; } - state->lifetimeApi = state->apiOptions.request( - ) | rpl::start_with_error_done([=](const QString &error) { - }, [=] { + const auto done = [=] { state->lifetimeApi.destroy(); loading->toggle(false, anim::type::instant); state->confirmButtonBusy = false; @@ -877,6 +917,14 @@ void CreateGiveawayBox( rebuildListOptions(1); contentWrap->toggle(true, anim::type::instant); contentWrap->resizeToWidth(box->width()); - }); + }; + if (prepaid) { + return done(); + } + state->lifetimeApi = state->apiOptions.request( + ) | rpl::start_with_error_done([=](const QString &error) { + box->uiShow()->showToast(error); + box->closeBox(); + }, done); }, box->lifetime()); } diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.h b/Telegram/SourceFiles/info/boosts/create_giveaway_box.h index d31383dc7..d1fb6736f 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.h +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class PeerData; +namespace Data { +struct BoostPrepaidGiveaway; +} // namespace Data + namespace Info { class Controller; } // namespace Info @@ -21,4 +25,5 @@ void CreateGiveawayBox( not_null box, not_null controller, not_null peer, - Fn reloadOnDone); + Fn reloadOnDone, + std::optional prepaidGiveaway); diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp index db1fedb08..e54ba3540 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "styles/style_boxes.h" #include "styles/style_giveaway.h" +#include "styles/style_statistics.h" namespace Giveaway { @@ -24,31 +25,46 @@ GiveawayTypeRow::GiveawayTypeRow( not_null parent, Type type, rpl::producer subtitle) +: GiveawayTypeRow( + parent, + type, + (type == Type::SpecificUsers) ? kColorIndexSpecific : kColorIndexRandom, + (type == Type::SpecificUsers) + ? tr::lng_giveaway_award_option() + : (type == Type::Random) + ? tr::lng_giveaway_create_option() + : (type == Type::AllMembers) + ? tr::lng_giveaway_users_all() + : tr::lng_giveaway_users_new(), + std::move(subtitle)) { +} + +GiveawayTypeRow::GiveawayTypeRow( + not_null parent, + Type type, + int colorIndex, + rpl::producer title, + rpl::producer subtitle) : RippleButton(parent, st::defaultRippleAnimation) , _type(type) , _st((_type == Type::SpecificUsers || _type == Type::Random) ? st::giveawayTypeListItem + : (_type == Type::Prepaid) + ? st::boostsListBox.item : st::giveawayGiftCodeMembersPeerList.item) , _userpic( - Ui::EmptyUserpic::UserpicColor((_type == Type::SpecificUsers) - ? kColorIndexSpecific - : kColorIndexRandom), - QString()) -, _name( - _st.nameStyle, - (type == Type::SpecificUsers) - ? tr::lng_giveaway_award_option(tr::now) - : (type == Type::Random) - ? tr::lng_giveaway_create_option(tr::now) - : (type == Type::AllMembers) - ? tr::lng_giveaway_users_all(tr::now) - : tr::lng_giveaway_users_new(tr::now), - Ui::NameTextOptions()) { + Ui::EmptyUserpic::UserpicColor(Ui::EmptyUserpic::ColorIndex(colorIndex)), + QString()) { std::move( subtitle ) | rpl::start_with_next([=] (const QString &s) { _status.setText(st::defaultTextStyle, s, Ui::NameTextOptions()); }, lifetime()); + std::move( + title + ) | rpl::start_with_next([=] (const QString &s) { + _name.setText(_st.nameStyle, s, Ui::NameTextOptions()); + }, lifetime()); } int GiveawayTypeRow::resizeGetHeight(int) { @@ -62,7 +78,10 @@ void GiveawayTypeRow::paintEvent(QPaintEvent *e) { const auto skipRight = _st.photoPosition.x(); const auto outerWidth = width(); const auto isSpecific = (_type == Type::SpecificUsers); - const auto hasUserpic = (_type == Type::Random) || isSpecific; + const auto isPrepaid = (_type == Type::Prepaid); + const auto hasUserpic = (_type == Type::Random) + || isSpecific + || isPrepaid; if (paintOver) { p.fillRect(e->rect(), _st.button.textBgOver); diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h index 0167b338e..67b1e92ad 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h @@ -25,6 +25,8 @@ public: AllMembers, OnlyNewMembers, + + Prepaid, }; GiveawayTypeRow( @@ -32,6 +34,13 @@ public: Type type, rpl::producer subtitle); + GiveawayTypeRow( + not_null parent, + Type type, + int colorIndex, + rpl::producer title, + rpl::producer subtitle); + void addRadio(std::shared_ptr> typeGroup); protected: diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index 2cfebee08..3badce3bc 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "info/boosts/create_giveaway_box.h" +#include "info/boosts/giveaway/giveaway_type_row.h" #include "info/boosts/info_boosts_widget.h" #include "info/info_controller.h" #include "info/profile/info_profile_icon.h" @@ -25,11 +26,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "statistics/widgets/chart_header_widget.h" #include "ui/boxes/boost_box.h" #include "ui/controls/invite_link_label.h" +#include "ui/effects/ripple_animation.h" +#include "ui/empty_userpic.h" +#include "ui/painter.h" #include "ui/rect.h" #include "ui/widgets/buttons.h" #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/labels.h" #include "ui/wrap/slide_wrap.h" +#include "styles/style_giveaway.h" #include "styles/style_info.h" #include "styles/style_statistics.h" @@ -230,7 +235,12 @@ void FillGetBoostsButton( tr::lng_boosts_get_boosts(), st)); button->setClickedCallback([=] { - show->showBox(Box(CreateGiveawayBox, controller, peer, reloadOnDone)); + show->showBox(Box( + CreateGiveawayBox, + controller, + peer, + reloadOnDone, + std::nullopt)); }); Ui::CreateChild( button, @@ -280,6 +290,13 @@ void InnerWidget::fill() { const auto &status = _state; const auto inner = this; + const auto reloadOnDone = crl::guard(this, [=] { + while (Ui::VerticalLayout::count()) { + delete Ui::VerticalLayout::widgetAt(0); + } + load(); + }); + { auto dividerContent = object_ptr(inner); Ui::FillBoostLimit( @@ -308,6 +325,37 @@ void InnerWidget::fill() { ::Settings::AddDivider(inner); ::Settings::AddSkip(inner); + if (!status.prepaidGiveaway.empty()) { + ::Settings::AddSkip(inner); + AddHeader(inner, tr::lng_boosts_prepaid_giveaway_title); + ::Settings::AddSkip(inner); + for (const auto &g : status.prepaidGiveaway) { + using namespace Giveaway; + const auto button = inner->add(object_ptr( + inner, + GiveawayTypeRow::Type::Prepaid, + g.id, + tr::lng_boosts_prepaid_giveaway_quantity( + lt_count, + rpl::single(g.quantity) | tr::to_count()), + tr::lng_boosts_prepaid_giveaway_moths( + lt_count, + rpl::single(g.months) | tr::to_count()))); + button->setClickedCallback([=] { + _controller->uiShow()->showBox(Box( + CreateGiveawayBox, + _controller, + _peer, + reloadOnDone, + g)); + }); + } + + ::Settings::AddSkip(inner); + ::Settings::AddDivider(inner); + ::Settings::AddSkip(inner); + } + const auto hasBoosts = (status.firstSliceBoosts.multipliedTotal > 0); const auto hasGifts = (status.firstSliceGifts.multipliedTotal > 0); if (hasBoosts || hasGifts) { @@ -429,12 +477,6 @@ void InnerWidget::fill() { ::Settings::AddSkip(inner); ::Settings::AddDividerText(inner, tr::lng_boosts_link_subtext()); - const auto reloadOnDone = crl::guard(this, [=] { - while (Ui::VerticalLayout::count()) { - delete Ui::VerticalLayout::widgetAt(0); - } - load(); - }); FillGetBoostsButton(inner, _controller, _show, _peer, reloadOnDone); resizeToWidth(width()); From 27e2a4b5d20b4d743eacfd0fdfcec17501fa8b29 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 8 Nov 2023 17:05:11 +0300 Subject: [PATCH 23/41] Fixed width of tabs for different boosts lists. --- .../info/boosts/info_boosts_inner_widget.cpp | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index 3badce3bc..2e6ff51f8 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -410,12 +410,25 @@ void InnerWidget::fill() { header->setSubTitle({}); } + class Slider final : public Ui::SettingsSlider { + public: + using Ui::SettingsSlider::SettingsSlider; + void setNaturalWidth(int w) { + _naturalWidth = w; + } + int naturalWidth() const override { + return _naturalWidth; + } + + private: + int _naturalWidth = 0; + + }; + const auto slider = inner->add( - object_ptr>( + object_ptr>( inner, - object_ptr( - inner, - st::defaultTabsSlider)), + object_ptr(inner, st::defaultTabsSlider)), st::boxRowPadding); slider->toggle(!hasOneTab, anim::type::instant); @@ -424,15 +437,10 @@ void InnerWidget::fill() { { const auto &st = st::defaultTabsSlider; - const auto sliderWidth = st.labelStyle.font->width(boostsTabText) + slider->entity()->setNaturalWidth(0 + + st.labelStyle.font->width(boostsTabText) + st.labelStyle.font->width(giftsTabText) - + rect::m::sum::h(st::boxRowPadding); - fakeShowed->events() | rpl::take(1) | rpl::map_to(-1) | rpl::then( - slider->entity()->widthValue() - ) | rpl::distinct_until_changed( - ) | rpl::start_with_next([=](int) { - slider->entity()->resizeToWidth(sliderWidth); - }, slider->lifetime()); + + rect::m::sum::h(st::boxRowPadding)); } const auto boostsWrap = inner->add( @@ -444,7 +452,7 @@ void InnerWidget::fill() { inner, object_ptr(inner))); - rpl::single(hasGifts ? 1 : 0) | rpl::then( + rpl::single(hasOneTab ? (hasGifts ? 1 : 0) : 0) | rpl::then( slider->entity()->sectionActivated() ) | rpl::start_with_next([=](int index) { boostsWrap->toggle(!index, anim::type::instant); From 9d4e1689fa33f359b634c6a716134207f085312f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 8 Nov 2023 18:10:48 +0300 Subject: [PATCH 24/41] Added state restoring to list for boost awarding members. --- .../SourceFiles/info/boosts/create_giveaway_box.cpp | 3 ++- .../boosts/giveaway/giveaway_list_controllers.cpp | 12 ++++++++++-- .../info/boosts/giveaway/giveaway_list_controllers.h | 7 ++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index cd833d1e2..605c5681b 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -367,7 +367,8 @@ void CreateGiveawayBox( using Controller = Giveaway::AwardMembersListController; auto listController = std::make_unique( controller, - peer); + peer, + state->selectedToAward); listController->setCheckError(CreateErrorCallback( state->apiOptions.giveawayAddPeersMax(), tr::lng_giveaway_maximum_users_error)); diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.cpp b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.cpp index dd6df700f..f65fda97d 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.cpp +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.cpp @@ -107,8 +107,16 @@ void ChannelRow::rightActionStopLastRipple() { AwardMembersListController::AwardMembersListController( not_null navigation, - not_null peer) -: ParticipantsBoxController(navigation, peer, ParticipantsRole::Members) { + not_null peer, + std::vector> selected) +: ParticipantsBoxController(navigation, peer, ParticipantsRole::Members) +, _selected(std::move(selected)) { +} + +void AwardMembersListController::prepare() { + ParticipantsBoxController::prepare(); + delegate()->peerListAddSelectedPeers(base::take(_selected)); + delegate()->peerListRefreshRows(); } void AwardMembersListController::rowClicked(not_null row) { diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.h b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.h index 8d08a8a15..8f3f44ecf 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.h +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.h @@ -27,7 +27,10 @@ class AwardMembersListController : public ParticipantsBoxController { public: AwardMembersListController( not_null navigation, - not_null peer); + not_null peer, + std::vector> selected); + + void prepare() override; void setCheckError(Fn callback); @@ -41,6 +44,8 @@ public: private: Fn _checkErrorCallback; + std::vector> _selected; + }; class MyChannelsListController : public PeerListController { From 135ffb13fdd531aa4ece1d4cc057c9c0b7a03513 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 8 Nov 2023 18:45:01 +0300 Subject: [PATCH 25/41] Added all joined channels to list in giveaway box. --- .../giveaway/giveaway_list_controllers.cpp | 42 ++++++++++++++++++- .../giveaway/giveaway_list_controllers.h | 4 ++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.cpp b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.cpp index f65fda97d..975dab5b3 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.cpp +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.cpp @@ -9,9 +9,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "data/data_channel.h" +#include "data/data_folder.h" #include "data/data_peer.h" #include "data/data_session.h" #include "data/data_user.h" +#include "dialogs/dialogs_indexed_list.h" +#include "history/history.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/boxes/confirm_box.h" @@ -156,7 +159,26 @@ MyChannelsListController::MyChannelsListController( std::make_unique(&peer->session())) , _peer(peer) , _show(show) -, _selected(std::move(selected)) { +, _selected(std::move(selected)) +, _otherChannels(std::make_unique>>()) { + { + const auto addList = [&](not_null list) { + for (const auto &row : list->all()) { + if (const auto history = row->history()) { + const auto channel = history->peer->asChannel(); + if (channel && !channel->isMegagroup()) { + _otherChannels->push_back(channel); + } + } + } + }; + auto &data = _peer->owner(); + addList(data.chatsList()->indexed()); + if (const auto folder = data.folderLoaded(Data::Folder::kId)) { + addList(folder->chatsList()->indexed()); + } + addList(data.contactsNoChatsList()); + } } std::unique_ptr MyChannelsListController::createSearchRow( @@ -175,6 +197,24 @@ std::unique_ptr MyChannelsListController::createRestoredRow( return nullptr; } +void MyChannelsListController::loadMoreRows() { + if (_apiLifetime || !_otherChannels) { + return; + } else if (_lastAddedIndex >= _otherChannels->size()) { + _otherChannels.release(); + return; + } + constexpr auto kPerPage = int(40); + const auto till = std::min( + int(_otherChannels->size()), + _lastAddedIndex + kPerPage); + while (_lastAddedIndex < till) { + delegate()->peerListAppendRow( + createRow(_otherChannels->at(_lastAddedIndex++))); + } + delegate()->peerListRefreshRows(); +} + void MyChannelsListController::rowClicked(not_null row) { const auto channel = row->peer()->asChannel(); const auto checked = !row->checked(); diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.h b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.h index 8f3f44ecf..6459d6d8f 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.h +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_list_controllers.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_participants_box.h" +class ChannelData; class PeerData; class PeerListRow; @@ -60,6 +61,7 @@ public: Main::Session &session() const override; void prepare() override; void rowClicked(not_null row) override; + void loadMoreRows() override; std::unique_ptr createSearchRow( not_null peer) override; @@ -76,6 +78,8 @@ private: Fn _checkErrorCallback; std::vector> _selected; + std::unique_ptr>> _otherChannels; + int _lastAddedIndex = 0; rpl::lifetime _apiLifetime; From 282c076931de50c17f9952893e514ba8f72f7445 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 8 Nov 2023 23:43:42 +0300 Subject: [PATCH 26/41] Removed redundant peerListShowBox / peerListHideLayer from delegates. --- .../boxes/filters/edit_filter_chats_list.cpp | 2 +- .../boxes/filters/edit_filter_links.cpp | 18 ++++++++--------- Telegram/SourceFiles/boxes/peer_list_box.cpp | 20 ------------------- Telegram/SourceFiles/boxes/peer_list_box.h | 20 ------------------- .../boxes/peer_list_controllers.cpp | 2 +- Telegram/SourceFiles/boxes/peer_lists_box.cpp | 10 ---------- Telegram/SourceFiles/boxes/peer_lists_box.h | 4 ---- .../boxes/peers/add_participants_box.cpp | 4 ++-- .../boxes/peers/choose_peer_box.cpp | 2 +- .../boxes/peers/edit_linked_chat_box.cpp | 4 ++-- .../boxes/peers/edit_participants_box.cpp | 2 +- .../boxes/peers/edit_peer_invite_link.cpp | 15 ++++++++------ .../boxes/peers/edit_peer_invite_links.cpp | 16 ++++++++------- .../SourceFiles/boxes/premium_limits_box.cpp | 12 ----------- .../calls/group/calls_group_members.cpp | 8 -------- .../calls/group/calls_group_members.h | 4 ---- .../info_common_groups_inner_widget.cpp | 10 ---------- .../info_common_groups_inner_widget.h | 4 ---- .../polls/info_polls_results_inner_widget.cpp | 12 ----------- .../info/profile/info_profile_members.cpp | 10 ---------- .../info/profile/info_profile_members.h | 4 ---- 21 files changed, 35 insertions(+), 148 deletions(-) diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp index 61e937dfe..989a9867b 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -362,7 +362,7 @@ void EditFilterChatsListController::rowClicked(not_null row) { delegate()->peerListSetRowChecked(row, !row->checked()); updateTitle(); } else { - delegate()->peerListShowBox(_limitBox(count)); + delegate()->peerListUiShow()->showBox(_limitBox(count)); } } diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp index d731ead51..9dcf6fd80 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp @@ -575,19 +575,19 @@ void LinkController::addLinkBlock(not_null container) { CopyInviteLink(delegate()->peerListUiShow(), link); }); const auto shareLink = crl::guard(weak, [=] { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( ShareInviteLinkBox(&_window->session(), link)); }); const auto getLinkQr = crl::guard(weak, [=] { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( InviteLinkQrBox(link, tr::lng_filters_link_qr_about())); }); const auto editLink = crl::guard(weak, [=] { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( Box(ChatFilterLinkBox, &_window->session(), _data)); }); const auto deleteLink = crl::guard(weak, [=] { - delegate()->peerListShowBox(DeleteLinkBox(_window, _data)); + delegate()->peerListUiShow()->showBox(DeleteLinkBox(_window, _data)); }); const auto createMenu = [=] { @@ -846,7 +846,7 @@ void LinksController::rebuild(const std::vector &rows) { void LinksController::rowClicked(not_null row) { const auto link = static_cast(row.get())->data(); - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( ShowLinkBox(_window, _currentFilter(), link)); } @@ -881,19 +881,19 @@ base::unique_qptr LinksController::createRowContextMenu( CopyInviteLink(delegate()->peerListUiShow(), link); }; const auto shareLink = [=] { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( ShareInviteLinkBox(&_window->session(), link)); }; const auto getLinkQr = [=] { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( InviteLinkQrBox(link, tr::lng_filters_link_qr_about())); }; const auto editLink = [=] { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( Box(ChatFilterLinkBox, &_window->session(), data)); }; const auto deleteLink = [=] { - delegate()->peerListShowBox(DeleteLinkBox(_window, data)); + delegate()->peerListUiShow()->showBox(DeleteLinkBox(_window, data)); }; auto result = base::make_unique_q( parent, diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index abd4f4b32..189a13215 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -85,16 +85,6 @@ PeerListContentDelegateShow::PeerListContentDelegateShow( : _show(show) { } -void PeerListContentDelegateShow::peerListShowBox( - object_ptr content, - Ui::LayerOptions options) { - _show->showBox(std::move(content), options); -} - -void PeerListContentDelegateShow::peerListHideLayer() { - _show->hideLayer(); -} - auto PeerListContentDelegateShow::peerListUiShow() -> std::shared_ptr{ return _show; @@ -324,16 +314,6 @@ void PeerListBox::peerListSetSearchMode(PeerListSearchMode mode) { } } -void PeerListBox::peerListShowBox( - object_ptr content, - Ui::LayerOptions options) { - _show->showBox(std::move(content), options); -} - -void PeerListBox::peerListHideLayer() { - _show->hideLayer(); -} - std::shared_ptr PeerListBox::peerListUiShow() { return _show; } diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 0f557da68..1a53254e1 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -332,10 +332,6 @@ public: virtual std::optional peerListLastRowMousePosition() = 0; virtual void peerListSortRows(Fn compare) = 0; virtual int peerListPartitionRows(Fn border) = 0; - virtual void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) = 0; - virtual void peerListHideLayer() = 0; virtual std::shared_ptr peerListUiShow() = 0; template @@ -1007,14 +1003,6 @@ public: object_ptr description) override { description.destroy(); } - void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) override { - Unexpected("...DelegateSimple::peerListShowBox"); - } - void peerListHideLayer() override { - Unexpected("...DelegateSimple::peerListHideLayer"); - } std::shared_ptr peerListUiShow() override { Unexpected("...DelegateSimple::peerListUiShow"); } @@ -1025,10 +1013,6 @@ class PeerListContentDelegateShow : public PeerListContentDelegateSimple { public: explicit PeerListContentDelegateShow( std::shared_ptr show); - void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) override; - void peerListHideLayer() override; std::shared_ptr peerListUiShow() override; private: @@ -1064,10 +1048,6 @@ public: bool peerListIsRowChecked(not_null row) override; int peerListSelectedRowsCount() override; void peerListScrollToTop() override; - void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) override; - void peerListHideLayer() override; std::shared_ptr peerListUiShow() override; void setAddedTopScrollSkip(int skip); diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index df1a24fba..bad9dbd89 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -685,7 +685,7 @@ void ChooseRecipientBoxController::rowClicked(not_null row) { }, box->lifetime()); }); *weak = owned.data(); - delegate()->peerListShowBox(std::move(owned)); + delegate()->peerListUiShow()->showBox(std::move(owned)); return; } const auto history = peer->owner().history(peer); diff --git a/Telegram/SourceFiles/boxes/peer_lists_box.cpp b/Telegram/SourceFiles/boxes/peer_lists_box.cpp index e445a21e2..80e2a9334 100644 --- a/Telegram/SourceFiles/boxes/peer_lists_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_lists_box.cpp @@ -372,16 +372,6 @@ void PeerListsBox::Delegate::peerListFinishSelectedRowsBunch() { _box->_select->entity()->finishItemsBunch(); } -void PeerListsBox::Delegate::peerListShowBox( - object_ptr content, - Ui::LayerOptions options) { - _show->showBox(std::move(content), options); -} - -void PeerListsBox::Delegate::peerListHideLayer() { - _show->hideLayer(); -} - auto PeerListsBox::Delegate::peerListUiShow() -> std::shared_ptr { return _show; diff --git a/Telegram/SourceFiles/boxes/peer_lists_box.h b/Telegram/SourceFiles/boxes/peer_lists_box.h index 94732b3e2..20377548a 100644 --- a/Telegram/SourceFiles/boxes/peer_lists_box.h +++ b/Telegram/SourceFiles/boxes/peer_lists_box.h @@ -54,10 +54,6 @@ private: _box->addSelectItem(row, anim::type::instant); } void peerListFinishSelectedRowsBunch() override; - void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) override; - void peerListHideLayer() override; std::shared_ptr peerListUiShow() override; private: diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index b40e1a0d2..575522e42 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -366,7 +366,7 @@ bool AddParticipantsBoxController::needsInviteLinkButton() { QPointer AddParticipantsBoxController::showBox( object_ptr box) const { const auto weak = Ui::MakeWeak(box.data()); - delegate()->peerListShowBox(std::move(box)); + delegate()->peerListUiShow()->showBox(std::move(box)); return weak; } @@ -668,7 +668,7 @@ void AddSpecialBoxController::migrate( QPointer AddSpecialBoxController::showBox( object_ptr box) const { const auto weak = Ui::MakeWeak(box.data()); - delegate()->peerListShowBox(std::move(box)); + delegate()->peerListUiShow()->showBox(std::move(box)); return weak; } diff --git a/Telegram/SourceFiles/boxes/peers/choose_peer_box.cpp b/Telegram/SourceFiles/boxes/peers/choose_peer_box.cpp index 767b0c915..7e27128c4 100644 --- a/Telegram/SourceFiles/boxes/peers/choose_peer_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/choose_peer_box.cpp @@ -433,7 +433,7 @@ void ChoosePeerBoxController::rowClicked(not_null row) { if (const auto user = peer->asUser()) { done(); } else { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( MakeConfirmBox(_bot, peer, _query, done)); } } diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp index 48bc383c9..c3e4b3d34 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp @@ -168,7 +168,7 @@ void Controller::choose(not_null chat) { const auto onstack = _callback; onstack(chat); }; - delegate()->peerListShowBox(Ui::MakeConfirmBox({ + delegate()->peerListUiShow()->showBox(Ui::MakeConfirmBox({ .text = text, .confirmed = sure, .confirmText = tr::lng_manage_discussion_group_link(tr::now), @@ -199,7 +199,7 @@ void Controller::choose(not_null chat) { }; chat->session().api().migrateChat(chat, crl::guard(this, done)); }; - delegate()->peerListShowBox(Ui::MakeConfirmBox({ + delegate()->peerListUiShow()->showBox(Ui::MakeConfirmBox({ .text = text, .confirmed = sure, .confirmText = tr::lng_manage_discussion_group_link(tr::now), diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 425e21c75..1801cc154 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -1281,7 +1281,7 @@ void ParticipantsBoxController::rebuild() { QPointer ParticipantsBoxController::showBox( object_ptr box) const { const auto weak = Ui::MakeWeak(box.data()); - delegate()->peerListShowBox(std::move(box)); + delegate()->peerListUiShow()->showBox(std::move(box)); return weak; } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 00f9eac3f..f43112511 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -347,21 +347,24 @@ void Controller::addHeaderBlock(not_null container) { const auto copyLink = crl::guard(weak, [=] { CopyInviteLink(delegate()->peerListUiShow(), link); }); - const auto shareLink = crl::guard(weak, [=] { - delegate()->peerListShowBox(ShareInviteLinkBox(_peer, link)); + const auto shareLink = crl::guard(weak, [=, peer = _peer] { + delegate()->peerListUiShow()->showBox(ShareInviteLinkBox(peer, link)); }); const auto getLinkQr = crl::guard(weak, [=] { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( InviteLinkQrBox(link, tr::lng_group_invite_qr_about())); }); const auto revokeLink = crl::guard(weak, [=] { - delegate()->peerListShowBox(RevokeLinkBox(_peer, admin, link)); + delegate()->peerListUiShow()->showBox( + RevokeLinkBox(_peer, admin, link)); }); const auto editLink = crl::guard(weak, [=] { - delegate()->peerListShowBox(EditLinkBox(_peer, _data.current())); + delegate()->peerListUiShow()->showBox( + EditLinkBox(_peer, _data.current())); }); const auto deleteLink = crl::guard(weak, [=] { - delegate()->peerListShowBox(DeleteLinkBox(_peer, admin, link)); + delegate()->peerListUiShow()->showBox( + DeleteLinkBox(_peer, admin, link)); }); const auto createMenu = [=] { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 4d7254388..7ed35cdf8 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -537,7 +537,7 @@ void LinksController::appendSlice(const InviteLinksSlice &slice) { } void LinksController::rowClicked(not_null row) { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( ShowInviteLinkBox(_peer, static_cast(row.get())->data())); } @@ -573,25 +573,27 @@ base::unique_qptr LinksController::createRowContextMenu( st::popupMenuWithIcons); if (data.revoked) { result->addAction(tr::lng_group_invite_context_delete(tr::now), [=] { - delegate()->peerListShowBox(DeleteLinkBox(_peer, _admin, link)); + delegate()->peerListUiShow()->showBox( + DeleteLinkBox(_peer, _admin, link)); }, &st::menuIconDelete); } else { result->addAction(tr::lng_group_invite_context_copy(tr::now), [=] { CopyInviteLink(delegate()->peerListUiShow(), link); }, &st::menuIconCopy); result->addAction(tr::lng_group_invite_context_share(tr::now), [=] { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( ShareInviteLinkBox(_peer, link)); }, &st::menuIconShare); result->addAction(tr::lng_group_invite_context_qr(tr::now), [=] { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( InviteLinkQrBox(link, tr::lng_group_invite_qr_about())); }, &st::menuIconQrCode); result->addAction(tr::lng_group_invite_context_edit(tr::now), [=] { - delegate()->peerListShowBox(EditLinkBox(_peer, data)); + delegate()->peerListUiShow()->showBox(EditLinkBox(_peer, data)); }, &st::menuIconEdit); result->addAction(tr::lng_group_invite_context_revoke(tr::now), [=] { - delegate()->peerListShowBox(RevokeLinkBox(_peer, _admin, link)); + delegate()->peerListUiShow()->showBox( + RevokeLinkBox(_peer, _admin, link)); }, &st::menuIconRemove); } return result; @@ -799,7 +801,7 @@ void AdminsController::loadMoreRows() { } void AdminsController::rowClicked(not_null row) { - delegate()->peerListShowBox( + delegate()->peerListUiShow()->showBox( Box(ManageInviteLinksBox, _peer, row->peer()->asUser(), 0, 0)); } diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.cpp b/Telegram/SourceFiles/boxes/premium_limits_box.cpp index 928915afd..9ce74e022 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_limits_box.cpp @@ -115,10 +115,6 @@ public: void peerListFinishSelectedRowsBunch() override; void peerListSetDescription( object_ptr description) override; - void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) override; - void peerListHideLayer() override; std::shared_ptr peerListUiShow() override; void peerListSetRowChecked( not_null row, @@ -183,14 +179,6 @@ void InactiveDelegate::peerListSetDescription( description.destroy(); } -void InactiveDelegate::peerListShowBox( - object_ptr content, - Ui::LayerOptions options) { -} - -void InactiveDelegate::peerListHideLayer() { -} - std::shared_ptr InactiveDelegate::peerListUiShow() { Unexpected("...InactiveDelegate::peerListUiShow"); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index cc3c33ddb..586583ad8 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -1975,14 +1975,6 @@ void Members::peerListSetDescription( description.destroy(); } -void Members::peerListShowBox( - object_ptr content, - Ui::LayerOptions options) { -} - -void Members::peerListHideLayer() { -} - std::shared_ptr Members::peerListUiShow() { Unexpected("...Members::peerListUiShow"); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.h b/Telegram/SourceFiles/calls/group/calls_group_members.h index f8b4c8c52..47cb05914 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.h +++ b/Telegram/SourceFiles/calls/group/calls_group_members.h @@ -88,10 +88,6 @@ private: void peerListFinishSelectedRowsBunch() override; void peerListSetDescription( object_ptr description) override; - void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) override; - void peerListHideLayer() override; std::shared_ptr peerListUiShow() override; void setupAddMember(not_null call); diff --git a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp index fed48653b..720f47eba 100644 --- a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp +++ b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp @@ -281,16 +281,6 @@ void InnerWidget::peerListSetDescription( description.destroy(); } -void InnerWidget::peerListShowBox( - object_ptr content, - Ui::LayerOptions options) { - _show->showBox(std::move(content), options); -} - -void InnerWidget::peerListHideLayer() { - _show->hideLayer(); -} - std::shared_ptr InnerWidget::peerListUiShow() { return _show; } diff --git a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.h b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.h index 91d906782..9cfb2f3a8 100644 --- a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.h +++ b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.h @@ -64,10 +64,6 @@ private: void peerListFinishSelectedRowsBunch() override; void peerListSetDescription( object_ptr description) override; - void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) override; - void peerListHideLayer() override; std::shared_ptr peerListUiShow() override; object_ptr setupList( diff --git a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp index 5fc2308a5..b1cefdeff 100644 --- a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp +++ b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp @@ -51,10 +51,6 @@ public: void peerListFinishSelectedRowsBunch() override; void peerListSetDescription( object_ptr description) override; - void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) override; - void peerListHideLayer() override; std::shared_ptr peerListUiShow() override; }; @@ -92,14 +88,6 @@ void ListDelegate::peerListSetDescription( description.destroy(); } -void ListDelegate::peerListShowBox( - object_ptr content, - Ui::LayerOptions options) { -} - -void ListDelegate::peerListHideLayer() { -} - std::shared_ptr ListDelegate::peerListUiShow() { Unexpected("...ListDelegate::peerListUiShow"); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.cpp b/Telegram/SourceFiles/info/profile/info_profile_members.cpp index 85e689903..0574dd6da 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_members.cpp @@ -459,16 +459,6 @@ void Members::peerListAddSelectedRowInBunch(not_null row) { void Members::peerListFinishSelectedRowsBunch() { } -void Members::peerListShowBox( - object_ptr content, - Ui::LayerOptions options) { - _show->showBox(std::move(content), options); -} - -void Members::peerListHideLayer() { - _show->hideLayer(); -} - std::shared_ptr Members::peerListUiShow() { return _show; } diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.h b/Telegram/SourceFiles/info/profile/info_profile_members.h index 2789756ce..ac86ca813 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members.h +++ b/Telegram/SourceFiles/info/profile/info_profile_members.h @@ -78,10 +78,6 @@ private: void peerListFinishSelectedRowsBunch() override; void peerListSetDescription( object_ptr description) override; - void peerListShowBox( - object_ptr content, - Ui::LayerOptions options = Ui::LayerOption::KeepOther) override; - void peerListHideLayer() override; std::shared_ptr peerListUiShow() override; //void peerListAppendRow( From 33cf9a0702bc88607f670d9da285e6fa5840071c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 8 Nov 2023 23:54:52 +0300 Subject: [PATCH 27/41] Fixed expiration date of boosts in boosts list. --- Telegram/SourceFiles/api/api_statistics.cpp | 4 +++- Telegram/SourceFiles/data/data_boosts.h | 3 ++- .../SourceFiles/info/boosts/info_boosts_inner_widget.cpp | 8 ++------ .../info/statistics/info_statistics_list_controllers.cpp | 9 +++------ 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index c5fb3ca76..9f2dd2cd3 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -587,6 +587,7 @@ void Boosts::requestBoosts( auto list = std::vector(); list.reserve(data.vboosts().v.size()); + constexpr auto kMonthsDivider = int(30 * 86400); for (const auto &boost : data.vboosts().v) { const auto &data = boost.data(); const auto path = data.vused_gift_slug() @@ -609,7 +610,8 @@ void Boosts::requestBoosts( ? FullMsgId{ _peer->id, data.vgiveaway_msg_id()->v } : FullMsgId(), QDateTime::fromSecsSinceEpoch(data.vdate().v), - data.vexpires().v, + QDateTime::fromSecsSinceEpoch(data.vexpires().v), + (data.vexpires().v - data.vdate().v) / kMonthsDivider, std::move(giftCodeLink), data.vmultiplier().value_or_empty(), }); diff --git a/Telegram/SourceFiles/data/data_boosts.h b/Telegram/SourceFiles/data/data_boosts.h index 0dad4b008..08fac640b 100644 --- a/Telegram/SourceFiles/data/data_boosts.h +++ b/Telegram/SourceFiles/data/data_boosts.h @@ -34,7 +34,8 @@ struct Boost final { UserId userId = UserId(0); FullMsgId giveawayMessage; QDateTime date; - crl::time expiresAt = 0; + QDateTime expiresAt; + int expiresAfterMonths = 0; GiftCodeLink giftCodeLink; int multiplier = 0; }; diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index 2e6ff51f8..e4abe223f 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -365,15 +365,11 @@ void InnerWidget::fill() { } else if (boost.userId) { const auto user = _peer->owner().user(boost.userId); if (boost.isGift || boost.isGiveaway) { - constexpr auto kMonthsDivider = int(30 * 86400); - const auto date = TimeId(boost.date.toSecsSinceEpoch()); - const auto months = (boost.expiresAt - date) - / kMonthsDivider; const auto d = Api::GiftCode{ .from = _peer->id, .to = user->id, - .date = date, - .months = int(months), + .date = TimeId(boost.date.toSecsSinceEpoch()), + .months = boost.expiresAfterMonths, }; _show->showBox(Box(GiftCodePendingBox, _controller, d)); } else { diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 7dd49517b..0f2e93cd4 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -391,19 +391,16 @@ BoostRow::BoostRow(const Data::Boost &boost) void BoostRow::init() { invalidateBadges(); - constexpr auto kMonthsDivider = int(30 * 86400); - const auto months = (_boost.expiresAt - _boost.date.toSecsSinceEpoch()) - / kMonthsDivider; auto status = !PeerListRow::special() ? tr::lng_boosts_list_status( tr::now, lt_date, - langDateTime(_boost.date)) - : tr::lng_months_tiny(tr::now, lt_count, months) + langDayOfMonth(_boost.expiresAt.date())) + : tr::lng_months_tiny(tr::now, lt_count, _boost.expiresAfterMonths) + ' ' + QChar(0x2022) + ' ' - + langDateTime(_boost.date); + + langDayOfMonth(_boost.date.date()); PeerListRow::setCustomStatus(std::move(status)); } From f7ab8298cf5bc5ec9804cef4d61c4df220911174 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 9 Nov 2023 00:53:31 +0300 Subject: [PATCH 28/41] Added show more button to statistics info for list of recent messages. --- .../info/statistics/info_statistics_common.h | 1 + .../info_statistics_inner_widget.cpp | 59 ++++++++++++++----- .../info_statistics_list_controllers.cpp | 3 +- .../info_statistics_recent_message.cpp | 1 + 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_common.h b/Telegram/SourceFiles/info/statistics/info_statistics_common.h index 30a3e092f..cc943ca68 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_common.h +++ b/Telegram/SourceFiles/info/statistics/info_statistics_common.h @@ -15,6 +15,7 @@ struct SavedState final { Data::AnyStatistics stats; base::flat_map recentPostPreviews; Data::PublicForwardsSlice publicForwardsFirstSlice; + int recentPostsExpanded = 0; }; } // namespace Info::Statistics diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp index b5b2ae8b0..19704c779 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_statistics.h" #include "apiwrap.h" +#include "base/call_delayed.h" #include "base/event_filter.h" #include "data/data_peer.h" #include "data/data_session.h" @@ -697,25 +698,53 @@ void InnerWidget::fillRecentPosts() { } }; - auto foundLoaded = false; - for (const auto &recent : stats.recentMessageInteractions) { - const auto messageWrap = content->add( - object_ptr(content)); - const auto msgId = recent.messageId; - if (const auto item = _peer->owner().message(_peer, msgId)) { - addMessage(messageWrap, item, recent); - foundLoaded = true; - continue; + const auto buttonWrap = container->add( + object_ptr>( + container, + object_ptr( + container, + tr::lng_stories_show_more()))); + + constexpr auto kPerPage = int(10); + const auto max = stats.recentMessageInteractions.size(); + if (_state.recentPostsExpanded) { + _state.recentPostsExpanded = std::max( + _state.recentPostsExpanded - kPerPage, + 0); + } + const auto showMore = [=] { + const auto from = _state.recentPostsExpanded; + _state.recentPostsExpanded = std::min( + int(max), + _state.recentPostsExpanded + kPerPage); + if (_state.recentPostsExpanded == max) { + buttonWrap->toggle(false, anim::type::instant); } - const auto callback = crl::guard(content, [=] { + for (auto i = from; i < _state.recentPostsExpanded; i++) { + const auto &recent = stats.recentMessageInteractions[i]; + const auto messageWrap = content->add( + object_ptr(content)); + const auto msgId = recent.messageId; if (const auto item = _peer->owner().message(_peer, msgId)) { addMessage(messageWrap, item, recent); - content->resizeToWidth(content->width()); + continue; } - }); - _peer->session().api().requestMessageData(_peer, msgId, callback); - } - if (!foundLoaded) { + const auto callback = crl::guard(content, [=] { + if (const auto item = _peer->owner().message(_peer, msgId)) { + addMessage(messageWrap, item, recent); + content->resizeToWidth(content->width()); + } + }); + _peer->session().api().requestMessageData(_peer, msgId, callback); + } + container->resizeToWidth(container->width()); + }; + const auto delay = st::defaultRippleAnimation.hideDuration; + buttonWrap->entity()->setClickedCallback([=] { + base::call_delayed(delay, crl::guard(container, showMore)); + }); + showMore(); + if (_messagePreviews.empty()) { wrap->toggle(false, anim::type::instant); } } diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 0f2e93cd4..498acdbd1 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -696,7 +696,6 @@ void AddMembersList( container, tr::lng_stories_show_more())), { 0, -st::settingsButton.padding.top(), 0, 0 }); - const auto button = wrap->entity(); const auto showMore = [=] { state->limit = std::min(int(max), state->limit + kPerPage); @@ -706,7 +705,7 @@ void AddMembersList( } container->resizeToWidth(container->width()); }; - button->setClickedCallback(showMore); + wrap->entity()->setClickedCallback(showMore); showMore(); } diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_recent_message.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_recent_message.cpp index 1a85458e9..022da23a8 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_recent_message.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_recent_message.cpp @@ -215,6 +215,7 @@ void MessagePreview::paintEvent(QPaintEvent *e) { .spoiler = Ui::Text::DefaultSpoilerCache(), .now = crl::now(), .elisionHeight = st::statisticsDetailsPopupHeaderStyle.font->height, + .elisionLines = 1, }); _views.draw(p, { .position = { width() - _viewsWidth, topTextTop }, From 97bd8659163110b9d418341d57059642562d8321 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 9 Nov 2023 15:50:26 +0300 Subject: [PATCH 29/41] Added terms to bottom of giveaway box for prepaid giveaways. --- .../info/boosts/create_giveaway_box.cpp | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index 605c5681b..a38a34234 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -648,6 +648,24 @@ void CreateGiveawayBox( Settings::AddSkip(countriesContainer); } + const auto addTerms = [=](not_null c) { + auto terms = object_ptr( + c, + tr::lng_premium_gift_terms( + lt_link, + tr::lng_premium_gift_terms_link( + ) | rpl::map([](const QString &t) { + return Ui::Text::Link(t, 1); + }), + Ui::Text::WithEntities), + st::boxDividerLabel); + terms->setLink(1, std::make_shared([=] { + box->closeBox(); + Settings::ShowPremium(&peer->session(), QString()); + })); + c->add(std::move(terms)); + }; + { const auto dateContainer = randomWrap->entity()->add( object_ptr(randomWrap)); @@ -685,12 +703,29 @@ void CreateGiveawayBox( }); Settings::AddSkip(dateContainer); - Settings::AddDividerText( - dateContainer, - tr::lng_giveaway_date_about( - lt_count, - state->sliderValue.value() | tr::to_count())); - Settings::AddSkip(dateContainer); + if (prepaid) { + auto terms = object_ptr(dateContainer); + terms->add(object_ptr( + terms, + tr::lng_giveaway_date_about( + lt_count, + state->sliderValue.value() | tr::to_count()), + st::boxDividerLabel)); + Settings::AddSkip(terms.data()); + Settings::AddSkip(terms.data()); + addTerms(terms.data()); + dateContainer->add(object_ptr( + dateContainer, + std::move(terms), + st::settingsDividerLabelPadding)); + } else { + Settings::AddDividerText( + dateContainer, + tr::lng_giveaway_date_about( + lt_count, + state->sliderValue.value() | tr::to_count())); + Settings::AddSkip(dateContainer); + } } const auto durationGroup = std::make_shared(0); @@ -718,23 +753,11 @@ void CreateGiveawayBox( Settings::AddSkip(listOptions); - auto terms = object_ptr( - listOptions, - tr::lng_premium_gift_terms( - lt_link, - tr::lng_premium_gift_terms_link( - ) | rpl::map([](const QString &t) { - return Ui::Text::Link(t, 1); - }), - Ui::Text::WithEntities), - st::boxDividerLabel); - terms->setLink(1, std::make_shared([=] { - box->closeBox(); - Settings::ShowPremium(&peer->session(), QString()); - })); + auto termsContainer = object_ptr(listOptions); + addTerms(termsContainer.data()); listOptions->add(object_ptr( listOptions, - std::move(terms), + std::move(termsContainer), st::settingsDividerLabelPadding)); box->verticalLayout()->resizeToWidth(box->width()); From ca1a548fb817c1167811e0f1e1c618b7642cdcc2 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 9 Nov 2023 16:27:54 +0300 Subject: [PATCH 30/41] Added boost badge to buttons for prepaid giveaways. --- .../info/boosts/create_giveaway_box.cpp | 3 ++- .../boosts/giveaway/giveaway_type_row.cpp | 22 +++++++++++++++---- .../info/boosts/giveaway/giveaway_type_row.h | 5 ++++- .../info/boosts/info_boosts_inner_widget.cpp | 15 ++++++++++++- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index a38a34234..87d8ead06 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -312,7 +312,8 @@ void CreateGiveawayBox( lt_duration, tr::lng_premium_gift_duration_months( lt_count, - rpl::single(prepaid->months) | tr::to_count()))) + rpl::single(prepaid->months) | tr::to_count())), + QImage()) )->setAttribute(Qt::WA_TransparentForMouseEvents); } if (!prepaid) { diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp index e54ba3540..4417bfb25 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.cpp @@ -36,7 +36,8 @@ GiveawayTypeRow::GiveawayTypeRow( : (type == Type::AllMembers) ? tr::lng_giveaway_users_all() : tr::lng_giveaway_users_new(), - std::move(subtitle)) { + std::move(subtitle), + QImage()) { } GiveawayTypeRow::GiveawayTypeRow( @@ -44,7 +45,8 @@ GiveawayTypeRow::GiveawayTypeRow( Type type, int colorIndex, rpl::producer title, - rpl::producer subtitle) + rpl::producer subtitle, + QImage badge) : RippleButton(parent, st::defaultRippleAnimation) , _type(type) , _st((_type == Type::SpecificUsers || _type == Type::Random) @@ -54,7 +56,8 @@ GiveawayTypeRow::GiveawayTypeRow( : st::giveawayGiftCodeMembersPeerList.item) , _userpic( Ui::EmptyUserpic::UserpicColor(Ui::EmptyUserpic::ColorIndex(colorIndex)), - QString()) { + QString()) +, _badge(std::move(badge)) { std::move( subtitle ) | rpl::start_with_next([=] (const QString &s) { @@ -111,8 +114,19 @@ void GiveawayTypeRow::paintEvent(QPaintEvent *e) { const auto namey = _st.namePosition.y(); const auto namew = outerWidth - namex - skipRight; + const auto badgew = _badge.width() / style::DevicePixelRatio(); + p.setPen(_st.nameFg); - _name.drawLeftElided(p, namex, namey, namew, width()); + _name.drawLeftElided(p, namex, namey, namew - badgew, width()); + + if (!_badge.isNull()) { + p.drawImage( + std::min( + namex + _name.maxWidth() + st::boostsListBadgePadding.left(), + outerWidth - badgew - skipRight), + namey + st::boostsListMiniIconSkip, + _badge); + } const auto statusx = _st.statusPosition.x(); const auto statusy = _st.statusPosition.y(); diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h index 67b1e92ad..9bb96812b 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway_type_row.h @@ -39,7 +39,8 @@ public: Type type, int colorIndex, rpl::producer title, - rpl::producer subtitle); + rpl::producer subtitle, + QImage badge); void addRadio(std::shared_ptr> typeGroup); @@ -56,6 +57,8 @@ private: Ui::Text::String _status; Ui::Text::String _name; + QImage _badge; + }; } // namespace Giveaway diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index e4abe223f..dc787dd60 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "info/boosts/create_giveaway_box.h" +#include "info/boosts/giveaway/boost_badge.h" #include "info/boosts/giveaway/giveaway_type_row.h" #include "info/boosts/info_boosts_widget.h" #include "info/info_controller.h" @@ -326,6 +327,8 @@ void InnerWidget::fill() { ::Settings::AddSkip(inner); if (!status.prepaidGiveaway.empty()) { + const auto multiplier = Api::PremiumGiftCodeOptions(_peer) + .giveawayBoostsPerPremium(); ::Settings::AddSkip(inner); AddHeader(inner, tr::lng_boosts_prepaid_giveaway_title); ::Settings::AddSkip(inner); @@ -340,7 +343,17 @@ void InnerWidget::fill() { rpl::single(g.quantity) | tr::to_count()), tr::lng_boosts_prepaid_giveaway_moths( lt_count, - rpl::single(g.months) | tr::to_count()))); + rpl::single(g.months) | tr::to_count()), + Info::Statistics::CreateBadge( + st::statisticsDetailsBottomCaptionStyle, + QString::number(g.quantity * multiplier), + st::boostsListBadgeHeight, + st::boostsListBadgeTextPadding, + st::premiumButtonBg2, + st::premiumButtonFg, + 1., + st::boostsListMiniIconPadding, + st::boostsListMiniIcon))); button->setClickedCallback([=] { _controller->uiShow()->showBox(Box( CreateGiveawayBox, From 2d67557a91f08883e5d66b7fa20d3119e916a7b9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Nov 2023 21:48:30 +0400 Subject: [PATCH 31/41] Drag correct element from file album. --- Telegram/SourceFiles/history/history_inner_widget.cpp | 9 ++++++--- .../history/view/history_view_list_widget.cpp | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 101d8a0b4..7c3fbbcaa 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1856,9 +1856,12 @@ std::unique_ptr HistoryInner::prepareDrag() { session().data().setMimeForwardIds(std::move(forwardIds)); auto result = std::make_unique(); result->setData(u"application/x-td-forward"_q, "1"); - if (const auto media = pressedView->media()) { - if (const auto document = media->getDocument()) { - const auto filepath = document->filepath(true); + if (pressedHandler) { + const auto lnkDocument = reinterpret_cast( + pressedHandler->property( + kDocumentLinkMediaProperty).toULongLong()); + if (lnkDocument) { + const auto filepath = lnkDocument->filepath(true); if (!filepath.isEmpty()) { QList urls; urls.push_back(QUrl::fromLocalFile(filepath)); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 410b1a337..2db6bddb8 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -3639,9 +3639,12 @@ std::unique_ptr ListWidget::prepareDrag() { session().data().setMimeForwardIds(std::move(forwardIds)); auto result = std::make_unique(); result->setData(u"application/x-td-forward"_q, "1"); - if (const auto media = pressedView->media()) { - if (const auto document = media->getDocument()) { - const auto filepath = document->filepath(true); + if (pressedHandler) { + const auto lnkDocument = reinterpret_cast( + pressedHandler->property( + kDocumentLinkMediaProperty).toULongLong()); + if (lnkDocument) { + const auto filepath = lnkDocument->filepath(true); if (!filepath.isEmpty()) { QList urls; urls.push_back(QUrl::fromLocalFile(filepath)); From a41bbd27c8543b5a8237ddd1df3cb5b2526660bb Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Nov 2023 17:58:20 +0400 Subject: [PATCH 32/41] Start multiboosts, support dynamic state. --- Telegram/Resources/langs/lang.strings | 8 + Telegram/SourceFiles/api/api_statistics.cpp | 4 +- .../boxes/peers/edit_peer_color_box.cpp | 10 +- Telegram/SourceFiles/data/data_boosts.h | 2 +- .../info/boosts/info_boosts_inner_widget.cpp | 7 +- Telegram/SourceFiles/ui/boxes/boost_box.cpp | 288 +++++++++++------- Telegram/SourceFiles/ui/boxes/boost_box.h | 11 +- .../ui/effects/premium_graphics.cpp | 79 ++--- .../SourceFiles/ui/effects/premium_graphics.h | 18 +- .../window/window_session_controller.cpp | 52 ++-- .../window/window_session_controller.h | 12 +- 11 files changed, 300 insertions(+), 191 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 44576f62f..a02afaf8f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2030,6 +2030,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_premium_gift_terms_link" = "here"; "lng_boost_channel_button" = "Boost Channel"; +"lng_boost_again_button" = "Boost Again"; "lng_boost_level#one" = "Level {count}"; "lng_boost_level#other" = "Level {count}"; "lng_boost_channel_title_first" = "Enable stories for channel"; @@ -2060,6 +2061,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boost_error_flood_text" = "You can change the channel you boost only once a day. Next time you can boost is in {left}."; "lng_boost_now_instead" = "You currently boost {channel}. Do you want to boost {other} instead?"; "lng_boost_now_replace" = "Replace"; +"lng_boost_reassign_title" = "Reassign boost"; +"lng_boost_reassign_text" = "To boost {channel}, reassign a previous boost from another channel."; +"lng_boost_remove_title" = "Remove your boost from"; +"lng_boost_reassign_button" = "Reassign"; +"lng_boost_available_in" = "available in {duration}"; +"lng_boost_available_in_toast#one" = "Wait until the boost is available or get **{count}** more boost by gifting a **Telegram Premium** subscription."; +"lng_boost_available_in_toast#other" = "Wait until the boost is available or get **{count}** more boosts by gifting a **Telegram Premium** subscription."; "lng_boost_channel_title_color" = "Enable colors"; "lng_boost_channel_needs_level_color#one" = "Your channel needs to reach **Level {count}** to change channel color."; diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 9f2dd2cd3..466094d44 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -519,7 +519,9 @@ rpl::producer Boosts::request() { : 0; _boostStatus.overview = Data::BoostsOverview{ - .isBoosted = data.is_my_boost(), + .mine = (data.vmy_boost_slots() + ? data.vmy_boost_slots()->v.size() + : 0), .level = std::max(data.vlevel().v, 0), .boostCount = std::max( data.vboosts().v, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp index 1eede85e3..7c12c4661 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp @@ -514,21 +514,17 @@ void Apply( close(); return; } - const auto next = data.vnext_level_boosts().value_or_empty(); const auto openStatistics = [=] { if (const auto controller = show->resolveWindow( ChatHelpers::WindowUsage::PremiumPromo)) { controller->showSection(Info::Boosts::Make(peer)); } }; + auto counters = Window::ParseBoostCounters(result); + counters.mine = 0; // Don't show current level as just-reached. show->show(Box(Ui::AskBoostBox, Ui::AskBoostBoxData{ .link = qs(data.vboost_url()), - .boost = { - .level = data.vlevel().v, - .boosts = data.vboosts().v, - .thisLevelBoosts = data.vcurrent_level_boosts().v, - .nextLevelBoosts = next, - }, + .boost = counters, .requiredLevel = required, }, openStatistics, nullptr)); cancel(); diff --git a/Telegram/SourceFiles/data/data_boosts.h b/Telegram/SourceFiles/data/data_boosts.h index 08fac640b..bada02a91 100644 --- a/Telegram/SourceFiles/data/data_boosts.h +++ b/Telegram/SourceFiles/data/data_boosts.h @@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { struct BoostsOverview final { - bool isBoosted = false; + int mine = 0; int level = 0; int boostCount = 0; int currentLevelBoostCount = 0; diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index dc787dd60..84b7cad52 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -302,17 +302,16 @@ void InnerWidget::fill() { auto dividerContent = object_ptr(inner); Ui::FillBoostLimit( fakeShowed->events(), - rpl::single(status.overview.isBoosted), dividerContent.data(), - Ui::BoostCounters{ + rpl::single(Ui::BoostCounters{ .level = status.overview.level, .boosts = status.overview.boostCount, .thisLevelBoosts = status.overview.currentLevelBoostCount, .nextLevelBoosts = status.overview.nextLevelBoostCount, - .mine = status.overview.isBoosted, - }, + .mine = status.overview.mine, + }), st::statisticsLimitsLinePadding); inner->add(object_ptr( inner, diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.cpp b/Telegram/SourceFiles/ui/boxes/boost_box.cpp index e9ffe759d..197bcc0ad 100644 --- a/Telegram/SourceFiles/ui/boxes/boost_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/boost_box.cpp @@ -20,6 +20,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include namespace Ui { +namespace { + +[[nodiscrd]] BoostCounters AdjustByReached(BoostCounters data) { + const auto exact = (data.boosts == data.thisLevelBoosts); + const auto reached = !data.nextLevelBoosts || (exact && data.mine > 0); + if (reached) { + if (data.nextLevelBoosts) { + --data.level; + } + data.boosts = data.nextLevelBoosts = std::max({ + data.boosts, + data.thisLevelBoosts, + 1 + }); + data.thisLevelBoosts = 0; + } else { + data.boosts = std::max(data.thisLevelBoosts, data.boosts); + data.nextLevelBoosts = std::max( + data.nextLevelBoosts, + data.boosts + 1); + } + return data; +} + +} // namespace void StartFireworks(not_null parent) { const auto result = Ui::CreateChild(parent.get()); @@ -42,62 +67,65 @@ void StartFireworks(not_null parent) { void BoostBox( not_null box, BoostBoxData data, - Fn)> boost) { + Fn)> boost) { box->setWidth(st::boxWideWidth); box->setStyle(st::boostBox); - const auto full = !data.boost.nextLevelBoosts; + //AssertIsDebug(); + //data.boost = { + // .level = 2, + // .boosts = 3, + // .thisLevelBoosts = 2, + // .nextLevelBoosts = 5, + // .mine = 2, + //}; struct State { - rpl::variable you = false; + rpl::variable data; + rpl::variable full; bool submitted = false; }; - const auto state = box->lifetime().make_state(State{ - .you = data.boost.mine, - }); + const auto state = box->lifetime().make_state(); + state->data = std::move(data.boost); FillBoostLimit( BoxShowFinishes(box), - state->you.value(), box->verticalLayout(), - data.boost, + state->data.value(), st::boxRowPadding); - { - const auto &d = data.boost; - if (!d.nextLevelBoosts - || ((d.thisLevelBoosts == d.boosts) && d.mine)) { - --data.boost.level; - } - } - box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); }); const auto name = data.name; - auto title = state->you.value() | rpl::map([=](bool your) { - return your + + auto title = state->data.value( + ) | rpl::map([=](BoostCounters counters) { + return (counters.mine > 0) ? tr::lng_boost_channel_you_title( lt_channel, - rpl::single(data.name)) - : full + rpl::single(name)) + : !counters.nextLevelBoosts ? tr::lng_boost_channel_title_max() - : !data.boost.level + : !counters.level ? tr::lng_boost_channel_title_first() : tr::lng_boost_channel_title_more(); }) | rpl::flatten_latest(); - auto text = state->you.value() | rpl::map([=](bool your) { - const auto bold = Ui::Text::Bold(data.name); - const auto now = data.boost.boosts + (your ? 1 : 0); - const auto left = (data.boost.nextLevelBoosts > now) - ? (data.boost.nextLevelBoosts - now) + + auto text = state->data.value( + ) | rpl::map([=](BoostCounters counters) { + const auto bold = Ui::Text::Bold(name); + const auto now = counters.boosts; + const auto full = !counters.nextLevelBoosts; + const auto left = (counters.nextLevelBoosts > now) + ? (counters.nextLevelBoosts - now) : 0; auto post = tr::lng_boost_channel_post_stories( lt_count, - rpl::single(float64(data.boost.level + 1)), + rpl::single(float64(counters.level + (left ? 1 : 0))), Ui::Text::RichLangValue); - return (your || full) - ? ((!full && left > 0) - ? (!data.boost.level + return (counters.mine || full) + ? (left + ? (!counters.level ? tr::lng_boost_channel_you_first( lt_count, rpl::single(float64(left)), @@ -108,16 +136,16 @@ void BoostBox( lt_post, std::move(post), Ui::Text::RichLangValue)) - : (!data.boost.level + : (!counters.level ? tr::lng_boost_channel_reached_first( Ui::Text::RichLangValue) : tr::lng_boost_channel_reached_more( lt_count, - rpl::single(float64(data.boost.level + 1)), + rpl::single(float64(counters.level)), lt_post, std::move(post), Ui::Text::RichLangValue))) - : !data.boost.level + : !counters.level ? tr::lng_boost_channel_needs_first( lt_count, rpl::single(float64(left)), @@ -133,12 +161,14 @@ void BoostBox( std::move(post), Ui::Text::RichLangValue); }) | rpl::flatten_latest(); + box->addRow( object_ptr( box, std::move(title), st::boostTitle), st::boxRowPadding + QMargins(0, st::boostTitleSkip, 0, 0)); + box->addRow( object_ptr( box, @@ -147,28 +177,83 @@ void BoostBox( (st::boxRowPadding + QMargins(0, st::boostTextSkip, 0, st::boostBottomSkip))); - auto submit = full - ? (tr::lng_box_ok() | rpl::type_erased()) - : state->you.value( - ) | rpl::map([](bool mine) { - return mine ? tr::lng_box_ok() : tr::lng_boost_channel_button(); - }) | rpl::flatten_latest(); + auto submit = state->data.value( + ) | rpl::map([=](BoostCounters counters) { + return !counters.nextLevelBoosts + ? tr::lng_box_ok() + : (counters.mine > 0) + ? tr::lng_boost_again_button() + : tr::lng_boost_channel_button(); + }) | rpl::flatten_latest(); + const auto button = box->addButton(rpl::duplicate(submit), [=] { if (state->submitted) { return; - } else if (!full && !state->you.current()) { + } else if (state->data.current().nextLevelBoosts > 0) { state->submitted = true; - boost(crl::guard(box, [=](bool success) { + const auto was = state->data.current().mine; + + //AssertIsDebug(); + //state->submitted = false; + //if (state->data.current().level == 5 + // && state->data.current().boosts == 11) { + // state->data = BoostCounters{ + // .level = 5, + // .boosts = 14, + // .thisLevelBoosts = 9, + // .nextLevelBoosts = 15, + // .mine = 14, + // }; + //} else if (state->data.current().level == 5) { + // state->data = BoostCounters{ + // .level = 7, + // .boosts = 16, + // .thisLevelBoosts = 15, + // .nextLevelBoosts = 19, + // .mine = 16, + // }; + //} else if (state->data.current().level == 4) { + // state->data = BoostCounters{ + // .level = 5, + // .boosts = 11, + // .thisLevelBoosts = 9, + // .nextLevelBoosts = 15, + // .mine = 9, + // }; + //} else if (state->data.current().level == 3) { + // state->data = BoostCounters{ + // .level = 4, + // .boosts = 7, + // .thisLevelBoosts = 7, + // .nextLevelBoosts = 9, + // .mine = 5, + // }; + //} else { + // state->data = BoostCounters{ + // .level = 3, + // .boosts = 5, + // .thisLevelBoosts = 5, + // .nextLevelBoosts = 7, + // .mine = 3, + // }; + //} + //return; + + boost(crl::guard(box, [=](BoostCounters result) { state->submitted = false; - if (success) { - StartFireworks(box->parentWidget()); - state->you = true; + + if (result.thisLevelBoosts || result.nextLevelBoosts) { + if (result.mine > was) { + StartFireworks(box->parentWidget()); + } + state->data = result; } })); } else { box->closeBox(); } }); + rpl::combine( std::move(submit), box->widthValue() @@ -270,18 +355,14 @@ void AskBoostBox( box->setStyle(st::boostBox); struct State { - rpl::variable you = false; bool submitted = false; }; - const auto state = box->lifetime().make_state(State{ - .you = data.boost.mine, - }); + const auto state = box->lifetime().make_state(); FillBoostLimit( BoxShowFinishes(box), - state->you.value(), box->verticalLayout(), - data.boost, + rpl::single(data.boost), st::boxRowPadding); box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); }); @@ -338,56 +419,22 @@ void AskBoostBox( void FillBoostLimit( rpl::producer<> showFinished, - rpl::producer you, not_null container, - BoostCounters data, + rpl::producer data, style::margins limitLinePadding) { - const auto full = !data.nextLevelBoosts; - - if (data.mine && data.boosts > 0) { - --data.boosts; - } - - if (full) { - data.nextLevelBoosts = data.boosts - + (data.mine ? 1 : 0); - data.thisLevelBoosts = 0; - if (data.level > 0) { - --data.level; - } - } else if (data.mine - && data.level > 0 - && data.boosts < data.thisLevelBoosts) { - --data.level; - data.nextLevelBoosts = data.thisLevelBoosts; - data.thisLevelBoosts = 0; - } - const auto addSkip = [&](int skip) { container->add(object_ptr(container, skip)); }; addSkip(st::boostSkipTop); - const auto levelWidth = [&](int add) { - return st::normalFont->width( - tr::lng_boost_level(tr::now, lt_count, data.level + add)); - }; - const auto paddings = 2 * st::premiumLineTextSkip; - const auto labelLeftWidth = paddings + levelWidth(0); - const auto labelRightWidth = paddings + levelWidth(1); - const auto ratio = [=](int boosts) { - const auto min = std::min( - data.boosts, - data.thisLevelBoosts); - const auto max = std::max({ - data.boosts, - data.nextLevelBoosts, - 1, - }); - Assert(boosts >= min && boosts <= max); + const auto ratio = [=](BoostCounters counters) { + const auto min = counters.thisLevelBoosts; + const auto max = counters.nextLevelBoosts; + + Assert(counters.boosts >= min && counters.boosts <= max); const auto count = (max - min); - const auto index = (boosts - min); + const auto index = (counters.boosts - min); if (!index) { return 0.; } else if (index == count) { @@ -399,26 +446,33 @@ void FillBoostLimit( - st::boxPadding.left() - st::boxPadding.right(); const auto average = available / float64(count); + const auto levelWidth = [&](int add) { + return st::normalFont->width( + tr::lng_boost_level( + tr::now, + lt_count, + counters.level + add)); + }; + const auto paddings = 2 * st::premiumLineTextSkip; + const auto labelLeftWidth = paddings + levelWidth(0); + const auto labelRightWidth = paddings + levelWidth(1); const auto first = std::max(average, labelLeftWidth * 1.); const auto last = std::max(average, labelRightWidth * 1.); const auto other = (available - first - last) / (count - 2); return (first + (index - 1) * other) / available; }; - const auto min = std::min(data.boosts, data.thisLevelBoosts); - const auto now = data.boosts; - const auto max = (data.nextLevelBoosts > min) - ? (data.nextLevelBoosts) - : (data.boosts > 0) - ? data.boosts - : 1; - auto bubbleRowState = ( - std::move(you) - ) | rpl::map([=](bool mine) { - const auto index = mine ? (now + 1) : now; + auto adjustedData = rpl::duplicate(data) | rpl::map(AdjustByReached); + + auto bubbleRowState = rpl::duplicate( + adjustedData + ) | rpl::combine_previous( + BoostCounters() + ) | rpl::map([=](BoostCounters previous, BoostCounters counters) { return Premium::BubbleRowState{ - .counter = index, - .ratio = ratio(index), + .counter = counters.boosts, + .ratio = ratio(counters), + .animateFromZero = (counters.level != previous.level), .dynamic = true, }; }); @@ -427,7 +481,6 @@ void FillBoostLimit( st::boostBubble, std::move(showFinished), rpl::duplicate(bubbleRowState), - max, true, nullptr, &st::premiumIconBoost, @@ -437,20 +490,33 @@ void FillBoostLimit( const auto level = [](int level) { return tr::lng_boost_level(tr::now, lt_count, level); }; - auto ratioValue = std::move( + auto limitState = std::move( bubbleRowState ) | rpl::map([](const Premium::BubbleRowState &state) { - return state.ratio; + return Premium::LimitRowState{ + .ratio = state.ratio, + .animateFromZero = state.animateFromZero, + .dynamic = state.dynamic + }; + }); + auto left = rpl::duplicate( + adjustedData + ) | rpl::map([=](BoostCounters counters) { + return level(counters.level); + }); + auto right = rpl::duplicate( + adjustedData + ) | rpl::map([=](BoostCounters counters) { + return level(counters.level + 1); }); Premium::AddLimitRow( container, st::boostLimits, Premium::LimitRowLabels{ - .leftLabel = level(data.level), - .rightLabel = level(data.level + 1), - .dynamic = true, + .leftLabel = std::move(left), + .rightLabel = std::move(right), }, - std::move(ratioValue), + std::move(limitState), limitLinePadding); } diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.h b/Telegram/SourceFiles/ui/boxes/boost_box.h index da8d57572..e8ef31993 100644 --- a/Telegram/SourceFiles/ui/boxes/boost_box.h +++ b/Telegram/SourceFiles/ui/boxes/boost_box.h @@ -23,7 +23,11 @@ struct BoostCounters { int boosts = 0; int thisLevelBoosts = 0; int nextLevelBoosts = 0; // Zero means no next level is available. - bool mine = false; + int mine = 0; + + friend inline constexpr bool operator==( + BoostCounters, + BoostCounters) = default; }; struct BoostBoxData { @@ -34,7 +38,7 @@ struct BoostBoxData { void BoostBox( not_null box, BoostBoxData data, - Fn)> boost); + Fn)> boost); struct AskBoostBoxData { QString link; @@ -57,9 +61,8 @@ void AskBoostBox( void FillBoostLimit( rpl::producer<> showFinished, - rpl::producer you, not_null container, - BoostCounters data, + rpl::producer data, style::margins limitLinePadding); } // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp index c7c8cadbe..6c134a8b8 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp @@ -195,7 +195,7 @@ public: [[nodiscard]] int height() const; [[nodiscard]] int width() const; [[nodiscard]] int bubbleRadius() const; - [[nodiscard]] int countMaxWidth(int maxCounter) const; + [[nodiscard]] int countMaxWidth(int maxPossibleCounter) const; void setCounter(int value); void setTailEdge(EdgeProgress edge); @@ -271,12 +271,12 @@ int Bubble::width() const { return filledWidth() + _numberAnimation.countWidth(); } -int Bubble::countMaxWidth(int maxCounter) const { +int Bubble::countMaxWidth(int maxPossibleCounter) const { auto numbers = Ui::NumbersAnimation(_st.font, [] {}); numbers.setDisabledMonospace(true); numbers.setDuration(0); numbers.setText(_textFactory(0), 0); - numbers.setText(_textFactory(maxCounter), maxCounter); + numbers.setText(_textFactory(maxPossibleCounter), maxPossibleCounter); numbers.finishAnimating(); return filledWidth() + numbers.maxWidth(); } @@ -389,7 +389,6 @@ public: const style::PremiumBubble &st, TextFactory textFactory, rpl::producer state, - int maxCounter, bool premiumPossible, rpl::producer<> showFinishes, const style::icon *icon, @@ -414,9 +413,8 @@ private: BubbleRowState _animatingFrom; float64 _animatingFromResultRatio = 0.; rpl::variable _state; - const int _maxCounter; Bubble _bubble; - const int _maxBubbleWidth; + int _maxBubbleWidth = 0; const bool _premiumPossible; const style::margins _outerPadding; @@ -439,7 +437,6 @@ BubbleWidget::BubbleWidget( const style::PremiumBubble &st, TextFactory textFactory, rpl::producer state, - int maxCounter, bool premiumPossible, rpl::producer<> showFinishes, const style::icon *icon, @@ -447,14 +444,12 @@ BubbleWidget::BubbleWidget( : RpWidget(parent) , _st(st) , _state(std::move(state)) -, _maxCounter(maxCounter) , _bubble( _st, [=] { update(); }, std::move(textFactory), icon, premiumPossible) -, _maxBubbleWidth(_bubble.countMaxWidth(_maxCounter)) , _premiumPossible(premiumPossible) , _outerPadding(outerPadding) , _deflection(kDeflection) @@ -485,6 +480,7 @@ BubbleWidget::BubbleWidget( } void BubbleWidget::animateTo(BubbleRowState state) { + _maxBubbleWidth = _bubble.countMaxWidth(state.counter); const auto parent = parentWidget(); const auto computeLeft = [=](float64 pointRatio, float64 animProgress) { const auto halfWidth = (_maxBubbleWidth / 2); @@ -541,6 +537,11 @@ void BubbleWidget::animateTo(BubbleRowState state) { const auto duration = kSlideDuration * (_ignoreDeflection ? kStepBeforeDeflection : 1.) * ((_state.current().ratio < 0.001) ? 0.5 : 1.); + if (state.animateFromZero) { + _animatingFrom.ratio = 0.; + _animatingFrom.counter = 0; + _animatingFromResultRatio = 0.; + } _appearanceAnimation.start([=](float64 value) { if (!_appearanceAnimation.animating()) { _animatingFrom = state; @@ -658,7 +659,7 @@ public: not_null parent, const style::PremiumLimits &st, LimitRowLabels labels, - rpl::producer ratio); + rpl::producer state); void setColorOverride(QBrush brush); @@ -675,6 +676,7 @@ private: float64 _ratio = 0.; Ui::Animations::Simple _animation; + rpl::event_stream<> _recaches; Ui::Text::String _leftLabel; Ui::Text::String _leftText; Ui::Text::String _rightLabel; @@ -707,44 +709,56 @@ Line::Line( QString min, float64 ratio) : Line(parent, st, LimitRowLabels{ - .leftLabel = tr::lng_premium_free(tr::now), - .leftCount = min, - .rightLabel = tr::lng_premium(tr::now), - .rightCount = max, -}, rpl::single(ratio)) { + .leftLabel = tr::lng_premium_free(), + .leftCount = rpl::single(min), + .rightLabel = tr::lng_premium(), + .rightCount = rpl::single(max), +}, rpl::single(LimitRowState{ ratio })) { } Line::Line( not_null parent, const style::PremiumLimits &st, LimitRowLabels labels, - rpl::producer ratio) + rpl::producer state) : Ui::RpWidget(parent) -, _st(st) -, _leftLabel(st::semiboldTextStyle, labels.leftLabel) -, _leftText(st::semiboldTextStyle, labels.leftCount) -, _rightLabel(st::semiboldTextStyle, labels.rightLabel) -, _rightText(st::semiboldTextStyle, labels.rightCount) -, _dynamic(labels.dynamic) { +, _st(st) { resize(width(), st::requestsAcceptButton.height); - std::move(ratio) | rpl::start_with_next([=](float64 ratio) { + const auto set = [&]( + Ui::Text::String &label, + rpl::producer &text) { + std::move(text) | rpl::start_with_next([=, &label](QString text) { + label = { st::semiboldTextStyle, text }; + _recaches.fire({}); + }, lifetime()); + }; + set(_leftLabel, labels.leftLabel); + set(_leftText, labels.leftCount); + set(_rightLabel, labels.rightLabel); + set(_rightText, labels.rightCount); + + std::move(state) | rpl::start_with_next([=](LimitRowState state) { + _dynamic = state.dynamic; if (width() > 0) { - const auto from = _animation.value(_ratio); + const auto from = state.animateFromZero + ? 0. + : _animation.value(_ratio); const auto duration = kSlideDuration * kStepBeforeDeflection; _animation.start([=] { update(); - }, from, ratio, duration, anim::easeOutCirc); + }, from, state.ratio, duration, anim::easeOutCirc); } - _ratio = ratio; + _ratio = state.ratio; }, lifetime()); rpl::combine( sizeValue(), - parent->widthValue() - ) | rpl::filter([](const QSize &size, int parentWidth) { + parent->widthValue(), + _recaches.events_starting_with({}) + ) | rpl::filter([](const QSize &size, int parentWidth, auto) { return !size.isEmpty() && parentWidth; - }) | rpl::start_with_next([=](const QSize &size, int) { + }) | rpl::start_with_next([=](const QSize &size, auto, auto) { recache(size); update(); }, lifetime()); @@ -906,7 +920,6 @@ void AddBubbleRow( .counter = current, .ratio = (current - min) / float64(max - min), }), - max, premiumPossible, ProcessTextFactory(phrase), icon, @@ -918,7 +931,6 @@ void AddBubbleRow( const style::PremiumBubble &st, rpl::producer<> showFinishes, rpl::producer state, - int max, bool premiumPossible, Fn text, const style::icon *icon, @@ -930,7 +942,6 @@ void AddBubbleRow( st, text ? std::move(text) : ProcessTextFactory(std::nullopt), std::move(state), - max, premiumPossible, std::move(showFinishes), icon, @@ -975,10 +986,10 @@ void AddLimitRow( not_null parent, const style::PremiumLimits &st, LimitRowLabels labels, - rpl::producer ratio, + rpl::producer state, const style::margins &padding) { parent->add( - object_ptr(parent, st, std::move(labels), std::move(ratio)), + object_ptr(parent, st, std::move(labels), std::move(state)), padding); } diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.h b/Telegram/SourceFiles/ui/effects/premium_graphics.h index 1292a6980..728dd9d40 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.h +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.h @@ -55,6 +55,7 @@ void AddBubbleRow( struct BubbleRowState { int counter = 0; float64 ratio = 0.; + bool animateFromZero = false; bool dynamic = false; }; void AddBubbleRow( @@ -62,7 +63,6 @@ void AddBubbleRow( const style::PremiumBubble &st, rpl::producer<> showFinishes, rpl::producer state, - int max, bool premiumPossible, Fn text, const style::icon *icon, @@ -84,17 +84,23 @@ void AddLimitRow( float64 ratio = kLimitRowRatio); struct LimitRowLabels { - QString leftLabel; - QString leftCount; - QString rightLabel; - QString rightCount; + rpl::producer leftLabel; + rpl::producer leftCount; + rpl::producer rightLabel; + rpl::producer rightCount; +}; + +struct LimitRowState { + float64 ratio = 0.; + bool animateFromZero = false; bool dynamic = false; }; + void AddLimitRow( not_null parent, const style::PremiumLimits &st, LimitRowLabels labels, - rpl::producer ratio, + rpl::producer state, const style::margins &padding); struct AccountsRowArgs final { diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index b6822f2d4..2cb64bcca 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -267,6 +267,19 @@ Fn PausedIn( return [=] { return IsPaused(controller, level); }; } +Ui::BoostCounters ParseBoostCounters( + const MTPpremium_BoostsStatus &status) { + const auto &data = status.data(); + const auto slots = data.vmy_boost_slots(); + return { + .level = data.vlevel().v, + .boosts = data.vboosts().v, + .thisLevelBoosts = data.vcurrent_level_boosts().v, + .nextLevelBoosts = data.vnext_level_boosts().value_or_empty(), + .mine = slots ? slots->v.size() : 0, + }; +} + bool operator==(const PeerThemeOverride &a, const PeerThemeOverride &b) { return (a.peer == b.peer) && (a.theme == b.theme); } @@ -629,20 +642,12 @@ void SessionNavigation::resolveBoostState(not_null channel) { channel->input )).done([=](const MTPpremium_BoostsStatus &result) { _boostStateResolving = nullptr; - const auto &data = result.data(); - const auto submit = [=](Fn done) { + const auto submit = [=](Fn done) { applyBoost(channel, done); }; - const auto next = data.vnext_level_boosts().value_or_empty(); uiShow()->show(Box(Ui::BoostBox, Ui::BoostBoxData{ .name = channel->name(), - .boost = { - .level = data.vlevel().v, - .boosts = data.vboosts().v, - .thisLevelBoosts = data.vcurrent_level_boosts().v, - .nextLevelBoosts = next, - .mine = data.is_my_boost(), - }, + .boost = ParseBoostCounters(result), }, submit)); }).fail([=](const MTP::Error &error) { _boostStateResolving = nullptr; @@ -652,7 +657,7 @@ void SessionNavigation::resolveBoostState(not_null channel) { void SessionNavigation::applyBoost( not_null channel, - Fn done) { + Fn done) { _api.request(MTPpremium_GetMyBoosts( )).done([=](const MTPpremium_MyBoosts &result) { const auto &data = result.data(); @@ -682,7 +687,7 @@ void SessionNavigation::applyBoost( .inform = true, })); } - done(false); + done({}); return; } auto slot = int(); @@ -725,7 +730,7 @@ void SessionNavigation::applyBoost( .title = tr::lng_boost_error_flood_title(), .inform = true, })); - done(false); + done({}); } else { const auto peer = _session->data().peer(different); replaceBoostConfirm(peer, channel, slot, done); @@ -737,12 +742,12 @@ void SessionNavigation::applyBoost( .title = tr::lng_boost_error_already_title(), .inform = true, })); - done(false); + done({}); } }).fail([=](const MTP::Error &error) { const auto type = error.type(); showToast(u"Error: "_q + type); - done(false); + done({}); }).handleFloodErrors().send(); } @@ -750,7 +755,7 @@ void SessionNavigation::replaceBoostConfirm( not_null from, not_null channel, int slot, - Fn done) { + Fn done) { const auto forwarded = std::make_shared(false); const auto confirmed = [=](Fn close) { *forwarded = true; @@ -777,23 +782,30 @@ void SessionNavigation::replaceBoostConfirm( box->boxClosing() | rpl::filter([=] { return !*forwarded; }) | rpl::start_with_next([=] { - done(false); + done({}); }, box->lifetime()); } void SessionNavigation::applyBoostChecked( not_null channel, int slot, - Fn done) { + Fn done) { _api.request(MTPpremium_ApplyBoost( MTP_flags(MTPpremium_ApplyBoost::Flag::f_slots), MTP_vector({ MTP_int(slot) }), channel->input )).done([=](const MTPpremium_MyBoosts &result) { - done(true); + _api.request(MTPpremium_GetBoostsStatus( + channel->input + )).done([=](const MTPpremium_BoostsStatus &result) { + done(ParseBoostCounters(result)); + }).fail([=](const MTP::Error &error) { + showToast(u"Error: "_q + error.type()); + done({}); + }).send(); }).fail([=](const MTP::Error &error) { showToast(u"Error: "_q + error.type()); - done(false); + done({}); }).send(); } diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 2ae888937..aecd4150d 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -68,6 +68,7 @@ struct ChatPaintContext; struct ChatThemeBackground; struct ChatThemeBackgroundData; class MessageSendingAnimationController; +struct BoostCounters; } // namespace Ui namespace Data { @@ -314,16 +315,18 @@ private: const PeerByLinkInfo &info); void resolveBoostState(not_null channel); - void applyBoost(not_null channel, Fn done); + void applyBoost( + not_null channel, + Fn done); void replaceBoostConfirm( not_null from, not_null channel, int slot, - Fn done); + Fn done); void applyBoostChecked( not_null channel, int slot, - Fn done); + Fn done); const not_null _session; @@ -752,4 +755,7 @@ void ActivateWindow(not_null controller); not_null controller, GifPauseReason level); +[[nodiscard]] Ui::BoostCounters ParseBoostCounters( + const MTPpremium_BoostsStatus &status); + } // namespace Window From c5d1739e9557829bac86b496bc8738f93a1a896c Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Nov 2023 13:31:01 +0400 Subject: [PATCH 33/41] Implement multiboost reassign box. --- Telegram/CMakeLists.txt | 2 + Telegram/Resources/langs/lang.strings | 13 +- Telegram/SourceFiles/boxes/passcode_box.cpp | 10 +- Telegram/SourceFiles/boxes/peer_list_box.cpp | 10 + Telegram/SourceFiles/boxes/peer_list_box.h | 3 + .../boxes/peers/edit_peer_color_box.cpp | 3 +- .../boxes/peers/edit_peer_invite_link.cpp | 2 +- .../boxes/peers/replace_boost_box.cpp | 613 ++++++++++++++++++ .../boxes/peers/replace_boost_box.h | 53 ++ Telegram/SourceFiles/ui/boxes/boost_box.cpp | 50 +- Telegram/SourceFiles/ui/boxes/boost_box.h | 10 + Telegram/SourceFiles/ui/boxes/confirm_box.cpp | 7 - Telegram/SourceFiles/ui/boxes/confirm_box.h | 22 +- .../ui/controls/userpic_button.cpp | 53 -- .../SourceFiles/ui/controls/userpic_button.h | 5 - Telegram/SourceFiles/ui/effects/premium.style | 8 +- .../window/window_session_controller.cpp | 181 ++---- .../window/window_session_controller.h | 12 +- 18 files changed, 844 insertions(+), 213 deletions(-) create mode 100644 Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp create mode 100644 Telegram/SourceFiles/boxes/peers/replace_boost_box.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 03382f711..109fecbb3 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -221,6 +221,8 @@ PRIVATE boxes/peers/peer_short_info_box.h boxes/peers/prepare_short_info_box.cpp boxes/peers/prepare_short_info_box.h + boxes/peers/replace_boost_box.cpp + boxes/peers/replace_boost_box.h boxes/about_box.cpp boxes/about_box.h boxes/about_sponsored_box.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a02afaf8f..4edc3388b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2052,6 +2052,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boost_channel_post_stories#other" = "post **{count} stories** per day"; "lng_boost_error_gifted_title" = "Can't boost with gifted Premium!"; "lng_boost_error_gifted_text" = "Because your **Telegram Premium** subscription was gifted to you, you can't use it to boost channels."; +"lng_boost_need_more" = "More boosts needed"; +"lng_boost_need_more_text#one" = "To boost {channel}, gift **Telegram Premium** to a friend and get **{count}** boosts."; +"lng_boost_need_more_text#other" = "To boost {channel}, gift **Telegram Premium** to a friend and get **{count}** boosts."; +"lng_boost_need_more_again#one" = "To boost {channel} again, gift **Telegram Premium** to a friend and get **{count}** additional boost."; +"lng_boost_need_more_again#other" = "To boost {channel} again, gift **Telegram Premium** to a friend and get **{count}** additional boosts."; "lng_boost_error_already_title" = "Already Boosted!"; "lng_boost_error_already_text" = "You are already boosting this channel."; "lng_boost_error_premium_title" = "Premium needed!"; @@ -2062,12 +2067,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boost_now_instead" = "You currently boost {channel}. Do you want to boost {other} instead?"; "lng_boost_now_replace" = "Replace"; "lng_boost_reassign_title" = "Reassign boost"; -"lng_boost_reassign_text" = "To boost {channel}, reassign a previous boost from another channel."; +"lng_boost_reassign_text" = "To boost {channel}, reassign a previous boost or {gift}."; +"lng_boost_reassign_gift#one" = "gift **Telegram Premium** to a friend to get **{count}** additional boost"; +"lng_boost_reassign_gift#other" = "gift **Telegram Premium** to a friend to get **{count}** additional boosts"; "lng_boost_remove_title" = "Remove your boost from"; "lng_boost_reassign_button" = "Reassign"; "lng_boost_available_in" = "available in {duration}"; "lng_boost_available_in_toast#one" = "Wait until the boost is available or get **{count}** more boost by gifting a **Telegram Premium** subscription."; "lng_boost_available_in_toast#other" = "Wait until the boost is available or get **{count}** more boosts by gifting a **Telegram Premium** subscription."; +"lng_boost_reassign_done#one" = "{count} boost is reassigned from {channels}."; +"lng_boost_reassign_done#other" = "{count} boosts are reassigned from {channels}."; +"lng_boost_reassign_channels#one" = "{count} channel"; +"lng_boost_reassign_channels#other" = "{count} channels"; "lng_boost_channel_title_color" = "Enable colors"; "lng_boost_channel_needs_level_color#one" = "Your channel needs to reach **Level {count}** to change channel color."; diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index dd165ac61..e320b434f 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -567,12 +567,10 @@ void PasscodeBox::validateEmail( } else if (error.type() == u"EMAIL_HASH_EXPIRED"_q) { const auto weak = Ui::MakeWeak(this); _clearUnconfirmedPassword.fire({}); - if (weak) { - auto box = Ui::MakeInformBox({ - Lang::Hard::EmailConfirmationExpired() - }); - weak->getDelegate()->show( - std::move(box), + if (const auto strong = weak.data()) { + strong->getDelegate()->show( + Ui::MakeInformBox( + Lang::Hard::EmailConfirmationExpired()), Ui::LayerOption::CloseOther); } } else { diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 189a13215..9f12a539e 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -1682,9 +1682,19 @@ crl::time PeerListContent::paintRow( return refreshStatusIn; } + const auto opacity = row->opacity(); const auto &bg = selected ? _st.item.button.textBgOver : _st.item.button.textBg; + if (opacity < 1.) { + p.setOpacity(opacity); + } + const auto guard = gsl::finally([&] { + if (opacity < 1.) { + p.setOpacity(1.); + } + }); + p.fillRect(0, 0, outerWidth, _rowHeight, bg); row->paintRipple(p, 0, 0, outerWidth); row->paintUserpic( diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 1a53254e1..e99469b9e 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -141,6 +141,9 @@ public: } virtual void rightActionStopLastRipple() { } + [[nodiscard]] virtual float64 opacity() { + return 1.; + } // By default elements code falls back to a simple right action code. virtual int elementsCount() const; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp index 7c12c4661..1913675af 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "base/unixtime.h" +#include "boxes/peers/replace_boost_box.h" #include "chat_helpers/compose/compose_show.h" #include "data/data_changes.h" #include "data/data_channel.h" @@ -520,7 +521,7 @@ void Apply( controller->showSection(Info::Boosts::Make(peer)); } }; - auto counters = Window::ParseBoostCounters(result); + auto counters = ParseBoostCounters(result); counters.mine = 0; // Don't show current level as just-reached. show->show(Box(Ui::AskBoostBox, Ui::AskBoostBoxData{ .link = qs(data.vboost_url()), diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index f43112511..1a32c9978 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -1335,7 +1335,7 @@ object_ptr ShowInviteLinkBox( auto data = rpl::single(link) | rpl::then(std::move(updates)); auto initBox = [=, data = rpl::duplicate(data)]( - not_null box) { + not_null box) { rpl::duplicate( data ) | rpl::start_with_next([=](const LinkData &link) { diff --git a/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp b/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp new file mode 100644 index 000000000..c629fe948 --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp @@ -0,0 +1,613 @@ +/* +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 "boxes/peers/replace_boost_box.h" + +#include "base/event_filter.h" +#include "base/unixtime.h" +#include "boxes/peer_list_box.h" +#include "data/data_channel.h" +#include "data/data_session.h" +#include "lang/lang_keys.h" +#include "main/main_account.h" +#include "main/main_app_config.h" +#include "main/main_session.h" +#include "main/session/session_show.h" +#include "ui/boxes/boost_box.h" +#include "ui/boxes/confirm_box.h" +#include "ui/chat/chat_style.h" +#include "ui/controls/userpic_button.h" +#include "ui/effects/premium_graphics.h" +#include "ui/layers/generic_box.h" +#include "ui/text/text_utilities.h" +#include "ui/toast/toast.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/padding_wrap.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/empty_userpic.h" +#include "ui/painter.h" +#include "styles/style_boxes.h" +#include "styles/style_premium.h" + +namespace { + +constexpr auto kWaitingOpacity = 0.5; + +class Row final : public PeerListRow { +public: + Row( + not_null session, + TakenBoostSlot slot, + TimeId unixtimeNow, + crl::time preciseNow); + + void updateStatus(TimeId unixtimeNow, crl::time preciseNow); + [[nodiscard]] TakenBoostSlot data() const { + return _data; + } + [[nodiscard]] bool waiting() const { + return _waiting; + } + + QString generateName() override; + QString generateShortName() override; + PaintRoundImageCallback generatePaintUserpicCallback( + bool forceRound) override; + float64 opacity() override; + +private: + [[nodiscard]]PaintRoundImageCallback peerPaintUserpicCallback(); + + TakenBoostSlot _data; + PeerData *_peer = nullptr; + std::shared_ptr _empty; + Ui::PeerUserpicView _userpic; + crl::time _startPreciseTime = 0; + TimeId _startUnixtime = 0; + bool _waiting = false; + +}; + +class Controller final : public PeerListController { +public: + Controller(not_null to, std::vector from); + + [[nodiscard]] rpl::producer> selectedValue() const { + return _selected.value(); + } + + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null row) override; + bool trackSelectedList() override { + return false; + } + +private: + void updateWaitingState(); + + not_null _to; + std::vector _from; + rpl::variable> _selected; + rpl::variable>> _selectedPeers; + base::Timer _waitingTimer; + bool _hasWaitingRows = false; + +}; + +Row::Row( + not_null session, + TakenBoostSlot slot, + TimeId unixtimeNow, + crl::time preciseNow) +: PeerListRow(PeerListRowId(slot.id)) +, _data(slot) +, _peer(session->data().peerLoaded(_data.peerId)) +, _startPreciseTime(preciseNow) +, _startUnixtime(unixtimeNow) { + updateStatus(unixtimeNow, preciseNow); +} + +void Row::updateStatus(TimeId unixtimeNow, crl::time preciseNow) { + _waiting = (_data.cooldown > unixtimeNow); + if (_waiting) { + const auto initial = crl::time(_data.cooldown - _startUnixtime); + const auto elapsed = (preciseNow + 500 - _startPreciseTime) / 1000; + const auto seconds = initial + - std::clamp(elapsed, crl::time(), initial); + const auto hours = seconds / 3600; + const auto minutes = seconds / 60; + const auto duration = (hours > 0) + ? u"%1:%2:%3"_q.arg( + hours + ).arg(minutes % 60, 2, 10, QChar('0') + ).arg(seconds % 60, 2, 10, QChar('0')) + : u"%1:%2"_q.arg( + minutes + ).arg(seconds % 60, 2, 10, QChar('0')); + setCustomStatus( + tr::lng_boost_available_in(tr::now, lt_duration, duration)); + } else { + const auto date = base::unixtime::parse(_data.expires); + setCustomStatus(tr::lng_boosts_list_status( + tr::now, + lt_date, + langDayOfMonth(date.date()))); + } +} + +QString Row::generateName() { + return _peer ? _peer->name() : u" "_q; +} + +QString Row::generateShortName() { + return _peer ? _peer->shortName() : generateName(); +} + +PaintRoundImageCallback Row::generatePaintUserpicCallback( + bool forceRound) { + if (_peer) { + return (forceRound && _peer->isForum()) + ? ForceRoundUserpicCallback(_peer) + : peerPaintUserpicCallback(); + } else if (!_empty) { + const auto colorIndex = _data.id % Ui::kColorIndexCount; + _empty = std::make_shared( + Ui::EmptyUserpic::UserpicColor(colorIndex), + u" "_q); + } + const auto empty = _empty; + return [=](Painter &p, int x, int y, int outerWidth, int size) { + empty->paintCircle(p, x, y, outerWidth, size); + }; +} + +float64 Row::opacity() { + return _waiting ? kWaitingOpacity : 1.; +} + +PaintRoundImageCallback Row::peerPaintUserpicCallback() { + const auto peer = _peer; + if (!_userpic.cloud && peer->hasUserpic()) { + _userpic = peer->createUserpicView(); + } + auto userpic = _userpic; + return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { + peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size); + }; +} + +Controller::Controller( + not_null to, + std::vector from) +: _to(to) +, _from(std::move(from)) +, _waitingTimer([=] { updateWaitingState(); }) { +} + +Main::Session &Controller::session() const { + return _to->session(); +} + +void Controller::prepare() { + delegate()->peerListSetTitle(tr::lng_boost_reassign_title()); + + const auto session = &_to->session(); + auto above = object_ptr((QWidget*)nullptr); + above->add( + CreateBoostReplaceUserpics( + above.data(), + _selectedPeers.value(), + _to), + st::boxRowPadding + st::boostReplaceUserpicsPadding); + above->add( + object_ptr( + above.data(), + tr::lng_boost_reassign_text( + lt_channel, + rpl::single(Ui::Text::Bold(_to->name())), + lt_gift, + tr::lng_boost_reassign_gift( + lt_count, + rpl::single(1. * BoostsForGift(session)), + Ui::Text::RichLangValue), + Ui::Text::RichLangValue), + st::boostReassignText), + st::boxRowPadding); + delegate()->peerListSetAboveWidget(std::move(above)); + + const auto now = base::unixtime::now(); + const auto precise = crl::now(); + ranges::stable_sort(_from, ranges::less(), [&](TakenBoostSlot slot) { + return (slot.cooldown > now) ? slot.cooldown : -slot.cooldown; + }); + for (const auto &slot : _from) { + auto row = std::make_unique(session, slot, now, precise); + if (row->waiting()) { + _hasWaitingRows = true; + } + delegate()->peerListAppendRow(std::move(row)); + } + + if (_hasWaitingRows) { + _waitingTimer.callEach(1000); + } + + delegate()->peerListRefreshRows(); +} + +void Controller::updateWaitingState() { + _hasWaitingRows = false; + const auto now = base::unixtime::now(); + const auto precise = crl::now(); + const auto count = delegate()->peerListFullRowsCount(); + for (auto i = 0; i != count; ++i) { + const auto bare = delegate()->peerListRowAt(i); + const auto row = static_cast(bare.get()); + if (row->waiting()) { + row->updateStatus(now, precise); + delegate()->peerListUpdateRow(row); + if (row->waiting()) { + _hasWaitingRows = true; + } + } + } + if (!_hasWaitingRows) { + _waitingTimer.cancel(); + } +} + +void Controller::rowClicked(not_null row) { + const auto slot = static_cast(row.get())->data(); + if (slot.cooldown > base::unixtime::now()) { + delegate()->peerListUiShow()->showToast({ + .text = tr::lng_boost_available_in_toast( + tr::now, + lt_count, + BoostsForGift(&session()), + Ui::Text::RichLangValue), + .adaptive = true, + }); + return; + } + auto now = _selected.current(); + const auto id = slot.id; + const auto checked = !row->checked(); + delegate()->peerListSetRowChecked(row, checked); + const auto peer = slot.peerId + ? _to->owner().peerLoaded(slot.peerId) + : nullptr; + auto peerRemoved = false; + if (checked) { + now.push_back(id); + } else { + now.erase(ranges::remove(now, id), end(now)); + + peerRemoved = true; + for (const auto left : now) { + const auto i = ranges::find(_from, left, &TakenBoostSlot::id); + Assert(i != end(_from)); + if (i->peerId == slot.peerId) { + peerRemoved = false; + break; + } + } + } + _selected = std::move(now); + + if (peer) { + auto selectedPeers = _selectedPeers.current(); + const auto i = ranges::find(selectedPeers, not_null(peer)); + if (peerRemoved) { + Assert(i != end(selectedPeers)); + selectedPeers.erase(i); + _selectedPeers = std::move(selectedPeers); + } else if (i == end(selectedPeers) && checked) { + selectedPeers.insert(begin(selectedPeers), peer); + _selectedPeers = std::move(selectedPeers); + } + } +} + +object_ptr ReassignBoostFloodBox(int seconds) { + const auto days = seconds / 86400; + const auto hours = seconds / 3600; + const auto minutes = seconds / 60; + return Ui::MakeInformBox({ + .text = tr::lng_boost_error_flood_text( + lt_left, + rpl::single(Ui::Text::Bold((days > 1) + ? tr::lng_days(tr::now, lt_count, days) + : (hours > 1) + ? tr::lng_hours(tr::now, lt_count, hours) + : (minutes > 1) + ? tr::lng_minutes(tr::now, lt_count, minutes) + : tr::lng_seconds(tr::now, lt_count, seconds))), + Ui::Text::RichLangValue), + .title = tr::lng_boost_error_flood_title(), + }); +} + +object_ptr ReassignBoostSingleBox( + not_null to, + TakenBoostSlot from, + Fn slots, int sources)> reassign, + Fn cancel) { + const auto reassigned = std::make_shared(); + const auto slot = from.id; + const auto peer = to->owner().peer(from.peerId); + const auto confirmed = [=](Fn close) { + *reassigned = true; + reassign({ slot }, 1); + close(); + }; + + auto result = Box([=](not_null box) { + Ui::ConfirmBox(box, { + .text = tr::lng_boost_now_instead( + lt_channel, + rpl::single(Ui::Text::Bold(peer->name())), + lt_other, + rpl::single(Ui::Text::Bold(to->name())), + Ui::Text::WithEntities), + .confirmed = confirmed, + .confirmText = tr::lng_boost_now_replace(), + .labelPadding = st::boxRowPadding, + }); + box->verticalLayout()->insert( + 0, + CreateBoostReplaceUserpics( + box, + rpl::single(std::vector{ peer }), + to), + st::boxRowPadding + st::boostReplaceUserpicsPadding); + }); + + result->boxClosing() | rpl::filter([=] { + return !*reassigned; + }) | rpl::start_with_next(cancel, result->lifetime()); + + return result; +} + +} // namespace + +ForChannelBoostSlots ParseForChannelBoostSlots( + not_null channel, + const QVector &boosts) { + auto result = ForChannelBoostSlots(); + const auto now = base::unixtime::now(); + for (const auto &my : boosts) { + const auto &data = my.data(); + const auto id = data.vslot().v; + const auto cooldown = data.vcooldown_until_date().value_or(0); + const auto peerId = data.vpeer() + ? peerFromMTP(*data.vpeer()) + : PeerId(); + if (!peerId && cooldown <= now) { + result.free.push_back(id); + } else if (peerId == channel->id) { + result.already.push_back(id); + } else { + result.other.push_back({ + .id = id, + .expires = data.vexpires().v, + .peerId = peerId, + .cooldown = cooldown, + }); + } + } + return result; +} + +Ui::BoostCounters ParseBoostCounters( + const MTPpremium_BoostsStatus &status) { + const auto &data = status.data(); + const auto slots = data.vmy_boost_slots(); + return { + .level = data.vlevel().v, + .boosts = data.vboosts().v, + .thisLevelBoosts = data.vcurrent_level_boosts().v, + .nextLevelBoosts = data.vnext_level_boosts().value_or_empty(), + .mine = slots ? slots->v.size() : 0, + }; +} + +int BoostsForGift(not_null session) { + const auto key = u"boosts_per_sent_gift"_q; + return session->account().appConfig().get(key, 0); +} + +[[nodiscard]] int SourcesCount( + const std::vector &from, + const std::vector &slots) { + auto checked = base::flat_set(); + checked.reserve(slots.size()); + for (const auto slot : slots) { + const auto i = ranges::find(from, slot, &TakenBoostSlot::id); + Assert(i != end(from)); + checked.emplace(i->peerId); + } + return checked.size(); +} + +object_ptr ReassignBoostsBox( + not_null to, + std::vector from, + Fn slots, int sources)> reassign, + Fn cancel) { + Expects(!from.empty()); + + const auto now = base::unixtime::now(); + if (from.size() == 1 && from.front().cooldown > now) { + cancel(); + return ReassignBoostFloodBox(from.front().cooldown - now); + } else if (from.size() == 1 && from.front().peerId) { + return ReassignBoostSingleBox(to, from.front(), reassign, cancel); + } + const auto reassigned = std::make_shared(); + auto controller = std::make_unique(to, from); + const auto raw = controller.get(); + auto initBox = [=](not_null box) { + raw->selectedValue( + ) | rpl::start_with_next([=](std::vector slots) { + box->clearButtons(); + if (!slots.empty()) { + const auto sources = SourcesCount(from, slots); + box->addButton(tr::lng_boost_reassign_button(), [=] { + *reassigned = true; + reassign(slots, sources); + }); + } + box->addButton(tr::lng_cancel(), [=] { + box->closeBox(); + }); + }, box->lifetime()); + + box->boxClosing() | rpl::filter([=] { + return !*reassigned; + }) | rpl::start_with_next(cancel, box->lifetime()); + }; + return Box(std::move(controller), std::move(initBox)); +} + +object_ptr CreateBoostReplaceUserpics( + not_null parent, + rpl::producer>> from, + not_null to) { + struct State { + std::vector> from; + std::vector> buttons; + QImage layer; + rpl::variable count = 0; + bool painting = false; + }; + const auto full = st::boostReplaceUserpic.size.height() + + st::boostReplaceIconAdd.y() + + st::lineWidth; + auto result = object_ptr(parent, full); + const auto raw = result.data(); + const auto &st = st::boostReplaceUserpic; + const auto right = CreateChild(raw, to, st); + const auto overlay = CreateChild(raw); + + const auto state = raw->lifetime().make_state(); + std::move( + from + ) | rpl::start_with_next([=]( + const std::vector> &list) { + const auto &st = st::boostReplaceUserpic; + auto was = base::take(state->from); + auto buttons = base::take(state->buttons); + state->from.reserve(list.size()); + state->buttons.reserve(list.size()); + for (const auto &peer : list) { + state->from.push_back(peer); + const auto i = ranges::find(was, peer); + if (i != end(was)) { + const auto index = int(i - begin(was)); + Assert(buttons[index] != nullptr); + state->buttons.push_back(std::move(buttons[index])); + } else { + state->buttons.push_back( + std::make_unique(raw, peer, st)); + const auto raw = state->buttons.back().get(); + base::install_event_filter(raw, [=](not_null e) { + return (e->type() == QEvent::Paint && !state->painting) + ? base::EventFilterResult::Cancel + : base::EventFilterResult::Continue; + }); + } + } + state->count.force_assign(int(list.size())); + overlay->update(); + }, raw->lifetime()); + + rpl::combine( + raw->widthValue(), + state->count.value() + ) | rpl::start_with_next([=](int width, int count) { + const auto skip = st::boostReplaceUserpicsSkip; + const auto left = width - 2 * right->width() - skip; + const auto shift = std::min( + st::boostReplaceUserpicsShift, + (count > 1 ? (left / (count - 1)) : width)); + const auto total = right->width() + + (count ? (skip + right->width() + (count - 1) * shift) : 0); + auto x = (width - total) / 2; + for (const auto &single : state->buttons) { + single->moveToLeft(x, 0); + x += shift; + } + if (count) { + x += right->width() - shift + skip; + } + right->moveToLeft(x, 0); + overlay->setGeometry(QRect(0, 0, width, raw->height())); + }, raw->lifetime()); + + overlay->paintRequest( + ) | rpl::filter([=] { + return !state->buttons.empty(); + }) | rpl::start_with_next([=] { + const auto outerw = overlay->width(); + const auto ratio = style::DevicePixelRatio(); + if (state->layer.size() != QSize(outerw, full) * ratio) { + state->layer = QImage( + QSize(outerw, full) * ratio, + QImage::Format_ARGB32_Premultiplied); + state->layer.setDevicePixelRatio(ratio); + } + state->layer.fill(Qt::transparent); + + auto q = QPainter(&state->layer); + auto hq = PainterHighQualityEnabler(q); + const auto stroke = st::boostReplaceIconOutline; + const auto half = stroke / 2.; + auto pen = st::windowBg->p; + pen.setWidthF(stroke * 2.); + state->painting = true; + for (const auto &button : state->buttons) { + q.setPen(pen); + q.setBrush(Qt::NoBrush); + q.drawEllipse(button->geometry()); + const auto position = button->pos(); + button->render(&q, position, QRegion(), QWidget::DrawChildren); + } + state->painting = false; + const auto last = state->buttons.back().get(); + const auto add = st::boostReplaceIconAdd; + const auto skip = st::boostReplaceIconSkip; + const auto w = st::boostReplaceIcon.width() + 2 * skip; + const auto h = st::boostReplaceIcon.height() + 2 * skip; + const auto x = last->x() + last->width() - w + add.x(); + const auto y = last->y() + last->height() - h + add.y(); + + auto brush = QLinearGradient(QPointF(x + w, y + h), QPointF(x, y)); + brush.setStops(Ui::Premium::ButtonGradientStops()); + q.setBrush(brush); + pen.setWidthF(stroke); + q.setPen(pen); + q.drawEllipse(x - half, y - half, w + stroke, h + stroke); + st::boostReplaceIcon.paint(q, x + skip, y + skip, outerw); + + const auto size = st::boostReplaceArrow.size(); + st::boostReplaceArrow.paint( + q, + (last->x() + + last->width() + + (st::boostReplaceUserpicsSkip - size.width()) / 2), + (last->height() - size.height()) / 2, + outerw); + + q.end(); + + auto p = QPainter(overlay); + p.drawImage(0, 0, state->layer); + }, overlay->lifetime()); + return result; +} diff --git a/Telegram/SourceFiles/boxes/peers/replace_boost_box.h b/Telegram/SourceFiles/boxes/peers/replace_boost_box.h new file mode 100644 index 000000000..f15cf0b14 --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/replace_boost_box.h @@ -0,0 +1,53 @@ +/* +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 +*/ +#pragma once + +#include "base/object_ptr.h" + +namespace Main { +class Session; +} // namespace Main + +namespace Ui { +struct BoostCounters; +class BoxContent; +class RpWidget; +} // namespace Ui + +struct TakenBoostSlot { + int id = 0; + TimeId expires = 0; + PeerId peerId = 0; + TimeId cooldown = 0; +}; + +struct ForChannelBoostSlots { + std::vector free; + std::vector already; + std::vector other; +}; + +[[nodiscard]] ForChannelBoostSlots ParseForChannelBoostSlots( + not_null channel, + const QVector &boosts); + +[[nodiscard]] Ui::BoostCounters ParseBoostCounters( + const MTPpremium_BoostsStatus &status); + +[[nodiscard]] int BoostsForGift(not_null session); + +object_ptr ReassignBoostsBox( + not_null to, + std::vector from, + Fn slots, int sources)> reassign, + Fn cancel); + +[[nodiscard]] object_ptr CreateBoostReplaceUserpics( + not_null parent, + rpl::producer>> from, + not_null to); diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.cpp b/Telegram/SourceFiles/ui/boxes/boost_box.cpp index 197bcc0ad..68010c530 100644 --- a/Telegram/SourceFiles/ui/boxes/boost_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/boost_box.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/boost_box.h" #include "lang/lang_keys.h" +#include "ui/boxes/confirm_box.h" #include "ui/effects/fireworks_animation.h" #include "ui/effects/premium_graphics.h" #include "ui/layers/generic_box.h" @@ -177,9 +178,10 @@ void BoostBox( (st::boxRowPadding + QMargins(0, st::boostTextSkip, 0, st::boostBottomSkip))); + const auto allowMulti = data.allowMulti; auto submit = state->data.value( ) | rpl::map([=](BoostCounters counters) { - return !counters.nextLevelBoosts + return (!counters.nextLevelBoosts || (counters.mine && !allowMulti)) ? tr::lng_box_ok() : (counters.mine > 0) ? tr::lng_boost_again_button() @@ -189,7 +191,8 @@ void BoostBox( const auto button = box->addButton(rpl::duplicate(submit), [=] { if (state->submitted) { return; - } else if (state->data.current().nextLevelBoosts > 0) { + } else if (state->data.current().nextLevelBoosts > 0 + && (allowMulti || !state->data.current().mine)) { state->submitted = true; const auto was = state->data.current().mine; @@ -346,6 +349,49 @@ object_ptr MakeLinkLabel( return result; } +void BoostBoxAlready(not_null box) { + ConfirmBox(box, { + .text = tr::lng_boost_error_already_text(Text::RichLangValue), + .title = tr::lng_boost_error_already_title(), + .inform = true, + }); +} + +void GiftForBoostsBox( + not_null box, + QString channel, + int receive, + bool again) { + ConfirmBox(box, { + .text = (again + ? tr::lng_boost_need_more_again + : tr::lng_boost_need_more_text)( + lt_count, + rpl::single(receive) | tr::to_count(), + lt_channel, + rpl::single(TextWithEntities{ channel }), + Text::RichLangValue), + .title = tr::lng_boost_need_more(), + .inform = true, + }); +} + +void GiftedNoBoostsBox(not_null box) { + InformBox(box, { + .text = tr::lng_boost_error_gifted_text(Text::RichLangValue), + .title = tr::lng_boost_error_gifted_title(), + }); +} + +void PremiumForBoostsBox(not_null box, Fn buyPremium) { + ConfirmBox(box, { + .text = tr::lng_boost_error_premium_text(Text::RichLangValue), + .confirmed = buyPremium, + .confirmText = tr::lng_boost_error_premium_yes(), + .title = tr::lng_boost_error_premium_title(), + }); +} + void AskBoostBox( not_null box, AskBoostBoxData data, diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.h b/Telegram/SourceFiles/ui/boxes/boost_box.h index e8ef31993..875a1405e 100644 --- a/Telegram/SourceFiles/ui/boxes/boost_box.h +++ b/Telegram/SourceFiles/ui/boxes/boost_box.h @@ -33,6 +33,7 @@ struct BoostCounters { struct BoostBoxData { QString name; BoostCounters boost; + bool allowMulti = false; }; void BoostBox( @@ -40,6 +41,15 @@ void BoostBox( BoostBoxData data, Fn)> boost); +void BoostBoxAlready(not_null box); +void GiftForBoostsBox( + not_null box, + QString channel, + int receive, + bool again); +void GiftedNoBoostsBox(not_null box); +void PremiumForBoostsBox(not_null box, Fn buyPremium); + struct AskBoostBoxData { QString link; BoostCounters boost; diff --git a/Telegram/SourceFiles/ui/boxes/confirm_box.cpp b/Telegram/SourceFiles/ui/boxes/confirm_box.cpp index 22b2b3987..5af3f3b28 100644 --- a/Telegram/SourceFiles/ui/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/confirm_box.cpp @@ -100,11 +100,4 @@ object_ptr MakeConfirmBox(ConfirmBoxArgs &&args) { return Box(ConfirmBox, std::move(args)); } -object_ptr MakeInformBox(v::text::data text) { - return MakeConfirmBox({ - .text = std::move(text), - .inform = true, - }); -} - } // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/confirm_box.h b/Telegram/SourceFiles/ui/boxes/confirm_box.h index b65a04361..c33192e29 100644 --- a/Telegram/SourceFiles/ui/boxes/confirm_box.h +++ b/Telegram/SourceFiles/ui/boxes/confirm_box.h @@ -40,10 +40,24 @@ struct ConfirmBoxArgs { bool strictCancel = false; }; -void ConfirmBox(not_null box, ConfirmBoxArgs &&args); +void ConfirmBox(not_null box, ConfirmBoxArgs &&args); -[[nodiscard]] object_ptr MakeConfirmBox( - ConfirmBoxArgs &&args); -[[nodiscard]] object_ptr MakeInformBox(v::text::data text); +inline void InformBox(not_null box, ConfirmBoxArgs &&args) { + args.inform = true; + ConfirmBox(box, std::move(args)); +} + +[[nodiscard]] object_ptr MakeConfirmBox(ConfirmBoxArgs &&args); + +[[nodiscard]] inline object_ptr MakeInformBox( + ConfirmBoxArgs &&args) { + args.inform = true; + return MakeConfirmBox(std::move(args)); +} + +[[nodiscard]] inline object_ptr MakeInformBox( + v::text::data text) { + return MakeInformBox({ .text = std::move(text) }); +} } // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/userpic_button.cpp b/Telegram/SourceFiles/ui/controls/userpic_button.cpp index bad612d20..67717c517 100644 --- a/Telegram/SourceFiles/ui/controls/userpic_button.cpp +++ b/Telegram/SourceFiles/ui/controls/userpic_button.cpp @@ -1108,57 +1108,4 @@ not_null CreateUploadSubButton( return upload; } -object_ptr CreateBoostReplaceUserpics( - not_null parent, - not_null from, - not_null to) { - const auto full = st::boostReplaceUserpic.size.height() - + st::boostReplaceIconAdd.y() - + st::lineWidth; - auto result = object_ptr(parent, full); - const auto raw = result.data(); - const auto &st = st::boostReplaceUserpic; - const auto left = CreateChild(raw, from, st); - const auto right = CreateChild(raw, to, st); - const auto overlay = CreateChild(raw); - raw->widthValue( - ) | rpl::start_with_next([=](int width) { - const auto skip = st::boostReplaceUserpicsSkip; - const auto total = left->width() + skip + right->width(); - left->moveToLeft((width - total) / 2, 0); - right->moveToLeft(left->x() + left->width() + skip, 0); - overlay->setGeometry(QRect(0, 0, width, raw->height())); - }, raw->lifetime()); - overlay->paintRequest( - ) | rpl::start_with_next([=] { - const auto outerw = overlay->width(); - const auto add = st::boostReplaceIconAdd; - const auto skip = st::boostReplaceIconSkip; - const auto w = st::boostReplaceIcon.width() + 2 * skip; - const auto h = st::boostReplaceIcon.height() + 2 * skip; - const auto x = left->x() + left->width() - w + add.x(); - const auto y = left->y() + left->height() - h + add.y(); - const auto stroke = st::boostReplaceIconOutline; - const auto half = stroke / 2.; - auto p = QPainter(overlay); - auto hq = PainterHighQualityEnabler(p); - auto pen = st::windowBg->p; - pen.setWidthF(stroke); - p.setPen(pen); - auto brush = QLinearGradient(QPointF(x + w, y + h), QPointF(x, y)); - brush.setStops(Premium::ButtonGradientStops()); - p.setBrush(brush); - p.drawEllipse(x - half, y - half, w + stroke, h + stroke); - st::boostReplaceIcon.paint(p, x + skip, y + skip, outerw); - - const auto size = st::boostReplaceArrow.size(); - st::boostReplaceArrow.paint( - p, - (outerw - size.width()) / 2, - (left->height() - size.height()) / 2, - outerw); - }, overlay->lifetime()); - return result; -} - } // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/userpic_button.h b/Telegram/SourceFiles/ui/controls/userpic_button.h index db3cdd3fd..8d2eb2adf 100644 --- a/Telegram/SourceFiles/ui/controls/userpic_button.h +++ b/Telegram/SourceFiles/ui/controls/userpic_button.h @@ -204,9 +204,4 @@ private: not_null contact, not_null controller); -[[nodiscard]] object_ptr CreateBoostReplaceUserpics( - not_null parent, - not_null from, - not_null to); - } // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index ef364e0fe..9c49a6c15 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -247,7 +247,6 @@ boostTitleSkip: 32px; boostTitle: FlatLabel(defaultFlatLabel) { minWidth: 40px; textFg: windowBoldFg; - align: align(top); maxHeight: 24px; style: TextStyle(boxTextStyle) { font: font(17px semibold); @@ -258,6 +257,10 @@ boostText: FlatLabel(defaultFlatLabel) { minWidth: 40px; align: align(top); } +boostReassignText: FlatLabel(defaultFlatLabel) { + minWidth: 40px; + align: align(top); +} boostBottomSkip: 6px; boostBox: Box(premiumPreviewDoubledLimitsBox) { buttonPadding: margins(22px, 22px, 22px, 22px); @@ -271,11 +274,12 @@ boostBox: Box(premiumPreviewDoubledLimitsBox) { boostReplaceUserpicsPadding: margins(0px, 18px, 0px, 20px); boostReplaceUserpicsSkip: 42px; +boostReplaceUserpicsShift: 24px; boostReplaceUserpic: UserpicButton(defaultUserpicButton) { size: size(60px, 60px); photoSize: 60px; } -boostReplaceIcon: icon{{ "stories/boost_mini", windowBg }}; +boostReplaceIcon: icon{{ "stories/boost_mini", premiumButtonFg }}; boostReplaceIconSkip: 3px; boostReplaceIconOutline: 2px; boostReplaceIconAdd: point(4px, 2px); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 2cb64bcca..89fac17ef 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/add_contact_box.h" #include "boxes/peers/add_bot_to_chat_box.h" #include "boxes/peers/edit_peer_info_box.h" +#include "boxes/peers/replace_boost_box.h" #include "boxes/delete_messages_box.h" #include "window/window_adaptive.h" #include "window/window_controller.h" @@ -267,19 +268,6 @@ Fn PausedIn( return [=] { return IsPaused(controller, level); }; } -Ui::BoostCounters ParseBoostCounters( - const MTPpremium_BoostsStatus &status) { - const auto &data = status.data(); - const auto slots = data.vmy_boost_slots(); - return { - .level = data.vlevel().v, - .boosts = data.vboosts().v, - .thisLevelBoosts = data.vcurrent_level_boosts().v, - .nextLevelBoosts = data.vnext_level_boosts().value_or_empty(), - .mine = slots ? slots->v.size() : 0, - }; -} - bool operator==(const PeerThemeOverride &a, const PeerThemeOverride &b) { return (a.peer == b.peer) && (a.theme == b.theme); } @@ -648,6 +636,7 @@ void SessionNavigation::resolveBoostState(not_null channel) { uiShow()->show(Box(Ui::BoostBox, Ui::BoostBoxData{ .name = channel->name(), .boost = ParseBoostCounters(result), + .allowMulti = (BoostsForGift(_session) > 0), }, submit)); }).fail([=](const MTP::Error &error) { _boostStateResolving = nullptr; @@ -663,86 +652,66 @@ void SessionNavigation::applyBoost( const auto &data = result.data(); _session->data().processUsers(data.vusers()); _session->data().processChats(data.vchats()); - const auto &list = data.vmy_boosts().v; - if (list.isEmpty()) { - if (!_session->premium()) { - const auto jumpToPremium = [=] { + const auto slots = ParseForChannelBoostSlots( + channel, + data.vmy_boosts().v); + if (!slots.free.empty()) { + applyBoostsChecked(channel, { slots.free.front() }, done); + } else if (slots.other.empty()) { + if (!slots.already.empty()) { + if (const auto receive = BoostsForGift(_session)) { + const auto again = true; + const auto name = channel->name(); + uiShow()->show( + Box(Ui::GiftForBoostsBox, name, receive, again)); + } else { + uiShow()->show(Box(Ui::BoostBoxAlready)); + } + } else if (!_session->premium()) { + uiShow()->show(Box(Ui::PremiumForBoostsBox, [=] { const auto id = peerToChannel(channel->id).bare; Settings::ShowPremium( parentController(), "channel_boost__" + QString::number(id)); - }; - uiShow()->show(Ui::MakeConfirmBox({ - .text = tr::lng_boost_error_premium_text( - Ui::Text::RichLangValue), - .confirmed = jumpToPremium, - .confirmText = tr::lng_boost_error_premium_yes(), - .title = tr::lng_boost_error_premium_title(), })); + } else if (const auto receive = BoostsForGift(_session)) { + const auto again = false; + const auto name = channel->name(); + uiShow()->show( + Box(Ui::GiftForBoostsBox, name, receive, again)); } else { - uiShow()->show(Ui::MakeConfirmBox({ - .text = tr::lng_boost_error_gifted_text( - Ui::Text::RichLangValue), - .title = tr::lng_boost_error_gifted_title(), - .inform = true, - })); + uiShow()->show(Box(Ui::GiftedNoBoostsBox)); } done({}); - return; - } - auto slot = int(); - auto different = PeerId(); - auto earliest = TimeId(-1); - const auto now = base::unixtime::now(); - for (const auto &my : list) { - const auto &data = my.data(); - const auto cooldown = data.vcooldown_until_date().value_or(0); - const auto peerId = data.vpeer() - ? peerFromMTP(*data.vpeer()) - : PeerId(); - if (!peerId && cooldown <= now) { - applyBoostChecked(channel, data.vslot().v, done); - return; - } else if (peerId != channel->id - && (earliest < 0 || cooldown < earliest)) { - slot = data.vslot().v; - different = peerId; - earliest = cooldown; - } - } - if (different) { - if (earliest > now) { - const auto seconds = earliest - now; - const auto days = seconds / 86400; - const auto hours = seconds / 3600; - const auto minutes = seconds / 60; - uiShow()->show(Ui::MakeConfirmBox({ - .text = tr::lng_boost_error_flood_text( - lt_left, - rpl::single(Ui::Text::Bold((days > 1) - ? tr::lng_days(tr::now, lt_count, days) - : (hours > 1) - ? tr::lng_hours(tr::now, lt_count, hours) - : (minutes > 1) - ? tr::lng_minutes(tr::now, lt_count, minutes) - : tr::lng_seconds(tr::now, lt_count, seconds))), - Ui::Text::RichLangValue), - .title = tr::lng_boost_error_flood_title(), - .inform = true, - })); - done({}); - } else { - const auto peer = _session->data().peer(different); - replaceBoostConfirm(peer, channel, slot, done); - } } else { - uiShow()->show(Ui::MakeConfirmBox({ - .text = tr::lng_boost_error_already_text( - Ui::Text::RichLangValue), - .title = tr::lng_boost_error_already_title(), - .inform = true, - })); - done({}); + const auto weak = std::make_shared>(); + const auto reassign = [=](std::vector slots, int sources) { + const auto count = int(slots.size()); + const auto callback = [=](Ui::BoostCounters counters) { + if (const auto strong = weak->data()) { + strong->closeBox(); + } + done(counters); + uiShow()->showToast(tr::lng_boost_reassign_done( + tr::now, + lt_count, + count, + lt_channels, + tr::lng_boost_reassign_channels( + tr::now, + lt_count, + sources))); + }; + applyBoostsChecked( + channel, + slots, + crl::guard(this, callback)); + }; + *weak = uiShow()->show(ReassignBoostsBox( + channel, + slots.other, + reassign, + [=] { done({}); })); } }).fail([=](const MTP::Error &error) { const auto type = error.type(); @@ -751,48 +720,18 @@ void SessionNavigation::applyBoost( }).handleFloodErrors().send(); } -void SessionNavigation::replaceBoostConfirm( - not_null from, +void SessionNavigation::applyBoostsChecked( not_null channel, - int slot, + std::vector slots, Fn done) { - const auto forwarded = std::make_shared(false); - const auto confirmed = [=](Fn close) { - *forwarded = true; - applyBoostChecked(channel, slot, done); - close(); - }; - const auto box = uiShow()->show(Box([=](not_null box) { - Ui::ConfirmBox(box, { - .text = tr::lng_boost_now_instead( - lt_channel, - rpl::single(Ui::Text::Bold(from->name())), - lt_other, - rpl::single(Ui::Text::Bold(channel->name())), - Ui::Text::WithEntities), - .confirmed = confirmed, - .confirmText = tr::lng_boost_now_replace(), - .labelPadding = st::boxRowPadding, - }); - box->verticalLayout()->insert( - 0, - Ui::CreateBoostReplaceUserpics(box, from, channel), - st::boxRowPadding + st::boostReplaceUserpicsPadding); + auto mtp = MTP_vector_from_range(ranges::views::all( + slots + ) | ranges::views::transform([](int slot) { + return MTP_int(slot); })); - box->boxClosing() | rpl::filter([=] { - return !*forwarded; - }) | rpl::start_with_next([=] { - done({}); - }, box->lifetime()); -} - -void SessionNavigation::applyBoostChecked( - not_null channel, - int slot, - Fn done) { _api.request(MTPpremium_ApplyBoost( MTP_flags(MTPpremium_ApplyBoost::Flag::f_slots), - MTP_vector({ MTP_int(slot) }), + std::move(mtp), channel->input )).done([=](const MTPpremium_MyBoosts &result) { _api.request(MTPpremium_GetBoostsStatus( diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index aecd4150d..0a5fa2225 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -318,14 +318,9 @@ private: void applyBoost( not_null channel, Fn done); - void replaceBoostConfirm( - not_null from, + void applyBoostsChecked( not_null channel, - int slot, - Fn done); - void applyBoostChecked( - not_null channel, - int slot, + std::vector slots, Fn done); const not_null _session; @@ -755,7 +750,4 @@ void ActivateWindow(not_null controller); not_null controller, GifPauseReason level); -[[nodiscard]] Ui::BoostCounters ParseBoostCounters( - const MTPpremium_BoostsStatus &status); - } // namespace Window From e4d920b1480f8c0b5f7be941bf79a7f25f26a7e5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Nov 2023 10:21:59 +0400 Subject: [PATCH 34/41] Show multiboost "x5" badge in boost box. --- Telegram/SourceFiles/ui/boxes/boost_box.cpp | 73 ++++++++++++++++++- Telegram/SourceFiles/ui/effects/premium.style | 6 ++ 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.cpp b/Telegram/SourceFiles/ui/boxes/boost_box.cpp index 68010c530..633cb3e1f 100644 --- a/Telegram/SourceFiles/ui/boxes/boost_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/boost_box.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" #include "ui/widgets/buttons.h" +#include "ui/painter.h" #include "styles/style_giveaway.h" #include "styles/style_layers.h" #include "styles/style_premium.h" @@ -45,6 +46,69 @@ namespace { return data; } +[[nodiscard]] object_ptr MakeTitle( + not_null box, + rpl::producer title, + rpl::producer repeated) { + auto result = object_ptr(box); + + struct State { + not_null title; + not_null repeated; + }; + const auto notEmpty = [](const QString &text) { + return !text.isEmpty(); + }; + const auto state = box->lifetime().make_state(State{ + .title = Ui::CreateChild( + result.data(), + rpl::duplicate(title), + st::boostTitle), + .repeated = Ui::CreateChild( + result.data(), + rpl::duplicate(repeated) | rpl::filter(notEmpty), + st::boostTitleBadge), + }); + state->title->show(); + state->repeated->showOn(std::move(repeated) | rpl::map(notEmpty)); + + result->resize(result->width(), st::boostTitle.style.font->height); + + rpl::combine( + result->widthValue(), + rpl::duplicate(title), + state->repeated->shownValue(), + state->repeated->widthValue() + ) | rpl::start_with_next([=](int outer, auto&&, bool shown, int badge) { + const auto repeated = shown ? badge : 0; + const auto skip = st::boostTitleBadgeSkip; + const auto available = outer - repeated - skip; + const auto use = std::min(state->title->textMaxWidth(), available); + state->title->resizeToWidth(use); + const auto left = (outer - use - skip - repeated) / 2; + state->title->moveToLeft(left, 0); + const auto mleft = st::boostTitleBadge.margin.left(); + const auto mtop = st::boostTitleBadge.margin.top(); + state->repeated->moveToLeft(left + use + skip + mleft, mtop); + }, result->lifetime()); + + const auto badge = state->repeated; + badge->paintRequest() | rpl::start_with_next([=] { + auto p = QPainter(badge); + auto hq = PainterHighQualityEnabler(p); + const auto radius = std::min(badge->width(), badge->height()) / 2; + p.setPen(Qt::NoPen); + auto brush = QLinearGradient( + QPointF(badge->width(), badge->height()), + QPointF()); + brush.setStops(Ui::Premium::ButtonGradientStops()); + p.setBrush(brush); + p.drawRoundedRect(badge->rect(), radius, radius); + }, badge->lifetime()); + + return result; +} + } // namespace void StartFireworks(not_null parent) { @@ -111,6 +175,10 @@ void BoostBox( ? tr::lng_boost_channel_title_first() : tr::lng_boost_channel_title_more(); }) | rpl::flatten_latest(); + auto repeated = state->data.value( + ) | rpl::map([=](BoostCounters counters) { + return (counters.mine > 1) ? u"x%1"_q.arg(counters.mine) : u""_q; + }); auto text = state->data.value( ) | rpl::map([=](BoostCounters counters) { @@ -164,10 +232,7 @@ void BoostBox( }) | rpl::flatten_latest(); box->addRow( - object_ptr( - box, - std::move(title), - st::boostTitle), + MakeTitle(box, std::move(title), std::move(repeated)), st::boxRowPadding + QMargins(0, st::boostTitleSkip, 0, 0)); box->addRow( diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index 9c49a6c15..69c356610 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -252,6 +252,12 @@ boostTitle: FlatLabel(defaultFlatLabel) { font: font(17px semibold); } } +boostTitleBadge: FlatLabel(defaultFlatLabel) { + margin: margins(4px, 2px, 4px, 2px); + style: semiboldTextStyle; + textFg: premiumButtonFg; +} +boostTitleBadgeSkip: 6px; boostTextSkip: 5px; boostText: FlatLabel(defaultFlatLabel) { minWidth: 40px; From 9b2f2b104fe9b0ca92008b0adda803cfd0233475 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Nov 2023 22:25:08 +0400 Subject: [PATCH 35/41] Show enlarge photo button in webpage preview. --- .../icons/chat/link_photo_enlarge.png | Bin 0 -> 661 bytes .../icons/chat/link_photo_enlarge@2x.png | Bin 0 -> 1171 bytes .../icons/chat/link_photo_enlarge@3x.png | Bin 0 -> 1813 bytes Telegram/SourceFiles/data/data_web_page.cpp | 4 ++ Telegram/SourceFiles/data/data_web_page.h | 1 + .../history/view/history_view_cursor_state.h | 1 + .../history/view/media/history_view_photo.cpp | 57 +++++++++++++++--- .../history/view/media/history_view_photo.h | 2 + .../view/media/history_view_web_page.cpp | 6 +- Telegram/SourceFiles/ui/chat/chat.style | 6 ++ Telegram/SourceFiles/ui/chat/chat_style.cpp | 4 ++ Telegram/SourceFiles/ui/chat/chat_style.h | 1 + 12 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 Telegram/Resources/icons/chat/link_photo_enlarge.png create mode 100644 Telegram/Resources/icons/chat/link_photo_enlarge@2x.png create mode 100644 Telegram/Resources/icons/chat/link_photo_enlarge@3x.png diff --git a/Telegram/Resources/icons/chat/link_photo_enlarge.png b/Telegram/Resources/icons/chat/link_photo_enlarge.png new file mode 100644 index 0000000000000000000000000000000000000000..c4ba35289e0e7b521d0448a6abe84d9dda19f0df GIT binary patch literal 661 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgf?7ydrV~B;| z-QfMJ8v`Zw)lHf7hmpr?O47v6B?cQjy*6sh?iD(vHkrpulBX=qaDj-KW<7&b-4dN0 zDuwSa+m^oDDStmhcE@YULSlxX+Cta-dN|tE~9~<-b+q%z=O>_t_ZH;%zijgsW9Xvh46ffw-!@^OOs>yDb(JUm2nm5}+Y+FkwPo zsMgho1sPkTrd0Btditqu|MAilGX2Njf47c~HIZ^X8MSuWF@X=u>)H=r%rPre<8^9| z5dUyBOEiA@WzS@eAIVEXw3c7i6m(mB@oCY?w9Pl_{;dhnVA*ps=YaC0mW%6WC^dZk zsWa7!Ggw2!M5^~_(!qy6YvlGGwstzS*-J&pVsmHAo!4J~{k1#&=;sB&Z)ItlC3}uP zPCUHBLah5?%I>>XK&TZ(SBG%eXd_{ zn|Q*$b=O~~7;U_nlOynJ(&?uG@86bv{{45&k?zDsM`yp?ch995N%RG++!CV~=)L@M zqu{jj&mW&nXZ5MkntI6L!P1HQZ@>NUd&0egk8Jf{b$?$rgW=WFuT_?pZbv^dmFvHM luEx(`rjNv@^nd>i?@xdJcW*@8VP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NF{YgYYR9Fe^m&+^dQ546I*TAE^ zqDV?eF)(06p%|f1{s9x2n;G~643vyyWS|TTkdS0zqL56Kk@C#zq$H1ndq4NCY};@D ze!sKN&!KMj{067J*Iw(p*Iw@v3MIaT1riqc&n%D>;JvxIx$W)kot>Q(6%|EAMFj-~ z;c)ou?CkLHaA|32adGkT@-hHy5RtO7vgzsRNF?&h`+I$Tou8ktuC5MpfxS#>YU;?y z$kWr4smb&6vq^k;d6}4)NKa3uYCm#wU<3=9llhV1NYfU>f(s;a8` z`uebquJrcyc1cM|3^&=ZqRqj^%)FD@>)51y2i6j$8h;$js{<>lqBJUTI1-{@;qq)JEiH{L+9W5t zLs7wJXJU%8BxK zpO=>>JV!@It_)U03YwruEmkr_F>!+)@~Hy&P6%IJU5$;6DSlZzJUoC#?MO-r($4>Y zNWuf*fF`FzQU71ZGmejsg+C`J#{p=&JwHDe2>O&RLxz)6KH6ZjnVA_~gta6Zf?l!j z;CI>X?Ci*`bWO)GIYq?Cho^*%<{%U^|FM?voSd9EpvI{k$T>;W49FagC0S3drW>rXmJh@E}Pj z48VNjh2oRREiEm?mMGNR-0X!A1D&~8;K*C%n;0ZRp~}k2>+5R~SKT8f1Q%webmn3K zW!kb2aBQQr!AMO_O)lucNKEOfjo>@x8zu@LD599SQJ#{=pj>Tjt&db}M@;F=#RAPQ z?qekF>FJ?sDj=#K6ALa@IR&S#R-ARp%*;dyETq(u1znRqrA9FQl(@lD%5faUStpFa zT+FguX_rDwM{S-(!%+uLhxZN0g<`8rfQ5%3hY;WJ0S lcwUphN?0Iafq%sUzX1nY)I*Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS>e@R3^RA>e5T5CvESrpE+d}S(% zHkm;MDvGG2L70$%4>T-9Qo$Eu(l0@LAR;1ZLSP`_kMv*uF!PlniYOX_N(#P6L!u-l zC#}FoVW#HHH*=UZyK|4{?sM-s<}l~}T6>@M-g~dT*4p>T5&froK=**|0o?<-2XqhU z9ylluSZE~Zy3?#Nl8hLj*eRCp%z~Y3yWK~ZoPc@avxiyr?FGH! zF(Ik$+}zy#`}ZRwBYk~+ot&H?($dlrLFD4ai^<8!4Gj%zYip8(RaI3-j~-P`MH3iZ zzI=Ikd0C{Ku&I1}e2g+YcI;S8OpN3P?(XhJL^aAm69lZo#>R%os$9@5(nNI`rY8r@5`#Xx|)?*R#vtzD)Rvu zV_{)odU`q{J~J&1;$mde7x*qXI9M80t=V(u&S7tmVG9cj*4EZWnPZW02nDLgj~^Sw z2Lf(Ck^-Q=zn_Q7bR5wENKH-U;f3p%w1i)H77c6>?uiNje99xsL>xBBc=hTPXMS+| z_H9;08yg!Q@s}=LVySX-b9tylI9&P5*|TSZf`W(=M;yDKhN=U|Nap9~iLGfNip#LL zxHu96prfOM9clryBCcGyf~-KP6fVQqvPZEZ+|wK1i}gW{d<6sXgvlLcqO5FaVq!LGbq88_M|M{QP`LhVS0JBPI_XJdnhZndI7v zY=MM=#gH9VNombOO!lkaSV61;L?{n%8Md;rLR{S4-3cZDu6_FSDGm7n5aRNX#Amx8 ztOAnAhLc=p3>`sSWdbpK-dJ{-nVG0)L}x4@ARszAn$WPL(EOkw&R9=R&x;o?Xodik z*ht4{2n7gM6KHL1rR_dAILOc1w9Eti;YX4P;O5Pn{6YmxuC4g~{d?kzblcX}mWCPu zqT-cRfKwuuVb7mGCw7S75fKqI)CgdgXmxdUYHCWUATGmtdwbCvB-W-SZ$xesKtaTw z%&x93_GB6*7ZA7%+uYnlDM1_)5)#;xP4j92DJd!F@DMjPy`o9g0JLe@(~N4Ghjmm` z6j5=8Y#d;mD5AnnVU6SwstgC)164E0L)FMr|KZ}s{RS$md17KBkI15;B9a0Cy+BMOO9-jGBk>=pwfagM5D*&%wzvgTusG`6Tc##zkxTdDY%vSsP z^XF$~W>~g(-v|v2RYzlfF7@^GtP+6HB$G^zd3fTwNA)FQh#^eH=H%g^8V(}l(9n=b z6|}q2LRA!+{rvnoJ3B>u*x@g*4Yv*Y&#V#xjLYiYy?Y@cA#5Ej@+cx?e0)5fLXAFi zf~U#0{aeP1#PbYGN=okDy(?)03Jp{-xM~5Qn}9HYdIvxJtsp!+Trw)aDTwuk4-x2p zAZVeA=TS*Doa@)Gi=N92myu_ty}jMT!vlv(6h#DDlh@5GJJE8<$;tWj=@X9uhBzZ5 zBj_BG28YWwsD-u!Zi3O#Q8bASwKtqWuxV*&5w&6k^A4SDf?7C8ckbN5ll1ubI8GN` z=tyU=X%S9qYina;W91)f4ykRluK);tgfJoH`w!U0+NlWspW24>Qjwbu9rAwbR#{nj z=yX-8kfd$sNy_ly>Uqe}AIvAD9Y{VzPcV37Js9blOToIKY!|LX0P<(dtXxrZQw#(k zFGWp&X{;VBH2AJh=pN8LpnE{~fbId^1G)!(g$MowfHDwG_3`nJ00000NkvXXu0mjf D5=Tbk literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/data/data_web_page.cpp b/Telegram/SourceFiles/data/data_web_page.cpp index e8eef8978..95b042775 100644 --- a/Telegram/SourceFiles/data/data_web_page.cpp +++ b/Telegram/SourceFiles/data/data_web_page.cpp @@ -391,3 +391,7 @@ bool WebPageData::computeDefaultSmallMedia() const { } return false; } + +bool WebPageData::suggestEnlargePhoto() const { + return !siteName.isEmpty() || !title.isEmpty() || !description.empty(); +} diff --git a/Telegram/SourceFiles/data/data_web_page.h b/Telegram/SourceFiles/data/data_web_page.h index 9aea4a0f5..04f16a7b6 100644 --- a/Telegram/SourceFiles/data/data_web_page.h +++ b/Telegram/SourceFiles/data/data_web_page.h @@ -90,6 +90,7 @@ struct WebPageData { [[nodiscard]] QString displayedSiteName() const; [[nodiscard]] bool computeDefaultSmallMedia() const; + [[nodiscard]] bool suggestEnlargePhoto() const; const WebPageId id = 0; WebPageType type = WebPageType::None; diff --git a/Telegram/SourceFiles/history/view/history_view_cursor_state.h b/Telegram/SourceFiles/history/view/history_view_cursor_state.h index 82fcda954..d5bcaa6f9 100644 --- a/Telegram/SourceFiles/history/view/history_view_cursor_state.h +++ b/Telegram/SourceFiles/history/view/history_view_cursor_state.h @@ -22,6 +22,7 @@ enum class CursorState : char { None, Text, Date, + Enlarge, Forwarded, }; diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index b3d79c6fe..85a2997a8 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_click_handler.h" #include "data/data_file_origin.h" #include "data/data_auto_download.h" +#include "data/data_web_page.h" #include "core/application.h" #include "styles/style_chat.h" @@ -242,6 +243,7 @@ QSize Photo::countCurrentSize(int newWidth) { maxWidth()); newWidth = qMax(pix.width(), minWidth); auto newHeight = qMax(pix.height(), st::minPhotoSize); + auto imageHeight = newHeight; if (_parent->hasBubble() && !_caption.isEmpty()) { auto captionMaxWidth = st::msgPadding.left() + _caption.maxWidth() @@ -252,7 +254,7 @@ QSize Photo::countCurrentSize(int newWidth) { } const auto maxWithCaption = qMin(st::msgMaxWidth, captionMaxWidth); newWidth = qMin(qMax(newWidth, maxWithCaption), thumbMaxWidth); - newHeight = adjustHeightForLessCrop( + imageHeight = newHeight = adjustHeightForLessCrop( dimensions, { newWidth, newHeight }); const auto captionw = newWidth @@ -266,6 +268,15 @@ QSize Photo::countCurrentSize(int newWidth) { newHeight += st::msgPadding.bottom(); } } + const auto enlargeInner = st::historyPageEnlargeSize; + const auto enlargeOuter = 2 * st::historyPageEnlargeSkip + enlargeInner; + const auto showEnlarge = (_parent->media() != this) + && _parent->data()->media() + && _parent->data()->media()->webpage() + && _parent->data()->media()->webpage()->suggestEnlargePhoto() + && (newWidth >= enlargeOuter) + && (imageHeight >= enlargeOuter); + _showEnlarge = showEnlarge ? 1 : 0; return { newWidth, newHeight }; } @@ -351,15 +362,16 @@ void Photo::draw(Painter &p, const PaintContext &context) const { fillImageOverlay(p, rthumb, rounding, context); } } - if (radial || (!loaded && !_data->loading())) { - const auto radialOpacity = (radial && loaded && !_data->uploading()) - ? _animation->radial.opacity() : - 1.; - const auto innerSize = st::msgFileLayout.thumbSize; - QRect inner(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize); + + const auto showEnlarge = loaded && _showEnlarge; + const auto paintInCenter = (radial || (!loaded && !_data->loading())); + if (paintInCenter || showEnlarge) { p.setPen(Qt::NoPen); if (context.selected()) { p.setBrush(st->msgDateImgBgSelected()); + } else if (showEnlarge) { + const auto over = ClickHandler::showAsActive(_openl); + p.setBrush(over ? st->msgDateImgBgOver() : st->msgDateImgBg()); } else if (isThumbAnimation()) { const auto over = _animation->a_thumbOver.value(1.); p.setBrush(anim::brush(st->msgDateImgBg(), st->msgDateImgBgOver(), over)); @@ -367,6 +379,13 @@ void Photo::draw(Painter &p, const PaintContext &context) const { const auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st->msgDateImgBgOver() : st->msgDateImgBg()); } + } + if (paintInCenter) { + const auto radialOpacity = (radial && loaded && !_data->uploading()) + ? _animation->radial.opacity() : + 1.; + const auto innerSize = st::msgFileLayout.thumbSize; + QRect inner(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize); p.setOpacity(radialOpacity * p.opacity()); @@ -386,6 +405,13 @@ void Photo::draw(Painter &p, const PaintContext &context) const { _animation->radial.draw(p, rinner, st::msgFileRadialLine, sti->historyFileThumbRadialFg); } } + if (showEnlarge) { + auto hq = PainterHighQualityEnabler(p); + const auto rect = enlargeRect(); + const auto radius = st::historyPageEnlargeRadius; + p.drawRoundedRect(rect, radius, radius); + sti->historyPageEnlarge.paintInCenter(p, rect); + } // date if (!_caption.isEmpty()) { @@ -631,6 +657,18 @@ QSize Photo::photoSize() const { return QSize(_data->width(), _data->height()); } +QRect Photo::enlargeRect() const { + const auto skip = st::historyPageEnlargeSkip; + const auto enlargeInner = st::historyPageEnlargeSize; + const auto enlargeOuter = 2 * skip + enlargeInner; + return { + width() - enlargeOuter + skip, + skip, + enlargeInner, + enlargeInner, + }; +} + TextState Photo::textState(QPoint point, StateRequest request) const { auto result = TextState(_parent); @@ -673,6 +711,11 @@ TextState Photo::textState(QPoint point, StateRequest request) const { : _data->loading() ? _cancell : _savel; + if (_showEnlarge + && result.link == _openl + && enlargeRect().contains(point)) { + result.cursor = CursorState::Enlarge; + } } if (_caption.isEmpty() && _parent->media() == this) { auto fullRight = paintx + paintw; diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.h b/Telegram/SourceFiles/history/view/media/history_view_photo.h index 7213dca21..4b123cc7c 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.h +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.h @@ -163,6 +163,7 @@ private: QPoint photoPosition) const; [[nodiscard]] QSize photoSize() const; + [[nodiscard]] QRect enlargeRect() const; void togglePollingStory(bool enabled) const; @@ -178,6 +179,7 @@ private: mutable uint32 _imageCacheForum : 1 = 0; mutable uint32 _imageCacheBlurred : 1 = 0; mutable uint32 _pollingStory : 1 = 0; + mutable uint32 _showEnlarge : 1 = 0; }; diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index 56299bf45..c622585b9 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -783,7 +783,11 @@ TextState WebPage::textState(QPoint point, StateRequest request) const { auto attachTop = tshift - bubble.top(); if (rtl()) attachLeft = width() - attachLeft - _attach->width(); result = _attach->textState(point - QPoint(attachLeft, attachTop), request); - result.link = replaceAttachLink(result.link); + if (result.cursor == CursorState::Enlarge) { + result.cursor = CursorState::None; + } else { + result.link = replaceAttachLink(result.link); + } } } if (!result.link && outer.contains(point)) { diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index d4f189ac0..7567c501a 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -640,6 +640,12 @@ historyPageButtonLine: 1px; historyPageButtonHeight: 36px; historyPageButtonPadding: margins(13px, 8px, 13px, 8px); +historyPageEnlarge: icon{{ "chat/link_photo_enlarge", historyFileThumbRadialFg }}; +historyPageEnlargeSelected: icon{{ "chat/link_photo_enlarge", historyFileThumbRadialFgSelected }}; +historyPageEnlargeSize: 36px; +historyPageEnlargeSkip: 4px; +historyPageEnlargeRadius: 8px; + historyCommentsButtonHeight: 40px; historyCommentsSkipLeft: 9px; historyCommentsSkipText: 10px; diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index 7bdebaee1..d85da4ec4 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -505,6 +505,10 @@ ChatStyle::ChatStyle(rpl::producer colorIndices) { &MessageImageStyle::historyVideoMessageMute, st::historyVideoMessageMute, st::historyVideoMessageMuteSelected); + make( + &MessageImageStyle::historyPageEnlarge, + st::historyPageEnlarge, + st::historyPageEnlargeSelected); updateDarkValue(); } diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 39f5a76e4..afa63a7ae 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -117,6 +117,7 @@ struct MessageImageStyle { style::icon historyVideoDownload = { Qt::Uninitialized }; style::icon historyVideoCancel = { Qt::Uninitialized }; style::icon historyVideoMessageMute = { Qt::Uninitialized }; + style::icon historyPageEnlarge = { Qt::Uninitialized }; }; struct ReactionPaintInfo { From b403f600e7bbc467ca7bb53a5b7978513773ce09 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Nov 2023 22:32:54 +0400 Subject: [PATCH 36/41] Use correct color for channels in giveaway. --- .../view/media/history_view_giveaway.cpp | 27 ++++++++++--------- .../view/media/history_view_giveaway.h | 5 ++-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp b/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp index 350ba3668..f65613ea3 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp @@ -105,6 +105,7 @@ void Giveaway::fillFromData(not_null giveaway) { st::msgMinWidth), .thumbnail = Dialogs::Stories::MakeUserpicThumbnail(channel), .link = channel->openLink(), + .colorIndex = channel->colorIndex(), }); } const auto channels = int(_channels.size()); @@ -342,18 +343,6 @@ void Giveaway::paintChannels( const auto st = context.st; const auto stm = context.messageStyle(); const auto selected = context.selected(); - const auto colorIndex = parent()->colorIndex(); - const auto cache = context.outbg - ? stm->replyCache[st->colorPatternIndex(colorIndex)].get() - : st->coloredReplyCache(selected, colorIndex).get(); - if (_channelCorners[0].isNull() || _channelBg != cache->bg) { - _channelBg = cache->bg; - _channelCorners = Images::CornersMask(size / 2); - for (auto &image : _channelCorners) { - style::colorizeImage(image, cache->bg, &image); - } - } - p.setPen(cache->icon); const auto padding = st::chatGiveawayChannelPadding; for (const auto &channel : _channels) { const auto &thumbnail = channel.thumbnail; @@ -364,7 +353,19 @@ void Giveaway::paintChannels( }); } - Ui::DrawRoundedRect(p, geometry, _channelBg, _channelCorners); + const auto colorIndex = channel.colorIndex; + const auto cache = context.outbg + ? stm->replyCache[st->colorPatternIndex(colorIndex)].get() + : st->coloredReplyCache(selected, colorIndex).get(); + if (channel.corners[0].isNull() || channel.bg != cache->bg) { + channel.bg = cache->bg; + channel.corners = Images::CornersMask(size / 2); + for (auto &image : channel.corners) { + style::colorizeImage(image, cache->bg, &image); + } + } + p.setPen(cache->icon); + Ui::DrawRoundedRect(p, geometry, channel.bg, channel.corners); if (channel.ripple) { channel.ripple->paint( p, diff --git a/Telegram/SourceFiles/history/view/media/history_view_giveaway.h b/Telegram/SourceFiles/history/view/media/history_view_giveaway.h index 6529ad497..250e214c2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_giveaway.h +++ b/Telegram/SourceFiles/history/view/media/history_view_giveaway.h @@ -69,6 +69,9 @@ private: QRect geometry; ClickHandlerPtr link; mutable std::unique_ptr ripple; + mutable std::array corners; + mutable QColor bg; + uint8 colorIndex = 0; }; void paintBadge(Painter &p, const PaintContext &context) const; @@ -94,10 +97,8 @@ private: Ui::Text::String _winnersTitle; Ui::Text::String _winners; - mutable QColor _channelBg; mutable QColor _badgeFg; mutable QColor _badgeBorder; - mutable std::array _channelCorners; mutable QImage _badge; mutable QImage _badgeCache; From ad059985759ddb04e95816dc7551908ac6bc5506 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Nov 2023 22:40:09 +0400 Subject: [PATCH 37/41] Update reply / forward icons in context menu. --- Telegram/Resources/icons/menu/forward.png | Bin 357 -> 622 bytes Telegram/Resources/icons/menu/forward@2x.png | Bin 603 -> 1104 bytes Telegram/Resources/icons/menu/forward@3x.png | Bin 822 -> 1617 bytes Telegram/Resources/icons/menu/reply.png | Bin 383 -> 599 bytes Telegram/Resources/icons/menu/reply@2x.png | Bin 593 -> 1179 bytes Telegram/Resources/icons/menu/reply@3x.png | Bin 806 -> 1784 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Telegram/Resources/icons/menu/forward.png b/Telegram/Resources/icons/menu/forward.png index abdda604583f1188473422dd5fe0ac009c1cc84e..4a50099f4a03d94c08e06d6028c5b0200d2412b0 100644 GIT binary patch delta 504 zcmV?ViP%-}!y7bH2xjM1OXX zf-jRuB%x5qVzJC-v&Z8hsL!jA%jNd_{o!zEG#Uzpf>%#aFn=5lW3iY{r<+cv{eEAq zR&$yVoGgFD)(rh))A7z~L-VlWt7uh&Q8|GMOxs$$2$K&?U>{MN~N;ZYAqHEuh+{Vep2al z`f|BAoz5rm@_#KBi{tTFqtU!b*aTD)>VR1aBLG&bH5?AZemJRjz5D@bjjP~bCT z^5|($M<)4+wOTDifMTJu(P&JiQq5+w*Xz+VjSXq5I2_e#6*;=!?<7gWh_u^n=yI3KwcT!!h|L14fa6co&zI~OjCMO|rWX>)V)oH=u*O`G=T z&mXu_IK$4)PE}R);>C+_5iAT14UL~ae>yrkVvz$2rKF?)6=nnl1>sTv70=1ZIdbF( zE=9ze0@UBz+q-!2Vyp%wBqZQ*3zjg#Y6?(LPfsr_ENtV(jaU_6G3fvQ|5%k46%}C> n$1Vtra#K@N>~f=`!!85>nd3@q&-S)U00000NkvXXu0mjfFEVAg diff --git a/Telegram/Resources/icons/menu/forward@2x.png b/Telegram/Resources/icons/menu/forward@2x.png index 5f51d642da365bd1586a6637f3bc993a995a464b..c27765246f6109434f70cfc2f6e06dd9800806cd 100644 GIT binary patch delta 990 zcmV<410np|1kebOfPVwJNklP97(p#ep|yetNuh;} zDg0F`7-C@|1RDiEprkQ{QA7(#Wkit#1dXEjM??`LDMAnx5s{$5=zHgRVOTaB&FK^Yin9f&$qBh;`K3+Dc&TcKiJNJdFc^fKI0q>q;K|$;nB8@cDe?uDiSY{rw%1 zhlhvi>S}pni1IQuHHGj;M@P)6xw$zU4nr6Ug&G?(PnlK0ZDy77K4y{?F3V(*FMb#l^+@`+G!<2857BMMZwU zAG3HyMn>cZ0gvFAo}Qll{e23<-WwYmg7Nb5@=i}rNo{s^HZd_#P)&+3At3=L9PGzH zyu7@4y)BEz@jDJe;`tDKx1$_=)Q(P$KHFJxdgn+a22U!Tx?Y#EZV zva$kWlz)P{y1LkkqG#N0H{7nSu0~t_iWV^m&J+FTeMK(HZ__oSnlrbeitI%`8bcgFp-y) zm7!E2)WpO@tbmo3l@%2gAk5U%ROsyO?Kz#!SbxeOMpQjIryxdy!2l|Wr7R$~TCGUW z>+5TJdOB}9EiG+eU;ynHWuCnSi!0fpbfChJro_^5e0&Uhm&?UN)6&wyC>si(1AIO` zJz>mk(70CI;yN=kBjtP`y*QAF(bd((T%#g791aNS0ak#ck|fxmkzsLhF}MAY$ntu< zo`0gUnw61}v9+~DLX;sUr>L<2$=0BCc6K5*+B|F*gv1VD-48Bbw1rvbFc1VJXA3(v zHU>b~*VhP!h2tzY+1c6H@3?*-F(CK&TU+jrDPtuAN(Ph+C>c;PASnZX0CDZx+T)0QXaE2J M07*qoM6N<$f&&HRk^lez delta 485 zcmVD+h2RHh zZ6o*-TInZf=SQRtDFh1}K?v9>q!jTvu$Opa$Y#y;5{ubtcg~revuAfF6vBZHpabXt zI)DxY??5t{?Dcx7R4RBWM8RY-x!>>G?KYRoeTAl0t985GzJE(ox7$Tc46g%yVlW9P z-|ka3o249tqhL;m=lN>2TC3GEnM^Dea}p?#LZKjvBFAy6s@Cgu3X>ITx7*O`pu7BW zTrQVzIP6xHcLQGHr z6h(2wMw zJ06e4V$lY$Ek3LUiW-Iif@ZTBn*bZ@^J?I4lh@Pl_id#Z7N1uGMbqgt5hTkpgN7&N zU(~?%&;W^`Mx*hp<~4)PY5l2pU$JY0@+wNhJDpA>5?(nR4w59TRx29Sfw4A5MsHgl**nwdObZoPfku4Q2&$Pzkd%7 z4wlCUNvupxPO75fI7dfEQBhHnSe06-lamvn8Xq6WoJ>ea`26`ZF@OE~m7bn1wHxB= z2Modx85zkLxPN!=-ltEWh<|x`SyWUczHLc0W@Tj&0Axk7Wocu<+o)1Aif5ee&Ji-BVIhkPoJ(r^#J4 zz~F0QV&duPDG9HyuS1DGF|f7~=F-yA#l=PPk0sOtgP!p4@E<>Z(2(8T-N3*=R=?ad zasb{>e}6waoyb2TTwPtS^$aWcmoHyv$l>AP)2C0b9VLkX_IqkJP4Kd>udlkgIwB%M zL-Q^!E`MulYlQ9W>?}1kRl~93h+zZq?%g}=8^{Tcz;wcj6dfIHZ*Q-grTh2qvmYl+ zrb_Ekv9YoH`}-^;%){>P?xdt7Gcz--)u{}yva%W+9Ax!ZRaL1-iOtB!$T&Yg zXRQto5BvG~iEGEy)bzuL53H4@rX~Xe$?D_6!hb?``fY4%Bqt|}3yHJ=Mq^_mi|zgU z_mXTWdAkH;!n=3xk{=Qgm6eq&HbhorLoqQitbw++He+LBk!=X5%gxQDX@We7GEYEI zd$xmv16E7|*3i&^({TGMl4pLp`ikT(z1(-iz&GH zlKiagstiT{Vy3=lMZk*Sxt*xj* zG(>>ZjOq)!dpL(h99_|^t*wcP2@Qus44dGtt}ZrFy?pud`Sa(RYfvZo;>8O}MAYt7 zGXOO*vdhZK3U7alPkX^*nfs8 z3xh|i7Mf7bllh}t%q8*<1*MR#prD|Ukr9$qLudj_wKeeM$rB;dI`T6!Gs#Pom8x2> z&%#_)?Q&YiuTU#D5aL~*k0TB2SkYIS;vwcP0 zBc4L}+HXTcL!956o0~i*u<)<0u79$qApF}3T0>y+HB6BYsKtj)1`7-yH@caG3RAJH ztW2nc(F7Qqd!bg^7Wbn^kI40kiV6Y-{`U4ZJ=)vb3kwUQr>cZ{U?%JsHHGyTy>2}pu0MYK_~*}`EF{q2Xmv_}>^e3!##2JKdixy$Zw6r|KW1lVLqbA$ z@8sHBA0HoT!jLKu`u~B&*<9HfZ|_fz;qdE0!jpw2q+OyBJlS|;9sa?ig;4nxT^pF002ov JPDHLkV1j-d=oA0| delta 706 zcmV;z0zLiF47LW4fPVs@NklVuJtybeIjYcyV4A^F)@WZS`tJO{>6WlcP%DivPj8X{c^?Jxg!Hstb^o@51 z==O)c(Q)Hl1%2b)0lNL~`xc2rUYC-+zEpCh!{P9Hy;iH$WHQNQ43yCWS2~?`I-Pjy z&1Qr2ce|aW6Mr@f&|^`tSe(sf!jsYG2Dx1BMbpX`*NH7lBoftX)n>DaEns`zZnv`) ze4r+m%PW=2=bH0SIUbKcu=3~>{gu#YH1heppm{o-y4~(@I9x85`~4o-5~ipJKA*4E zY8ea$tS}yrL!r>^cB2-QF3e;y{}OFBn-+^jx{PE+Ab$`z91g-R^ZDFtHj@aDuSFXi zk4It4LZLveAyL606>?Oo)r3@NEa8!SnolaFQi(4ilx?+I>2x}B=g%tmq(Y9u;jqxI zQmORJBl#S+RLCj{BLwq!Jmls@D|n_trt0-N4tBj>6$*u@O~MJzsgT=tyN!cww_8H< zqRaWGLVwmeoemDRUav)+l1%&;sh9_Ixm+K*3v>P@IGIcujYizy#bSXza#?*V;T&u_ zouW533WlDT7^Xsk?|yB;u&G~1qmke5ce~yFe*b(vQ|sIpat?+Jeg7Qy&fYx3`K|b- z`zPhZ!O(pf%L#)g>KvztgQ2Se+Z6^J-80@g7CVcjQYqm-JI3lD2Lyls5C8%|00;m9 oAOHk_01yBIKmZ5;f%ggg1ws+w`maXBq5uE@07*qoM6N<$f(#E!8vpzX zxm*sW)oLY~PqoA0z^j$ZW%{M5=N}G-$K!D}n_(R#==FNZWb#SOXkfu=wQ99mVzC$y z9Y~2pl1`@)_k2E&c`B9acDoEMv(;*~lgXst@8b$4lUj*Pw#8!EY&Hljj&?X4n98rc zTCGM1;b^^HUn~|R!E83~_xtsFJs1qgIcF`EN=v0ur_+IP3=-nz_xlwJ1!Ml-{)z+d X__MnGXP->T00000NkvXXu0mjf55wx} delta 264 zcmV+j0r&pb1pfk%fPVn#Nkl9H@#Dvl#Ubn;KYjqMnKEU{rAwDE zWl;<|bLLE5Uf%ZY+pk@_hN+Z;gTvO=_SC6U(b3Us*RI7>fPW%VQd08m+cy^%7Zgz> zPFh;p+qZA|`1p{-a4-p)_4@T|S65dYx>1E8W;r@Kq6$NKK;Hq~g(ig(MsT^Fo*rP( z8WoK28&$VQFcp8{p#N5*r&^U0uDtzRqM}!xa`5B7c?Z#tK{UWpi_L#M|HBk5CC? zIFF#9pw7-tIs_n@nVGS%vEl3@n@&zn8yg#>24TlG5=rpxjubjIHAOtj%gaciQh0Do z*h*DYR7jyv$JEx=lBVtLZChJgb)z0VUteFsmz0#~;jWiUQBe`{IA~FW^#brt85tQQ zNN^YPD1X6rLqh}6B1Z^1t2snOL=ZA1B}L63uY+T;u&@AToI7i4YY}8dMn)?uD?p-P zB_<|{Knl4A1O(8+RaRCC`KsGPLPA(c&(6+PH_GY_4-ey0&(F`TtgOTl;cJeLj%e+^ zad!s>1`=mhR+czH@J6M?K_z@sQqI0lG|3X=!OoOUv5Y8k2=Hm71E$LwtOE$eNIlz>kY)o0^(p6^LbacJ}i} zlYb!o3hU+N)zQ(xYFT}KJzH@!a1j8T=(EO4MmcM5ZzoMC=B}=;y!EpnvIYkSvt8KL z)x~Q5^z<~ShK7cC5-IJPni|ryzrXM8?ae5@5(Ee2z{$x;LSYwDoSK`PF|xb6%NV3L z3JMBdUS2?lV-+4APDDBc!2wrC+#yL*V}D~KMv9Az(M770u7gLWrKLSQJb>}`_Lep# zt>($e3Gc?Yu?yMBz?Gz@Gga9AX6D=9?uIQCD2Ml&}l;%T*71;;491$y?=Q>9X2 z&sS9y3|W?yn1XwTuGj0uVgaR_&1SFHORSzk@Po|(PgKX_vE6QOw_9v3iymzVivGTW zrFsetZp%_M2vXqh3Lp3TJ#~dIC-(b&$8jV{`f^<=M+Arf5g-CYfCwZK_yaSUTaKlU R?VbPt002ovPDHLkV1kRM-M#<- diff --git a/Telegram/Resources/icons/menu/reply@3x.png b/Telegram/Resources/icons/menu/reply@3x.png index e5825a5a949ea6fd5e9b95fbc5e9e9d0c345e319..04c83d47f9aa6b252f26fd66d91c435b9169fb46 100644 GIT binary patch delta 1676 zcmV;726OqQ2KWt-fPV&JNklroKP3W61e6FU5l|waM1MeufD(aaM!TMZ0Pi)qXkub=_wHSW z(Vu2pTicp7YcwgY=>GNV*Von6{h_j?m64ILq6zRiXJcd2+S=o37MLj z;`*OHeafr5a(_FWoSa_2eod@?|NedQ*_T1bYGRffJU{M^s2yEQA@%i)TtWTFO zUzTVI34i2~BS)@Wxl&zS{pr&u7KCkARaN1jmxw8kn4_cPt5>hciG_uQy|vATB3x7g;8*E z;<>oEXbGS}9P5e;*v!lfR~ytIsK@TBSFh5fm6VjoV<{yig+PD&_<^lo9$alukb->p z@PTs!cRzV14cTpbdpmK%33KSsA#J>pM}HPYA@ZTPxL6)gH#av57|@Z1$HvCW11%W^ z3H0FLAWfQVsg|9c9qs!-$7()(`m|)PJ&}d#fuk3L5)NuI00*GYn&u(29T4J zgY-wDI>b;lz!rxi1e+~#XzaJ|-o1PC<_$7WDIi#B-@kuHCIsQm&Q3YgIwW$B9)CTe zu%Ut1-Pl;__nLx&0#=vWZ{NPfCC$srOO#$&Ss7_*DjA7_@C$$j$PhXjpbEnL(yG0h zN&|$3D=g}hs!A$PxWADw>PRYMvOM4=Bm-zPHa6l;C5kLcjKeH1FW{e{X;`T6A}_`+_=GRxg0lKRU#rHc+pTe z;AF5lo0^*P$}G3@*s)_k=gi0?HKKFp&XJg( zpC5}OXZ!Hs!}M;z9un5wyLW3cpotjCM@B||{P>alMx&yl0w)*=pmFfvL4Oj!gtJ33 zw*vzMk!q1K?A`bj#%n{A7_@MxsBOGBW7w(=u1;jDu&|KVkZhfvo}QEzz$(tDn3$NQ z=u(JrS{E){An(zrt*vF}jV{+sO-)O!$lQZp2lV##k~gT#p}4DNLpm`iCM6{SgaGf| zyN9L70aCdZ^(8eEdU@QiVSj^K2`wuti?yT`F(_hlV!7db=g?|uYB<7QzkX%$_1MNa zgDNkD84c{_w6o#O;fAm#B|3q5W6|#1xq})EZN<2DNW_sr4BsnCOG{aA5a6Jop!4U? zQ)hjBy>v?`36r8)17%j}I5J3LG2oX1YEH0^yjzQaVYX66IQYfI-+%eN2!58srY7VN zQU?JJnuboCI59Ld#DQRG;q3l9tT_Mhef{OjmqZE20NyKFa+){@^rI+%qeUWu)ccr# zJ%J&_755Q*wj3E5!4F&T8AU)?8B!vkL_mpv5&_6o%JFM3fqlXlSX>5K;RB8ihn58l6NZ z@dGq|0nrge6nd2cAsa1)h#=8W_(<42*}Iv@V!1nK+?&n0h1|@YIp=-e`S$ik4>>>% zkOSlZIY17O1LOcXKn{=tfCX-2FHMr@WPG=&KP~bd;i$T%I0djyGAS(yR0djyGAP2|+a)2Bl2gm_(;PnoC Y0_Bi#3AcY@(EtDd07*qoM6N<$f>%&S=Kufz From ef1b197771e1df45df265d5b543b2a64a64ee4a4 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 9 Nov 2023 13:34:24 +0400 Subject: [PATCH 38/41] Update lib_ui & patches --- Telegram/build/prepare/prepare.py | 2 +- Telegram/lib_ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 8cd67af64..a6f0eeef0 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -418,7 +418,7 @@ if customRunCommand: stage('patches', """ git clone https://github.com/desktop-app/patches.git cd patches - git checkout f603f4f986 + git checkout 58c8cd0c0f """) stage('msys64', """ diff --git a/Telegram/lib_ui b/Telegram/lib_ui index d28afffcb..0bee95570 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit d28afffcb9211228d6b5a4424da330637f12ca4a +Subproject commit 0bee95570e7d28afa5cee91b3d1c642051ba1da8 From 639a82ce28250e905ae76c40678e048173536564 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Nov 2023 23:03:17 +0400 Subject: [PATCH 39/41] Add confirmation for prepared giveaway start. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/api/api_statistics.cpp | 5 ++--- .../boxes/peers/replace_boost_box.cpp | 2 +- .../info/boosts/create_giveaway_box.cpp | 22 ++++++++++++++++++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4edc3388b..39c744769 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2116,6 +2116,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_giveaway_users_about" = "Choose if you want to limit the giveaway only to those who joined the channel after the giveaway started or to users from specific countries."; "lng_giveaway_start" = "Start Giveaway"; "lng_giveaway_award" = "Gift Premium"; +"lng_giveaway_start_sure" = "Are you sure you want to start this prepaid giveaway now? This action cannot be undone."; "lng_giveaway_date_title" = "Date when giveaway ends"; "lng_giveaway_date" = "Date and Time"; "lng_giveaway_date_about#one" = "Choose when {count} subscriber of your channel will be randomly selected to receive Telegram Premium."; diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 466094d44..383d0973c 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -518,10 +518,9 @@ rpl::producer Boosts::request() { ? (100. * premiumMemberCount / participantCount) : 0; + const auto slots = data.vmy_boost_slots(); _boostStatus.overview = Data::BoostsOverview{ - .mine = (data.vmy_boost_slots() - ? data.vmy_boost_slots()->v.size() - : 0), + .mine = slots ? int(slots->v.size()) : 0, .level = std::max(data.vlevel().v, 0), .boostCount = std::max( data.vboosts().v, diff --git a/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp b/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp index c629fe948..0129b8596 100644 --- a/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp @@ -413,7 +413,7 @@ Ui::BoostCounters ParseBoostCounters( .boosts = data.vboosts().v, .thisLevelBoosts = data.vcurrent_level_boosts().v, .nextLevelBoosts = data.vnext_level_boosts().value_or_empty(), - .mine = slots ? slots->v.size() : 0, + .mine = slots ? int(slots->v.size()) : 0, }; } diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index 87d8ead06..235e2f130 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_common.h" #include "settings/settings_premium.h" // Settings::ShowPremium #include "ui/boxes/choose_date_time.h" +#include "ui/boxes/confirm_box.h" #include "ui/effects/premium_graphics.h" #include "ui/effects/premium_top_bar.h" #include "ui/layers/generic_box.h" @@ -908,18 +909,37 @@ void CreateGiveawayBox( state->confirmButtonBusy = false; } }; - if (prepaid) { + const auto startPrepaid = [=](Fn close) { + if (!weak) { + close(); + return; + } state->apiOptions.applyPrepaid( invoice, prepaid->id ) | rpl::start_with_error_done([=](const QString &error) { if (const auto window = weakWindow.get()) { window->uiShow()->showToast(error); + close(); done(Payments::CheckoutResult::Cancelled); } }, [=] { + close(); done(Payments::CheckoutResult::Paid); }, box->lifetime()); + }; + if (prepaid) { + const auto cancel = [=](Fn close) { + if (weak) { + state->confirmButtonBusy = false; + } + close(); + }; + show->show(Ui::MakeConfirmBox({ + .text = tr::lng_giveaway_start_sure(tr::now), + .confirmed = startPrepaid, + .cancelled = cancel, + })); } else { Payments::CheckoutProcess::Start(std::move(invoice), done); } From d6bd88d11929ba20b4d254577a08d577fd148618 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Nov 2023 23:05:01 +0400 Subject: [PATCH 40/41] Version 4.11.6. - Support multiple boosts and reassignment. - Improve giveaway creation flow. - Fix crash in topics creation. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 6 ++++++ 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 522be5d7b..87d43e603 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.11.6.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 880e9bb0c..d4c42a1cf 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,11,5,0 - PRODUCTVERSION 4,11,5,0 + FILEVERSION 4,11,6,0 + PRODUCTVERSION 4,11,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "4.11.5.0" + VALUE "FileVersion", "4.11.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.11.5.0" + VALUE "ProductVersion", "4.11.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 00ab6e2f6..0ceaffbf2 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,11,5,0 - PRODUCTVERSION 4,11,5,0 + FILEVERSION 4,11,6,0 + PRODUCTVERSION 4,11,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "4.11.5.0" + VALUE "FileVersion", "4.11.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.11.5.0" + VALUE "ProductVersion", "4.11.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index c8977d179..59219d9c3 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 4011005; -constexpr auto AppVersionStr = "4.11.5"; +constexpr auto AppVersion = 4011006; +constexpr auto AppVersionStr = "4.11.6"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 0ba84356e..447ef2247 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 4011005 +AppVersion 4011006 AppVersionStrMajor 4.11 -AppVersionStrSmall 4.11.5 -AppVersionStr 4.11.5 +AppVersionStrSmall 4.11.6 +AppVersionStr 4.11.6 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 4.11.5 +AppVersionOriginal 4.11.6 diff --git a/changelog.txt b/changelog.txt index c68073a13..245ef9c1e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +4.11.6 (09.11.23) + +- Support multiple boosts and reassignment. +- Improve giveaway creation flow. +- Fix crash in topics creation. + 4.11.5 (06.11.23) - Giveaway phrases and sticker fixes. From 2afc171bec78e9cb22d180f5cc8b978f12a911ce Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 10 Nov 2023 00:32:52 +0400 Subject: [PATCH 41/41] Version 4.11.6: Fix build with GCC. --- Telegram/SourceFiles/boxes/gift_premium_box.cpp | 2 +- .../SourceFiles/info/boosts/info_boosts_inner_widget.cpp | 2 -- Telegram/SourceFiles/ui/boxes/boost_box.cpp | 7 +------ 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index 96196bc11..1244964d2 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -692,7 +692,7 @@ void GiftCodePendingBox( AddTable(box->verticalLayout(), controller, data, true); - const auto footer = box->addRow( + box->addRow( object_ptr( box, tr::lng_gift_link_pending_footer(), diff --git a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp index 84b7cad52..34e0d926f 100644 --- a/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/boosts/info_boosts_inner_widget.cpp @@ -184,8 +184,6 @@ void FillShareLink( label->clicks( ) | rpl::start_with_next(copyLink, label->lifetime()); - const auto copyShareWrap = content->add( - object_ptr(content)); { const auto wrap = content->add( object_ptr( diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.cpp b/Telegram/SourceFiles/ui/boxes/boost_box.cpp index 633cb3e1f..83bd4fa37 100644 --- a/Telegram/SourceFiles/ui/boxes/boost_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/boost_box.cpp @@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { namespace { -[[nodiscrd]] BoostCounters AdjustByReached(BoostCounters data) { +[[nodiscard]] BoostCounters AdjustByReached(BoostCounters data) { const auto exact = (data.boosts == data.thisLevelBoosts); const auto reached = !data.nextLevelBoosts || (exact && data.mine > 0); if (reached) { @@ -465,11 +465,6 @@ void AskBoostBox( box->setWidth(st::boxWideWidth); box->setStyle(st::boostBox); - struct State { - bool submitted = false; - }; - const auto state = box->lifetime().make_state(); - FillBoostLimit( BoxShowFinishes(box), box->verticalLayout(),