From e5320b4b4e25be85a1c993e6242659bc965f0fee Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 14 Jan 2021 17:13:43 +0400 Subject: [PATCH] Implement new permanent invite link management. --- .../icons/info/edit/group_manage_actions.png | Bin 0 -> 430 bytes .../info/edit/group_manage_actions@2x.png | Bin 0 -> 780 bytes .../info/edit/group_manage_actions@3x.png | Bin 0 -> 1479 bytes .../icons/info/edit/group_manage_admins.png | Bin 0 -> 1149 bytes .../info/edit/group_manage_admins@2x.png | Bin 0 -> 2350 bytes .../info/edit/group_manage_admins@3x.png | Bin 0 -> 3434 bytes .../icons/info/edit/group_manage_links.png | Bin 0 -> 933 bytes .../icons/info/edit/group_manage_links@2x.png | Bin 0 -> 1696 bytes .../icons/info/edit/group_manage_links@3x.png | Bin 0 -> 2442 bytes .../icons/info/edit/group_manage_members.png | Bin 0 -> 620 bytes .../info/edit/group_manage_members@2x.png | Bin 0 -> 1255 bytes .../info/edit/group_manage_members@3x.png | Bin 0 -> 2238 bytes .../info/edit/group_manage_permissions.png | Bin 0 -> 980 bytes .../info/edit/group_manage_permissions@2x.png | Bin 0 -> 1862 bytes .../info/edit/group_manage_permissions@3x.png | Bin 0 -> 2750 bytes Telegram/Resources/langs/lang.strings | 1 - Telegram/SourceFiles/api/api_invite_links.cpp | 312 ++++++++++++++---- Telegram/SourceFiles/api/api_invite_links.h | 54 ++- .../SourceFiles/boxes/add_contact_box.cpp | 58 ++-- Telegram/SourceFiles/boxes/add_contact_box.h | 3 + Telegram/SourceFiles/boxes/confirm_box.cpp | 2 +- .../boxes/peers/edit_peer_info_box.cpp | 26 +- .../boxes/peers/edit_peer_type_box.cpp | 143 ++------ .../boxes/peers/edit_peer_type_box.h | 7 +- .../calls/calls_group_settings.cpp | 22 +- Telegram/SourceFiles/data/data_changes.h | 2 +- Telegram/SourceFiles/data/data_channel.cpp | 14 +- Telegram/SourceFiles/data/data_chat.cpp | 12 +- Telegram/SourceFiles/info/info.style | 9 +- 29 files changed, 385 insertions(+), 280 deletions(-) create mode 100644 Telegram/Resources/icons/info/edit/group_manage_actions.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_actions@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_actions@3x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_admins.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_admins@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_admins@3x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_links.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_links@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_links@3x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_members.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_members@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_members@3x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_permissions.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_permissions@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_permissions@3x.png diff --git a/Telegram/Resources/icons/info/edit/group_manage_actions.png b/Telegram/Resources/icons/info/edit/group_manage_actions.png new file mode 100644 index 0000000000000000000000000000000000000000..3c1d15b2d90dc2a6750caaac6ea989a4213e4f54 GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp5{CgJJg7!twx zHY}R2MS-W~?}xlsNB2xnH@cX-K{iQv&7N0TXVgp-oieR22f1eV6q>|MKA(NUx4Z9*y}ShXPw!#t}cP^Zs@`66;doOmN+c$-F$obK~3%U15;KW`TROj;*Le%McW?- z#0|NQTw+O@*_I(=aph#CYw9Hyi{g}12^D&$l?wOhZFzV~!EHtVnaeB^u2x@b>lGNC zE_m*qQlF9joz?c76XTvsQ8QjHJ@)@n_KM|`zWuOg`fMXT#msEO@~P3<2h!ImUYr$B z%d(c;g!h>5%-<7M_B%K)IA-RZaNA72S#R2`2zKdj4<*`tH(JChZFyh1^z{s{m?`tu zJ-)>6$+!1gyUK4xp{Z3VGv7A43%FizTM+tyd)5pS-Xtff5Kvfoy85}Sb4q9e01=j* AzyJUM literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_actions@2x.png b/Telegram/Resources/icons/info/edit/group_manage_actions@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b04ad81f404549bc4d43d370b94361e1263305c0 GIT binary patch literal 780 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^uz(rC1}St4blSkcz*Oex;uuoF z_%_PB?~nnH?{hJan2zQG)4l_Jw%;9+j&psJwKth|X+r#5wN$C-RkQl%-Z)<-B*m6| z#>0R6m&FPA-SyR#cJtq} zr*2u8&wHqUq1ySp?b7ay-c8aFS-wICAsY6X`ww z*%&Igxq{{Tr(E`YJmX=FUH|XD|NbtR<2{49IPqMolB*o!$~k?KtdDP=*6_|)XzI-7 zkmuLVll=In>?7~3`Ww#@HS4AEq%r(5oo(`#r^Cu3zBdw|*af9eZF_ezrmHj(Wvuh7F=_mZpYN z>TKlJ-Efz+(KIle?!EG-^50yB1||cZdta9_-?}h8`OhSV2OoIfpH=&Gz@@FM)pKuI z>dK#ApBLo%*Z%$?#d!bS!dt&GS+2PKD6p7u`)ylq^)+V66WbO%QEZ#?b+Vn0f+hb&Sswe%Fy~zqm$``gV_@AcYhQs^R7=;P`qebx}Qyh zVe8}g^Ndw|D`bhyKlvdPT|BIdTT(k?5lR$P;b+V4Bj@tGw@hL~*TR0kgC(L6udw7gV z*@TJf!0nb5ZU@xvL?pDhIWSht7P!iBqeYs#=Y<_p9peha_4PlGh#mo@L{C>gmvv4F FO#nQhM|A)I literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_actions@3x.png b/Telegram/Resources/icons/info/edit/group_manage_actions@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..004040f69b26e878bfb2250413950568e4f3b187 GIT binary patch literal 1479 zcmb7^do!7OPjA(p5!PuXv1+csCsquS$tF(|ESks7F<7!j!64#)`@z zQKVQK$r)CUW}zC=cx|IZ!YMMU9>;5HQnffcXZve^?2qsL-pA*h``5j>cwa9Km>vuO z01fOpcYmdm_N8_}8E49G*((i{;P2%Ma0U$)l!bc)0UPP#10a;S8UV_P0aW%8rSy~n z00;{NK$H&J@3FxDOi3)rzxh7cV13ya08}lp?k<5Tpk<*4U#+@|y29uV<2kW)*OmCxQSRU{0_I_FR6cl;Q| zOc>l)TH;7L0xX5@$$m$}Ul-wt8j1lL44#Il2M{z1NcVILv>xa_q?R*woCQ|1h8tX> zD#!gaO3E_mzX41XtEFWWiA0vy)EIB9u9BS5=vmdp=;zMcD8}@C@~T)aCvR?UYK>ZN z3x3j#-5In@TQ4gsTU@J>6kVxs&*G{IB*a%0wI*3+v}aW%XB}^Yr*Ep{)2Xg`K4%FG z2E)3tvhu-u%EqKbVi_D9yvG0V;Tvm5$EzVBjp8nzfDjrgCS{Eb)2+#giKq#gET7ZV zRHNL`S<)J(=T$QV)2&=DJjQ3 z`qbX=NP)h-e(sZ|AS$-6c^4&LW-yt1@uFSn=xBtnw|77wzzCr&EOtprNx$<6jj5*i zlwykH?c3PEhQPix;<1PcB(}D^Pvi6ULpFZ#u$^Cu&A|71+nU3OL@lEeYfJD@J@%@t z#XY&^Vfxhxe86FYMsu+ftD{3IY_cULU-KI+NNQcGAsH6!bmWD@z2p_P!9FoeD=8BE zG&{?ZOdKgKEzSR03z<5OPB&1`e>nGYG$_E2Scc96IGBium6a9m%a@sw$w`}{q9U@| z_RKLQMKAc>v@fa)PKk^Znvn4Xmv=N)dH8uxRgzID><2LZLHK$AsQhkScOwwUE`J{g zoDOf`B7pm*r_tK9Tms@T3=?rrWNr^~aB%3KnyTWNi)OnV4;AhvgLEu13@cRaw0L}e z9GlJVT+hmAKGDCh5YsgNv#L4=>h10QzLhPTT@#w2)O~3ThQ4iK+>IL&t{4n85zNdy zyiKK=h2mD?1uDtWIx?r%+uP%tTU$c&Kco{AovU{ks}hOsLJn{VtOFeM1>}Wx{wdY# zXac2-wU@C;ENoThI=JV^Xci6!04Y-v4+;lA-z$xRb7bZ)Wi| zwa1GDI6y9;$*t@$bsNarVV|NUSnhHoC+(C(rAI{#iiLW*N@X)741cbT_87t$JU6~Ti6IpKaa@m7Pmfu zAorkW+7b^00uI5*Dg>bZBoVJo8`REw1QWQL83wAd5?;(SfVsoxp+TTGL|#fGfNcKN cCFuX*dtk`AXL>{(gs}fau^zte99L52UxJaD;Q#;t literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_admins.png b/Telegram/Resources/icons/info/edit/group_manage_admins.png new file mode 100644 index 0000000000000000000000000000000000000000..79c0cb7ca5b5c629df79dc08c88773ac0d1f5f6d GIT binary patch literal 1149 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 U+n+>Zo&W#<07*qoM6N<$f?eQF!~g&Q literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_admins@3x.png b/Telegram/Resources/icons/info/edit/group_manage_admins@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c57d003cb51d8301ad2284d83830fabbba484f26 GIT binary patch literal 3434 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 WGv?IyTc7^TImTDbQI-16QU3*gfkG7k literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_links.png b/Telegram/Resources/icons/info/edit/group_manage_links.png new file mode 100644 index 0000000000000000000000000000000000000000..f865287ded592413137f8cd049bab60732c669a7 GIT binary patch literal 933 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp5{&hP2s7!twx zHq_gDQ=ovHuZFVXq9&y^p5r#-<#|gDnxx{j`<~=Bk8y$LwsjPS*O#KrW?IM zAGUGba_QDE(w%$mO!&upSN|;NWnMq8c-~H%^mB8J)fgN;{+&7Fzs{%d2WE|BY&vVj z7M^~}wEsT;A7d;&cwOR&CH_f6t2iH_8&jo=kMotA#L+ZtNAl#IGo};`R2`Z zn-2H&@%j3^>hEh-k>Gjt%IphYNqKqm?YC@`Pr9@;YVD0$`)NjT z3{@Pg?d|N?cD>&6`t#@FmAU~RGEBIPjg13VhCKNCHMH&g`SYLWf8R z&A)HJt(ct)yd6a3UUz*CzjfgEZ|nLX zhbyc$HZ~0zE(IkeDy^^jOfS1k^>#Y1x%cg%?q}C;-aHvQ(Q3i<*H^Dx-26A{dYO-c zsgO{X&X;Z4vNzwB^&CxFqkO4%@#4i=vP*l9#mr~eddWxaFyo4&LAPULV~z1caQx`nn zx_mkN5!2?whn*L!%6FAgyWl0D9DO(~CFRHd4WLwN$Jp5Pt@sPK`%O?5@pScbS?83{ F1OQverEvfN literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_links@2x.png b/Telegram/Resources/icons/info/edit/group_manage_links@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..671b6419b5bac4d50ce44a4c7c711a8cb1a08773 GIT binary patch literal 1696 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 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_members@2x.png b/Telegram/Resources/icons/info/edit/group_manage_members@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f9c210affcddd19a74097be280caa2a2fb54f61a GIT binary patch literal 1255 zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuAQAtEWRCodHSidhlK@i{fgv75z zbTldvQ4vu{G}IariT^`FtB~jvMB#tXXe1IMDiT2>lsrKpK|({qeD`y)Ywq6NyW6+x z>v^}6Y;JCMW_ISYv%7Ow5JHe_xus%mBmWBA1K=|t=Lq;Mkn;o(J_CFPVLJ@i8rSJq}suSUT@Q-yQ(0rb+M$1?bd3CfEZIm-Yt-2Q)c3NnKrCK?9GzC_(p^ zv%n4jd8kEWqNSxJDi(`nfuRp$QG$(M1i=mfd9sJ%!79fEuL8taG!tM$jq_)Fdpj*I zF4F1gDaqR;XyDO@F)nMt+W{c2w-5kG^)zQ7!wEK`I^*Nxbai#b#X>yBAQRQA;@u7a ztPCgY`7|dW!wELH4g&l9{Ny6BJY*bzP+I^XwJWWB6bZ=MYzOO7`1bZzF5Wi{4-bF8 zBydp~Zewn4PW1HjXwCQccd@XrAiBG|g9aXbiXaOe8w#bLG-UMd`Vc%jI}0uH{QP{G zKj`CxgVEG)CT|unQw%0`eGO(gQ_{|msE;Y{b^yS*%R=Z+0^qU~Mmlq<`7NN*%hksa ziX2XFZ*K$;R7njUeVm}9{HWq-zXjB!$b`7mcj{V`PdnovISN!F$CUx&>a zfPsMlv9huv&d<+-zrXU37bZ=ncAyIzuoXxB{r%$T=t#W2z6Rd`@+&ZSmP#eDxw$E3 zW@d!^&k(1YsjsR#w5h3y*4EbO>}*i3R+uQ(DU`U-g$+*F4w#GEYi@3)#>Pe}6bjVR z(n6!7qqMcP#nl3Sd3iac$8E&$W&p^;Dl{P+@9gX}nJ`usHliS4t{8{Q`qtOiLq0t` zJWxYJLz!HHEi(Y*X&;<CwKdxF5DSVXiE;TU%S{>FJ59lKktGNmtr80{|5nC#0~c zjU;)SA00b8JKQarnwrYwm+|G<*475lG~M6dYu=WL+KSFq$^PBlU1(UA(V_RG7h?cW zgec;?>iex{)R*Xcl3&IE93CEOTb$ROmdwS)g|@(X-I>4~A0O)x1b=dJqTkb)OnXHd z3FPGOXyw8AoTT#<^dP2=$yV2W71$>%-;{sFDJDgOd> RK*|6B002ovPDHLkV1j2=I*kAT literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_members@3x.png b/Telegram/Resources/icons/info/edit/group_manage_members@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..220e567f3b749fce0fbca9f6b1111f2e4e4c41b1 GIT binary patch literal 2238 zcma)8XHe729{q>3P!QUU?4BMTc)HUNN=3<5aW z3_4mPbNr)vkvacY9~oa>@U&w`oVP+DogzUi?l$FiS5Dmc)n+Y)6Br=aA1}#wkjj>m z_484HT!3(zDGfXDFZT~$=T<$z4(u$%pq&L{U_hX_`|;lBS$428AoJZTVJ z^karKyVeosfx5Y^;Xnq5^45zF9XvAOlDX%c3ZMWE3z^!TDGoUxUS4Dhj{%7isG?vD zfw6vde`B6fe+Dw? z#f_M0ZP(V2vqJ?2PZaG6ET3CEE^Wqs7|jsVU>^M53;i~(WEghv8PpHFcI#~ICF5Zia&Rcg8Syy`XT<22}x7PK5|BXN>ny&7De zs_WT5_ze@AveT9kHag@34}l2e))$AMr0TAwRO#5Xb|J&4t@FVuB0@1co5||#EhZ6b zj1oO8gPyPD8}M^^x-oRg0jhm7^?Z-lXj>SIxxKOQ=~pLlsYA(kUiI7eu9&^t&~tF{ z-si`{U=(`MmV`gc>M5YbnGty>P*v} z5^GYZN*MoxRZF?4z%uN&@s|(!1gzkByomMYv8DNTPvDpoJYPkkCID7ZH*NZ?^b$jH zYG8AEZ7@$mZ|y@@^Be22`ILgH!QdGUfPLPylbgM1kP1^I+or@Xn6S;7)Np&@01ZJP z*E_}2J0wY(6XRJ58q@JYtLDu0k3AQ?s03Fo?&BL}a#I0#~*eLCa-?^x;2kv~n$#)=}Q!KZ4=siiiF0yE-%r+Ve z(l?*$iZkAuwoc>`vd)C!qKkiALh|FwKWDLnF z)$WZzLj2y_y*tLJE16qTFTX3kDb-v`;sxTnsdM8Ufcv z2K53*65rw2m07DvBa_ErBkhqJKKEB=`*~`G;J=B+coo_*qoaL2NC|uWG>?s=VuXkN z2qtCC<*)8U-a7@A3IgJ_XGQT(INWux2m3n@YEae{LTwEdQCtW;U>)umdaS8FZN7{< zvFceaAnn*`IQ)Y(nc)WySxhyr5eIa!3{}IF>S8-@9qn2rQT#YFd3kB9?0$uo&S$sr zrcl^yI+cG?QLrUX!;{j`QRWSy+$G{0aWf^tl7>0H-6*g9?Tyxbmb1AG@1BJBNO%Fr0PxP{N=-LKqzF#E!iGg;sv$dBD5M>jN0p#H*`K zKMZ-vlykqG=+md^=!G)%a4r)`)4~KWK|S{N5}06^WXLTFc4L|M-dSH4O&eSWS?SRCc{C_C~`IAARjBX jLvopj88?OgKMf9nBApKMEWOj?=Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$?K1oDDR9Fe^R?jPZQ4~M(12M!T zzmhUCWhVYm6m9i4qOwEG7Kx#&6Hda#;JCYUA%u1<|lpo24h0K(Nk|H$Bkn_Fg zH1GAE_rn|Wt=Y`2_v`%loXgCDnl=Zk>R~10r zmb=|hC0YkVCDDW;UN>Cl2gi!-o0Kg z+b5GrLwie0;B-1+ZEX$u`ugDY^%YX7l!*EJ5Y38>c6N45jSbleFBI#C(P)%sa({o1 zU0q$K_T)p?=;$c>P*ZAYX)*ILUV{4idZcbf1h<_fh?oIwAO(@ zfO`e`&CbrM^b$eEQ=dS4dpq9T+zni*Hrq}%=65WeiXD}Fq&(BY2 zXlQ`O#zxrR--pf3O@aA;hlcIa(vm9O^g&ta?d{dr3MzTYY*_%!l=}PoK~w{XO$H+M zaBy${x3{+%yL<(P1o8#8>Tl^RunP7UyCQ*tXW$<>40Bx&!)eI?0000di literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_permissions@2x.png b/Telegram/Resources/icons/info/edit/group_manage_permissions@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..594f89414024b1001923a5bf5f62aaeed7d78b82 GIT binary patch literal 1862 zcmV-M2f6r(P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuCut`KgRCodHT3bjJK^Puy+0C>v z#ptSNB$ZDRXrx6%MYji|^hLb96%|^9(t|<|MW|8GYe5#I(T$)7#Y%~wumrKIVVD|S zAWbp5*x&yzp5xB$%yD)*>l)__9M1f+|NNJ4W_JGhXG0icfWLq(p#kgpnBzeJf-@k% z5eP1U0G|LXI0J$+Aixm_E`b1_04#i}01y=w#b(Z&$!5)(#lphE*zoYM_8S}=WWRs^ zcGOHd47u{l&d!E&=gvWQcQ?q&_wV1~_U+qHR8#~Ak`s1LzVmC>u7w8=9>{!_wtxEc z2@V`M01*)p{sdz300<8c*G63Gq2hz*&!0nbao30t;ofoapG3Hz^Hxe_WWDqvt>z|{Ze&!4b<{d!?PU-lTD$qhF!+E#Ss z$`zCGZ{ECt%*;$>ne*q*hYJ@jnB;u^{27)lTc#{W;FGfe6c-npjBaXb0$dWdj6Qhq zAdHTVaxx!1dSpx17#Pk0fcrY&n&ymcYik4C>;J`IICkt9CzFUhd-m9s=^Oy%<>j2A zLqkKbbm>yNhI0L`U%$@De*OA2U;xGO2&TK}xpM%JQTkF@St&BW)JEGS?iP@flLPVb z@uuhGSYl!#&`VHXva+&-yb}Q73;?OAse0Q0LtCXtZqA%J@c8j#Z{Kg8i(>#RT)2?40uMYKEh9QRJ57&9 zMn+gqPmigcBE~OYzIfa5jKE@JW4*LW#W4VA)bs@d7CkAgpEz-X8F|af${2wlD!4qj z=D4^xj-H5cEFWNW!U%}Wuy^m?u~n;9vHbje?fh`}?p@a0+$>{fbLYN^uMT zaztM+T-B34>olsTPoMV5Lk}r&g4?8*@6Vqp)4&ejWYnVKOF53V)yRdoEX)ehUH_3uhD&WT?$BRq7B$efomipC>m2GrxZQf`<>FP2Y{wyU0t2YZdwNW_U)4?;M!|zYfb%C8)(!oUc3m47cb^y z+HhLmUmbwUmoJ-ar75~$!v?)TX`O;3;~SjP;}aD;3d8yH=VA5g)l%7ZK~O#nZQs6~ z<>lp}p7DZPAKSWht9GL^-pUvqNpX}IMt<6jFWvf5ox4FLQ|L2o-y>RM#z@#DuiJ{du`IkcmQ zWK3q8@;d+sdA|C1`}Qrnbmo zP5tPyQ(rKT2XsP2C#URBc?RHK&kfe5O`EjjC4Ij`vxENk@88ey;9UT-nxo?cOcJ3q zI(9y3!7WlrHu~u%pPF*ImbOw}ym%qJ&2iaT5zuM?&>Q>4jT=fB(0v}o{8P|blTDVt z5u)#MHgDd{@S2;^O$_Y@o`1T$z+)@JbQPmF{r^xb1MojMWyMBE(&APOaGN&>fZHSB zw*KG@aC-#Y)*qY!ZjXT5`hzpT?GbQWf56Xxzw{I`Z6-h%0{{R307*qoM6N<$f@yzr AO#lD@ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_permissions@3x.png b/Telegram/Resources/icons/info/edit/group_manage_permissions@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..ae6732b326c6dc507205f511ddd28604f7c271b2 GIT binary patch literal 2750 zcmb7GX*kqfAO6qS#uCP^$c#yiy^u0xZDuSzV~=bT*@mbrAtojsvSiI9OAkUkwlRe4 z%9do!5@M`n9eX0nJHEW{=l8?!{C?-0`<(0C=UnH$?kHnJ-BTQ=IRF4SrLTuJWn}x4 zfUq*!nOFW-842WLs;dK(_J}PqI%wzH`Yt#eAj7C30O*+;z#A!wV08-tHciNB|a(y)uC!evxfcEY0Vpe)o7 z=uX(1ySi;)A#_tDi$GiGUCWiwQ`ZV2?aHzkrQKG#>+z(QfBJXlacA0YX0vy7#c1l6 z>z((Nlt;PRMe2bf4MoCKi}~!0IbP+t<$fpcPZM$PcwvFEL(Q1tBm-!Nd#VbFqx5wdSoEOBrGIvJ(t$>#ZR|3|jU?{BsU}3zP()YXi0qsXPr!cq9V|cH|lYPke#zH^But$AbJ=C?% zz2PB1nm%r7Nv_u#XH}G$?;$pj}Tb_~WmhVb}0jCP?>iA5H5^Jeb1NMd# zf|!D}09at?kJ%6>#`x@XOY7$Hh$1#V4_!X+IR31<$PV7k?lj0^!PFZ51FX*K*mJa- z^2STfwI@Th_KXe^(Bis6ZLa*wQ8TgF7hhssnY?4ZL}Irci(+Y4G<*A()e9;msAY6_+`Suq?xvu3o0?x-8I@cp^(`cO?^OX)GtEFf>Zfq zFvHxr^}kJYK+TNr}HlIRuvI;HQwQ|5}K17u8ohkB21n?H6ev3u4$IuFXtc4!Tur` zwBPiV7XTuYvm;-TZ(|w*OzOx4fD|fi@W`2r24LASI@#qmV*NKZAhmw0X=VM7mIk2% zU-do4t4eSgDWcYr6~;^?^Bb?F{OR9|?~RmJowt4e@B`N8W*;|TiWZW)4fD{f$qwK4 zg~BsucvZcEeyvdZ>v@Sb7G?;s4H@$SUd_Pmg6wdYSQKPPI+)?`!__^}<_4`z5)E zV6%L~Sd|NO;r=o}2{FK?#So7d=e4p&$ViiqiBy5WSBT(u{Vg0*tyV`geJ$$ZHb6r1 z)v%%it=>)4`EL>THr13~(%L%~TI+Pp!OwAQJk1AtTNo6^P1d4#YHnu8otP^j$z*6= zb|Kp|&p^9U@hYICth~3svwQ;jns3Boa7n81n^%SJ;s7Ct?-&qx&ef!A!_{ODknTpl z{>*`?|6S(LVk?cM-_!7#LCbcF-HQ@&PCm}@c`mAaem?F@wWn|2`D0KP0ji4)E4B}@ zOi{2)jukB8Qkp|f@j2syLmC}BxZ}87y)&fdqXq@Otfk0VeN3ExSqwXLVu-%}URzby zY6#8$(_rDP7?WPt@&t*J`&QNWC*&+IT^{pFRr7gU8HDje+yx~&bl{tUyQe}GjI&6U z&VqQHb;!ju=g#}F1JKN@>&Ew74U&~aK(|BASFpZII5U}0Gtkn4TXy)iww ziR$R!NLHmiR`r!+a_TOzt9$v?MN80;#iZj5@J%gLIJ+1|?Tl9o{_|@!TrweWVVlaF zld0wtVU(s4g^=y-D%cLBjMR9hbHLT|> z&0bu&|NT|1sZhbRo6z=b2hXyj2sN-USdxn#*&3S4{)E(NAl{*iO2`OLwaD$;PJ~w` zXec)O(Z7qrT^H}j12EB)VII|%euV&4hmU_F#g!#?lODSlYL*wI*$;#8#An^#(*s2 zh1Eg9s~`@SB0RCwrr+_x{=4*vS^5X|#=A)Hpv9##e6~gnFBR{UDmj`L&Tb7Gji8{7 zi;W`Kh_o@4Ji zhg_M<11RyU=OzIXe9An`k&Cvk?WCUvyUw03PVdIH`xeJs@K+J-u|Q6=Y_=iRB-nbq z&_|BWF+;^m3Yc_~2X api) : _api(api) { @@ -28,93 +45,167 @@ InviteLinks::InviteLinks(not_null api) : _api(api) { void InviteLinks::create( not_null peer, + Fn done, TimeId expireDate, int usageLimit) { - if (_createRequests.contains(peer)) { + performCreate(peer, std::move(done), false, expireDate, usageLimit); +} + +void InviteLinks::performCreate( + not_null peer, + Fn done, + bool revokeLegacyPermanent, + TimeId expireDate, + int usageLimit) { + if (const auto i = _createCallbacks.find(peer) + ; i != end(_createCallbacks)) { + if (done) { + i->second.push_back(std::move(done)); + } return; } + auto &callbacks = _createCallbacks[peer]; + if (done) { + callbacks.push_back(std::move(done)); + } using Flag = MTPmessages_ExportChatInvite::Flag; - const auto requestId = _api->request(MTPmessages_ExportChatInvite( + _api->request(MTPmessages_ExportChatInvite( MTP_flags((expireDate ? Flag::f_expire_date : Flag(0)) | (usageLimit ? Flag::f_usage_limit : Flag(0))), peer->input, MTP_int(expireDate), MTP_int(usageLimit) )).done([=](const MTPExportedChatInvite &result) { - _createRequests.erase(peer); - const auto link = (result.type() == mtpc_chatInviteExported) - ? qs(result.c_chatInviteExported().vlink()) - : QString(); - if (!expireDate && !usageLimit) { - editPermanentLink(peer, QString(), link); + const auto callbacks = _createCallbacks.take(peer); + const auto link = prepend(peer, result); + if (callbacks) { + for (const auto &callback : *callbacks) { + callback(link); + } } }).fail([=](const RPCError &error) { - _createRequests.erase(peer); + _createCallbacks.erase(peer); }).send(); - _createRequests.emplace(peer, requestId); +} + +auto InviteLinks::lookupPermanent(not_null peer) -> Link* { + auto i = _firstSlices.find(peer); + return (i != end(_firstSlices)) ? lookupPermanent(i->second) : nullptr; +} + +auto InviteLinks::lookupPermanent(Links &links) -> Link* { + const auto first = links.links.begin(); + return (first != end(links.links) && first->permanent && !first->revoked) + ? &*first + : nullptr; +} + +auto InviteLinks::lookupPermanent(const Links &links) const -> const Link* { + const auto first = links.links.begin(); + return (first != end(links.links) && first->permanent && !first->revoked) + ? &*first + : nullptr; +} + +auto InviteLinks::prepend( + not_null peer, + const MTPExportedChatInvite &invite) -> Link { + const auto link = parse(peer, invite); + auto i = _firstSlices.find(peer); + if (i == end(_firstSlices)) { + i = _firstSlices.emplace(peer).first; + } + auto &links = i->second; + if (link.permanent) { + if (const auto permanent = lookupPermanent(links)) { + permanent->revoked = true; + } + editPermanentLink(peer, link.link); + } + ++links.count; + links.links.insert(begin(links.links), link); + notify(peer); + return link; } void InviteLinks::edit( not_null peer, const QString &link, TimeId expireDate, + int usageLimit, + Fn done) { + performEdit(peer, link, std::move(done), false, expireDate, usageLimit); +} + +void InviteLinks::performEdit( + not_null peer, + const QString &link, + Fn done, + bool revoke, + TimeId expireDate, int usageLimit) { const auto key = EditKey{ peer, link }; - if (_editRequests.contains(key)) { + if (const auto i = _editCallbacks.find(key); i != end(_editCallbacks)) { + if (done) { + i->second.push_back(std::move(done)); + } return; } + auto &callbacks = _editCallbacks[key]; + if (done) { + callbacks.push_back(std::move(done)); + } + + if (const auto permanent = revoke ? lookupPermanent(peer) : nullptr) { + if (permanent->link == link) { + // In case of revoking a permanent link + // we should just create a new one instead. + performCreate(peer, std::move(done), true); + return; + } + } using Flag = MTPmessages_EditExportedChatInvite::Flag; const auto requestId = _api->request(MTPmessages_EditExportedChatInvite( - MTP_flags((expireDate ? Flag::f_expire_date : Flag(0)) - | (usageLimit ? Flag::f_usage_limit : Flag(0))), + MTP_flags((revoke ? Flag::f_revoked : Flag(0)) + | ((!revoke && expireDate) ? Flag::f_expire_date : Flag(0)) + | ((!revoke && usageLimit) ? Flag::f_usage_limit : Flag(0))), peer->input, MTP_string(link), MTP_int(expireDate), MTP_int(usageLimit) )).done([=](const MTPmessages_ExportedChatInvite &result) { - _editRequests.erase(key); + const auto callbacks = _editCallbacks.take(key); + const auto peer = key.peer; result.match([&](const MTPDmessages_exportedChatInvite &data) { _api->session().data().processUsers(data.vusers()); - const auto &invite = data.vinvite(); - const auto link = (invite.type() == mtpc_chatInviteExported) - ? qs(invite.c_chatInviteExported().vlink()) - : QString(); - // #TODO links + const auto link = parse(peer, data.vinvite()); + auto i = _firstSlices.find(peer); + if (i != end(_firstSlices)) { + const auto j = ranges::find( + i->second.links, + key.link, + &Link::link); + if (j != end(i->second.links)) { + *j = link; + notify(peer); + } + } + for (const auto &callback : *callbacks) { + callback(link); + } }); }).fail([=](const RPCError &error) { - _editRequests.erase(key); + _editCallbacks.erase(key); }).send(); - _editRequests.emplace(key, requestId); } -void InviteLinks::revoke(not_null peer, const QString &link) { - const auto key = EditKey{ peer, link }; - if (_editRequests.contains(key)) { - return; - } - - const auto requestId = _api->request(MTPmessages_EditExportedChatInvite( - MTP_flags(MTPmessages_EditExportedChatInvite::Flag::f_revoked), - peer->input, - MTP_string(link), - MTPint(), // expire_date - MTPint() // usage_limit - )).done([=](const MTPmessages_ExportedChatInvite &result) { - _editRequests.erase(key); - result.match([&](const MTPDmessages_exportedChatInvite &data) { - _api->session().data().processUsers(data.vusers()); - const auto &invite = data.vinvite(); - const auto link = (invite.type() == mtpc_chatInviteExported) - ? qs(invite.c_chatInviteExported().vlink()) - : QString(); - editPermanentLink(peer, key.link, link); - }); - }).fail([=](const RPCError &error) { - _editRequests.erase(key); - }).send(); - _editRequests.emplace(key, requestId); +void InviteLinks::revoke( + not_null peer, + const QString &link, + Fn done) { + performEdit(peer, link, std::move(done), true); } void InviteLinks::requestLinks(not_null peer) { @@ -129,16 +220,80 @@ void InviteLinks::requestLinks(not_null peer) { MTP_int(kFirstPage) )).done([=](const MTPmessages_ExportedChatInvites &result) { _firstSliceRequests.remove(peer); - _firstSlices.emplace_or_assign(peer, parseSlice(peer, result)); - peer->session().changes().peerUpdated( - peer, - Data::PeerUpdate::Flag::InviteLink); + auto slice = parseSlice(peer, result); + auto i = _firstSlices.find(peer); + const auto permanent = (i != end(_firstSlices)) + ? lookupPermanent(i->second) + : nullptr; + if (!permanent) { + BringPermanentToFront(slice); + const auto j = _firstSlices.emplace_or_assign( + peer, + std::move(slice)).first; + if (const auto permanent = lookupPermanent(j->second)) { + editPermanentLink(peer, permanent->link); + } + } else { + RemovePermanent(slice); + auto &existing = i->second.links; + existing.erase(begin(existing) + 1, end(existing)); + existing.insert( + end(existing), + begin(slice.links), + end(slice.links)); + i->second.count = std::max(slice.count, int(existing.size())); + } + notify(peer); }).fail([=](const RPCError &error) { _firstSliceRequests.remove(peer); }).send(); _firstSliceRequests.emplace(peer, requestId); } +void InviteLinks::setPermanent( + not_null peer, + const MTPExportedChatInvite &invite) { + auto link = parse(peer, invite); + link.permanent = true; // #TODO links remove hack + //if (!link.permanent) { + // LOG(("API Error: " + // "InviteLinks::setPermanent called with non-permanent link.")); + // return; + //} + auto i = _firstSlices.find(peer); + if (i == end(_firstSlices)) { + i = _firstSlices.emplace(peer).first; + } + auto &links = i->second; + if (const auto permanent = lookupPermanent(links)) { + if (permanent->link == link.link) { + if (permanent->usage != link.usage) { + permanent->usage = link.usage; + notify(peer); + } + return; + } + permanent->revoked = true; + } + links.links.insert(begin(links.links), link); + editPermanentLink(peer, link.link); + notify(peer); +} + +void InviteLinks::clearPermanent(not_null peer) { + if (const auto permanent = lookupPermanent(peer)) { + permanent->revoked = true; + editPermanentLink(peer, QString()); + notify(peer); + } +} + +void InviteLinks::notify(not_null peer) { + peer->session().changes().peerUpdated( + peer, + Data::PeerUpdate::Flag::InviteLinks); +} + auto InviteLinks::links(not_null peer) const -> Links { const auto i = _firstSlices.find(peer); return (i != end(_firstSlices)) ? i->second : Links(); @@ -147,28 +302,42 @@ auto InviteLinks::links(not_null peer) const -> Links { auto InviteLinks::parseSlice( not_null peer, const MTPmessages_ExportedChatInvites &slice) const -> Links { + auto i = _firstSlices.find(peer); + const auto permanent = (i != end(_firstSlices)) + ? lookupPermanent(i->second) + : nullptr; auto result = Links(); slice.match([&](const MTPDmessages_exportedChatInvites &data) { - auto &owner = peer->session().data(); - owner.processUsers(data.vusers()); + peer->session().data().processUsers(data.vusers()); result.count = data.vcount().v; for (const auto &invite : data.vinvites().v) { - invite.match([&](const MTPDchatInviteExported &data) { - result.links.push_back({ - .link = qs(data.vlink()), - .admin = owner.user(data.vadmin_id().v), - .date = data.vdate().v, - .expireDate = data.vexpire_date().value_or_empty(), - .usageLimit = data.vusage_limit().value_or_empty(), - .usage = data.vusage().value_or_empty(), - .revoked = data.is_revoked(), - }); - }); + const auto link = parse(peer, invite); + if (!permanent || link.link != permanent->link) { + result.links.push_back(link); + } } }); return result; } +auto InviteLinks::parse( + not_null peer, + const MTPExportedChatInvite &invite) const -> Link { + return invite.match([&](const MTPDchatInviteExported &data) { + return Link{ + .link = qs(data.vlink()), + .admin = peer->session().data().user(data.vadmin_id().v), + .date = data.vdate().v, + .expireDate = data.vexpire_date().value_or_empty(), + .usageLimit = data.vusage_limit().value_or_empty(), + .usage = data.vusage().value_or_empty(), + .permanent = data.is_permanent(), + .expired = data.is_expired(), + .revoked = data.is_revoked(), + }; + }); +} + void InviteLinks::requestMoreLinks( not_null peer, const QString &last, @@ -180,7 +349,9 @@ void InviteLinks::requestMoreLinks( MTP_string(last), MTP_int(kPerPage) )).done([=](const MTPmessages_ExportedChatInvites &result) { - done(parseSlice(peer, result)); + auto slice = parseSlice(peer, result); + RemovePermanent(slice); + done(std::move(slice)); }).fail([=](const RPCError &error) { done(Links()); }).send(); @@ -188,16 +359,11 @@ void InviteLinks::requestMoreLinks( void InviteLinks::editPermanentLink( not_null peer, - const QString &from, - const QString &to) { + const QString &link) { if (const auto chat = peer->asChat()) { - if (chat->inviteLink() == from) { - chat->setInviteLink(to); - } + chat->setInviteLink(link); } else if (const auto channel = peer->asChannel()) { - if (channel->inviteLink() == from) { - channel->setInviteLink(to); - } + channel->setInviteLink(link); } else { Unexpected("Peer in InviteLinks::editMainLink."); } diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index 7de6341be..f2534f3d3 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -18,6 +18,8 @@ struct InviteLink { TimeId expireDate = 0; int usageLimit = 0; int usage = 0; + bool permanent = false; + bool expired = false; bool revoked = false; }; @@ -35,14 +37,24 @@ public: void create( not_null peer, + Fn done = nullptr, TimeId expireDate = 0, int usageLimit = 0); void edit( not_null peer, const QString &link, TimeId expireDate, - int usageLimit); - void revoke(not_null peer, const QString &link); + int usageLimit, + Fn done = nullptr); + void revoke( + not_null peer, + const QString &link, + Fn done = nullptr); + + void setPermanent( + not_null peer, + const MTPExportedChatInvite &invite); + void clearPermanent(not_null peer); void requestLinks(not_null peer); [[nodiscard]] Links links(not_null peer) const; @@ -64,21 +76,47 @@ private: } }; - void editPermanentLink( - not_null peer, - const QString &from, - const QString &to); [[nodiscard]] Links parseSlice( not_null peer, const MTPmessages_ExportedChatInvites &slice) const; + [[nodiscard]] Link parse( + not_null peer, + const MTPExportedChatInvite &invite) const; + [[nodiscard]] Link *lookupPermanent(not_null peer); + [[nodiscard]] Link *lookupPermanent(Links &links); + [[nodiscard]] const Link *lookupPermanent(const Links &links) const; + Link prepend( + not_null peer, + const MTPExportedChatInvite &invite); + void notify(not_null peer); + + void editPermanentLink( + not_null peer, + const QString &link); + + void performEdit( + not_null peer, + const QString &link, + Fn done, + bool revoke, + TimeId expireDate = 0, + int usageLimit = 0); + void performCreate( + not_null peer, + Fn done, + bool revokeLegacyPermanent, + TimeId expireDate = 0, + int usageLimit = 0); const not_null _api; base::flat_map, Links> _firstSlices; base::flat_map, mtpRequestId> _firstSliceRequests; - base::flat_map, mtpRequestId> _createRequests; - base::flat_map _editRequests; + base::flat_map< + not_null, + std::vector>> _createCallbacks; + base::flat_map>> _editCallbacks; }; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 54e633fdc..ee74e1c52 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -614,7 +614,9 @@ void GroupInfoBox::createGroup( } void GroupInfoBox::submit() { - if (_creationRequestId) return; + if (_creationRequestId || _creatingInviteLink) { + return; + } auto title = TextUtilities::PrepareForSending(_title->getLastText()); auto description = _description @@ -653,6 +655,8 @@ void GroupInfoBox::submit() { } void GroupInfoBox::createChannel(const QString &title, const QString &description) { + Expects(!_creationRequestId); + const auto flags = (_type == Type::Megagroup) ? MTPchannels_CreateChannel::Flag::f_megagroup : MTPchannels_CreateChannel::Flag::f_broadcast; @@ -692,29 +696,9 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio channel, std::move(image)); } + using Flag = MTPmessages_ExportChatInvite::Flag; _createdChannel = channel; - _creationRequestId = _api.request(MTPmessages_ExportChatInvite( - MTP_flags(0), - _createdChannel->input, - MTPint(), // expire_date - MTPint() // usage_limit - )).done([=](const MTPExportedChatInvite &result) { - _creationRequestId = 0; - if (result.type() == mtpc_chatInviteExported) { - auto link = qs(result.c_chatInviteExported().vlink()); - _createdChannel->setInviteLink(link); - } - if (_channelDone) { - const auto callback = _channelDone; - const auto argument = _createdChannel; - closeBox(); - callback(argument); - } else { - Ui::show(Box( - _navigation, - _createdChannel)); - } - }).send(); + checkInviteLink(); }; if (!success) { LOG(("API Error: channel not found in updates (GroupInfoBox::creationDone)")); @@ -733,6 +717,32 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio }).send(); } +void GroupInfoBox::checkInviteLink() { + Expects(_createdChannel != nullptr); + + if (!_createdChannel->inviteLink().isEmpty()) { + channelReady(); + return; + } + _creatingInviteLink = true; + _createdChannel->session().api().inviteLinks().create( + _createdChannel, + crl::guard(this, [=](auto&&) { channelReady(); })); +} + +void GroupInfoBox::channelReady() { + if (_channelDone) { + const auto callback = _channelDone; + const auto argument = _createdChannel; + closeBox(); + callback(argument); + } else { + Ui::show(Box( + _navigation, + _createdChannel)); + } +} + void GroupInfoBox::descriptionResized() { updateMaxHeight(); update(); @@ -834,7 +844,7 @@ void SetupChannelBox::prepare() { _channel->session().changes().peerUpdates( _channel, - Data::PeerUpdate::Flag::InviteLink + Data::PeerUpdate::Flag::InviteLinks ) | rpl::start_with_next([=] { rtlupdate(_invitationLink); }, lifetime()); diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index d28e57450..39bb98717 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -121,6 +121,8 @@ private: void createGroup(not_null selectUsersBox, const QString &title, const std::vector> &users); void submitName(); void submit(); + void checkInviteLink(); + void channelReady(); void descriptionResized(); void updateMaxHeight(); @@ -138,6 +140,7 @@ private: // group / channel creation mtpRequestId _creationRequestId = 0; + bool _creatingInviteLink = false; ChannelData *_createdChannel = nullptr; }; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index a9485ea86..e5b4f766d 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -394,7 +394,7 @@ void MaxInviteBox::prepare() { _channel->session().changes().peerUpdates( _channel, - Data::PeerUpdate::Flag::InviteLink + Data::PeerUpdate::Flag::InviteLinks ) | rpl::start_with_next([=] { rtlupdate(_invitationLink); }, lifetime()); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index edf4465bb..a3ae08f6e 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -331,7 +331,6 @@ private: void showEditLinkedChatBox(); void fillPrivacyTypeButton(); void fillLinkedChatButton(); - void fillInviteLinkButton(); void fillSignaturesButton(); void fillHistoryVisibilityButton(); void fillManageSection(); @@ -640,6 +639,8 @@ void Controller::refreshHistoryVisibility(anim::type animated) { void Controller::showEditPeerTypeBox( std::optional> error) { + Expects(_privacySavedValue.has_value()); + const auto boxCallback = crl::guard(this, [=]( Privacy checked, QString publicLink) { _privacyTypeUpdates.fire(std::move(checked)); @@ -652,7 +653,7 @@ void Controller::showEditPeerTypeBox( _peer, _channelHasLocationOriginalValue, boxCallback, - _privacySavedValue, + *_privacySavedValue, _usernameSavedValue, error), Ui::LayerOption::KeepOther); @@ -798,22 +799,9 @@ void Controller::fillLinkedChatButton() { _linkedChatUpdates.fire_copy(*_linkedChatSavedValue); } -void Controller::fillInviteLinkButton() { - Expects(_controls.buttonsLayout != nullptr); - - const auto buttonCallback = [=] { - Ui::show(Box(_peer), Ui::LayerOption::KeepOther); - }; - - AddButtonWithText( - _controls.buttonsLayout, - tr::lng_profile_invite_link_section(), - rpl::single(QString()), //Empty text. - buttonCallback); -} - void Controller::fillSignaturesButton() { Expects(_controls.buttonsLayout != nullptr); + const auto channel = _peer->asChannel(); if (!channel) return; @@ -964,8 +952,6 @@ void Controller::fillManageSection() { if (canEditUsername) { fillPrivacyTypeButton(); - } else if (canEditInviteLink) { - fillInviteLinkButton(); } if (canViewOrEditLinkedChat) { fillLinkedChatButton(); @@ -1010,7 +996,7 @@ void Controller::fillManageSection() { peer->session().api().inviteLinks().requestLinks(peer); return peer->session().changes().peerUpdates( peer, - Data::PeerUpdate::Flag::InviteLink + Data::PeerUpdate::Flag::InviteLinks ) | rpl::map([=] { return peer->session().api().inviteLinks().links( peer).count; @@ -1018,7 +1004,7 @@ void Controller::fillManageSection() { }) | rpl::flatten_latest( ) | ToPositiveNumberString(), [=] { ShowEditInviteLinks(_navigation, _peer); }, - st::infoIconPermissions); + st::infoIconInviteLinks); } if (canViewAdmins) { AddButtonWithCount( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 5b20229e2..170bb5dc9 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -58,7 +58,7 @@ public: not_null container, not_null peer, bool useLocationPhrases, - std::optional privacySavedValue, + Privacy privacySavedValue, std::optional usernameSavedValue); void createContent(); @@ -66,17 +66,11 @@ public: void setFocusUsername(); rpl::producer getTitle() { - return _isInviteLink - ? tr::lng_profile_invite_link_section() - : _isGroup + return _isGroup ? tr::lng_manage_peer_group_type() : tr::lng_manage_peer_channel_type(); } - bool isInviteLink() { - return _isInviteLink; - } - bool isAllowSave() { return _isAllowSave; } @@ -98,17 +92,14 @@ private: base::unique_qptr usernameResult; const style::FlatLabel *usernameResultStyle = nullptr; - Ui::SlideWrap *createInviteLinkWrap = nullptr; - Ui::SlideWrap *editInviteLinkWrap = nullptr; + Ui::SlideWrap *inviteLinkWrap = nullptr; Ui::FlatLabel *inviteLink = nullptr; }; Controls _controls; - object_ptr createPrivaciesEdit(); object_ptr createUsernameEdit(); - object_ptr createInviteLinkCreate(); - object_ptr createInviteLinkEdit(); + object_ptr createInviteLinkBlock(); void observeInviteLink(); @@ -124,8 +115,7 @@ private: not_null st); bool canEditInviteLink() const; - void refreshEditInviteLink(); - void refreshCreateInviteLink(); + void refreshInviteLinkBlock(); void createInviteLink(); void revokeInviteLink(const QString &link); @@ -143,12 +133,11 @@ private: not_null _peer; MTP::Sender _api; - std::optional _privacySavedValue; + Privacy _privacySavedValue = Privacy(); std::optional _usernameSavedValue; bool _useLocationPhrases = false; bool _isGroup = false; - bool _isInviteLink = false; bool _isAllowSave = false; base::unique_qptr _wrap; @@ -165,7 +154,7 @@ Controller::Controller( not_null container, not_null peer, bool useLocationPhrases, - std::optional privacySavedValue, + Privacy privacySavedValue, std::optional usernameSavedValue) : _peer(peer) , _api(&_peer->session().mtp()) @@ -173,8 +162,6 @@ Controller::Controller( , _usernameSavedValue(usernameSavedValue) , _useLocationPhrases(useLocationPhrases) , _isGroup(_peer->isChat() || _peer->isMegagroup()) -, _isInviteLink(!_privacySavedValue.has_value() - && !_usernameSavedValue.has_value()) , _isAllowSave(!_usernameSavedValue.value_or(QString()).isEmpty()) , _wrap(container) , _checkUsernameTimer([=] { checkUsernameAvailability(); }) { @@ -184,18 +171,11 @@ Controller::Controller( void Controller::createContent() { _controls = Controls(); - if (_isInviteLink) { - _wrap->add(createInviteLinkCreate()); - _wrap->add(createInviteLinkEdit()); - return; - } - fillPrivaciesButtons(_wrap, _privacySavedValue); // Skip. _wrap->add(object_ptr(_wrap)); // - _wrap->add(createInviteLinkCreate()); - _wrap->add(createInviteLinkEdit()); + _wrap->add(createInviteLinkBlock()); _wrap->add(createUsernameEdit()); if (_controls.privacy->value() == Privacy::NoUsername) { @@ -378,16 +358,14 @@ void Controller::privacyChanged(Privacy value) { // Otherwise box will change own Y position. if (value == Privacy::HasUsername) { - refreshCreateInviteLink(); - refreshEditInviteLink(); + refreshInviteLinkBlock(); toggleEditUsername(); _controls.usernameResult = nullptr; checkUsernameAvailability(); } else { toggleEditUsername(); - refreshCreateInviteLink(); - refreshEditInviteLink(); + refreshInviteLinkBlock(); } }; if (value == Privacy::HasUsername) { @@ -562,29 +540,26 @@ void Controller::revokeInviteLink(const QString &link) { bool Controller::canEditInviteLink() const { if (const auto channel = _peer->asChannel()) { - return channel->amCreator() - || (channel->adminRights() & ChatAdminRight::f_invite_users); + return channel->canHaveInviteLink(); } else if (const auto chat = _peer->asChat()) { - return chat->amCreator() - || (chat->adminRights() & ChatAdminRight::f_invite_users); + return chat->canHaveInviteLink(); } return false; } void Controller::observeInviteLink() { - if (!_controls.editInviteLinkWrap) { + if (!_controls.inviteLinkWrap) { return; } _peer->session().changes().peerFlagsValue( _peer, - Data::PeerUpdate::Flag::InviteLink + Data::PeerUpdate::Flag::InviteLinks ) | rpl::start_with_next([=] { - refreshCreateInviteLink(); - refreshEditInviteLink(); - }, _controls.editInviteLinkWrap->lifetime()); + refreshInviteLinkBlock(); + }, _controls.inviteLinkWrap->lifetime()); } -object_ptr Controller::createInviteLinkEdit() { +object_ptr Controller::createInviteLinkBlock() { Expects(_wrap != nullptr); if (!canEditInviteLink()) { @@ -595,18 +570,16 @@ object_ptr Controller::createInviteLinkEdit() { _wrap, object_ptr(_wrap), st::editPeerInvitesMargins); - _controls.editInviteLinkWrap = result.data(); + _controls.inviteLinkWrap = result.data(); const auto container = result->entity(); - if (!_isInviteLink) { - container->add(object_ptr( - container, - tr::lng_profile_invite_link_section(), - st::editPeerSectionLabel)); - container->add(object_ptr( - container, - st::editPeerInviteLinkBoxBottomSkip)); - } + container->add(object_ptr( + container, + tr::lng_profile_invite_link_section(), + st::editPeerSectionLabel)); + container->add(object_ptr( + container, + st::editPeerInviteLinkBoxBottomSkip)); _controls.inviteLink = container->add(object_ptr( container, @@ -634,7 +607,7 @@ object_ptr Controller::createInviteLinkEdit() { return result; } -void Controller::refreshEditInviteLink() { +void Controller::refreshInviteLinkBlock() { const auto link = inviteLinkText(); auto text = TextWithEntities(); if (!link.isEmpty()) { @@ -652,74 +625,26 @@ void Controller::refreshEditInviteLink() { _controls.inviteLink->setMarkedText(text); // Hack to expand FlatLabel width to naturalWidth again. - _controls.editInviteLinkWrap->resizeToWidth(st::boxWideWidth); + _controls.inviteLinkWrap->resizeToWidth(st::boxWideWidth); - _controls.editInviteLinkWrap->toggle( + _controls.inviteLinkWrap->toggle( inviteLinkShown() && !link.isEmpty(), anim::type::instant); } -object_ptr Controller::createInviteLinkCreate() { - Expects(_wrap != nullptr); - - if (!canEditInviteLink()) { - return nullptr; - } - - auto result = object_ptr>( - _wrap, - object_ptr(_wrap), - st::editPeerInvitesMargins); - const auto container = result->entity(); - - if (!_isInviteLink) { - container->add(object_ptr( - container, - tr::lng_profile_invite_link_section(), - st::editPeerSectionLabel)); - container->add(object_ptr( - container, - st::editPeerInviteLinkSkip)); - } - - container->add(object_ptr( - _wrap, - tr::lng_group_invite_create(tr::now), - st::editPeerInviteLinkButton) - )->addClickHandler([this] { - createInviteLink(); - }); - _controls.createInviteLinkWrap = result.data(); - - observeInviteLink(); - - return result; -} - -void Controller::refreshCreateInviteLink() { - _controls.createInviteLinkWrap->toggle( - inviteLinkShown() && inviteLinkText().isEmpty(), - anim::type::instant); -} - bool Controller::inviteLinkShown() { return !_controls.privacy - || (_controls.privacy->value() == Privacy::NoUsername) - || _isInviteLink; + || (_controls.privacy->value() == Privacy::NoUsername); } } // namespace -EditPeerTypeBox::EditPeerTypeBox(QWidget*, not_null peer) -: EditPeerTypeBox(nullptr, peer, false, {}, {}, {}, {}) { -} - EditPeerTypeBox::EditPeerTypeBox( QWidget*, not_null peer, bool useLocationPhrases, std::optional> savedCallback, - std::optional privacySaved, + Privacy privacySaved, std::optional usernameSaved, std::optional> usernameError) : _peer(peer) @@ -760,7 +685,7 @@ void EditPeerTypeBox::prepare() { setTitle(controller->getTitle()); - if (!controller->isInviteLink() && _savedCallback.has_value()) { + if (_savedCallback.has_value()) { addButton(tr::lng_settings_save(), [=] { const auto v = controller->getPrivacy(); if (!controller->isAllowSave() && (v == Privacy::HasUsername)) { @@ -772,13 +697,11 @@ void EditPeerTypeBox::prepare() { local(v, (v == Privacy::HasUsername) ? controller->getUsernameInput() - : QString()); // We dont need username with private type. + : QString()); // We don't need username with private type. closeBox(); }); } - addButton( - controller->isInviteLink() ? tr::lng_close() : tr::lng_cancel(), - [=] { closeBox(); }); + addButton(tr::lng_cancel(), [=] { closeBox(); }); setDimensionsToContent(st::boxWideWidth, content); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h index 7e0e6c620..8aae656e2 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h @@ -32,15 +32,12 @@ enum class UsernameState { class EditPeerTypeBox : public Ui::BoxContent { public: - // Edit just the invite link. - EditPeerTypeBox(QWidget*, not_null peer); - EditPeerTypeBox( QWidget*, not_null peer, bool useLocationPhrases, std::optional> savedCallback, - std::optional privacySaved, + Privacy privacySaved, std::optional usernameSaved, std::optional> usernameError = {}); @@ -53,7 +50,7 @@ private: bool _useLocationPhrases = false; std::optional> _savedCallback; - std::optional _privacySavedValue; + Privacy _privacySavedValue = Privacy(); std::optional _usernameSavedValue; std::optional> _usernameError; diff --git a/Telegram/SourceFiles/calls/calls_group_settings.cpp b/Telegram/SourceFiles/calls/calls_group_settings.cpp index 97d52424d..1ff199880 100644 --- a/Telegram/SourceFiles/calls/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/calls_group_settings.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_group_call.h" +#include "data/data_changes.h" #include "core/application.h" #include "boxes/single_choice_box.h" #include "webrtc/webrtc_audio_input_tester.h" @@ -32,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_calls.h" #include "main/main_session.h" #include "apiwrap.h" +#include "api/api_invite_links.h" #include "styles/style_layers.h" #include "styles/style_calls.h" #include "styles/style_settings.h" @@ -441,23 +443,9 @@ void GroupCallSettingsBox( )->addClickHandler([=] { if (!copyLink() && !state->generatingLink) { state->generatingLink = true; - peer->session().api().request(MTPmessages_ExportChatInvite( - MTP_flags(0), - peer->input, - MTPint(), // expire_date - MTPint() // usage_limit - )).done([=](const MTPExportedChatInvite &result) { - if (result.type() == mtpc_chatInviteExported) { - const auto link = qs( - result.c_chatInviteExported().vlink()); - if (const auto chat = peer->asChat()) { - chat->setInviteLink(link); - } else if (const auto channel = peer->asChannel()) { - channel->setInviteLink(link); - } - copyLink(); - } - }).send(); + peer->session().api().inviteLinks().create( + peer, + crl::guard(layout, [=](auto&&) { copyLink(); })); } }); } diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index 568840d82..cf00ebec3 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -74,7 +74,7 @@ struct PeerUpdate { IsBot = (1 << 19), // For chats and channels - InviteLink = (1 << 20), + InviteLinks = (1 << 20), Members = (1 << 21), Admins = (1 << 22), BannedUsers = (1 << 23), diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index d21af2b41..6bc11d7d5 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "main/main_session.h" #include "api/api_chat_invite.h" +#include "api/api_invite_links.h" #include "apiwrap.h" namespace { @@ -98,10 +99,7 @@ void ChannelData::setAccessHash(uint64 accessHash) { } void ChannelData::setInviteLink(const QString &newInviteLink) { - if (newInviteLink != _inviteLink) { - _inviteLink = newInviteLink; - session().changes().peerUpdated(this, UpdateFlag::InviteLink); - } + _inviteLink = newInviteLink; } bool ChannelData::canHaveInviteLink() const { @@ -778,11 +776,11 @@ void ApplyChannelUpdate( next->v - channel->slowmodeSeconds()); } if (const auto invite = update.vexported_invite()) { - invite->match([&](const MTPDchatInviteExported &data) { - channel->setInviteLink(qs(data.vlink())); - }); + channel->session().api().inviteLinks().setPermanent( + channel, + *invite); } else { - channel->setInviteLink(QString()); + channel->session().api().inviteLinks().clearPermanent(channel); } if (const auto location = update.vlocation()) { channel->setLocation(*location); diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index c35953c72..8f4f29659 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "main/main_session.h" #include "apiwrap.h" +#include "api/api_invite_links.h" namespace { @@ -127,10 +128,7 @@ void ChatData::invalidateParticipants() { } void ChatData::setInviteLink(const QString &newInviteLink) { - if (newInviteLink != _inviteLink) { - _inviteLink = newInviteLink; - session().changes().peerUpdated(this, UpdateFlag::InviteLink); - } + _inviteLink = newInviteLink; } bool ChatData::canHaveInviteLink() const { @@ -389,11 +387,9 @@ void ApplyChatUpdate(not_null chat, const MTPDchatFull &update) { chat->setUserpicPhoto(MTP_photoEmpty(MTP_long(0))); } if (const auto invite = update.vexported_invite()) { - invite->match([&](const MTPDchatInviteExported &data) { - chat->setInviteLink(qs(data.vlink())); - }); + chat->session().api().inviteLinks().setPermanent(chat, *invite); } else { - chat->setInviteLink(QString()); + chat->session().api().inviteLinks().clearPermanent(chat); } if (const auto pinned = update.vpinned_msg_id()) { SetTopPinnedMessageId(chat, pinned->v); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index bcf621494..72601f186 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -348,7 +348,7 @@ infoProfileSeparatorPadding: margins( infoIconFg: menuIconFg; infoIconInformation: icon {{ "info_information", infoIconFg }}; -infoIconMembers: icon {{ "info_members", infoIconFg }}; +infoIconMembers: icon {{ "info/edit/group_manage_members", infoIconFg, point(-2px, 0px) }}; infoIconNotifications: icon {{ "info_notifications", infoIconFg }}; infoIconActions: icon {{ "info_actions", infoIconFg }}; //infoIconFeed: icon {{ "info_feed", infoIconFg }}; @@ -360,10 +360,11 @@ infoIconMediaLink: icon {{ "info_media_link", infoIconFg }}; infoIconMediaGroup: icon {{ "info_common_groups", infoIconFg }}; infoIconMediaVoice: icon {{ "info_media_voice", infoIconFg }}; infoIconMediaRound: icon {{ "info_media_round", infoIconFg }}; -infoIconRecentActions: icon {{ "info_recent_actions", infoIconFg, point(-2px, 0px) }}; -infoIconAdministrators: icon {{ "info_administrators", infoIconFg, point(-2px, -1px) }}; +infoIconRecentActions: icon {{ "info/edit/group_manage_actions", infoIconFg, point(-2px, -1px) }}; +infoIconAdministrators: icon {{ "info/edit/group_manage_admins", infoIconFg, point(-3px, 0px) }}; infoIconBlacklist: icon {{ "info_blacklist", infoIconFg, point(-2px, -2px) }}; -infoIconPermissions: icon {{ "info_permissions", infoIconFg }}; +infoIconPermissions: icon {{ "info/edit/group_manage_permissions", infoIconFg, point(0px, -2px) }}; +infoIconInviteLinks: icon {{ "info/edit/group_manage_links", infoIconFg, point(-2px, 0px) }}; infoInformationIconPosition: point(25px, 12px); infoNotificationsIconPosition: point(20px, 5px); infoSharedMediaIconPosition: point(20px, 24px);