From 258f99205152e08a5e2e647752fc61c5e03bd54b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 5 Sep 2018 13:05:17 +0300 Subject: [PATCH] no message --- .../SettingsCameraIcon@2x.png | Bin 973 -> 652 bytes .../SettingsCameraIcon@3x.png | Bin 1466 -> 719 bytes .../SavedMessagesIcon@2x.png | Bin 661 -> 370 bytes .../SavedMessagesIcon@3x.png | Bin 896 -> 505 bytes .../Apple_Pay_Payment_Mark@2x.png | Bin 2473 -> 1577 bytes .../Apple_Pay_Payment_Mark@3x.png | Bin 3759 -> 2472 bytes .../ic_chatslistpin@2x.png | Bin 1634 -> 562 bytes .../ic_chatslistpin@3x.png | Bin 2479 -> 586 bytes .../StickerKeyboardSettingsIcon@2x.png | Bin 1152 -> 1925 bytes .../StickerKeyboardSettingsIcon@3x.png | Bin 1807 -> 2357 bytes .../Open In/Safari.imageset/Safari@2x.png | Bin 7547 -> 7489 bytes TelegramUI.xcodeproj/project.pbxproj | 27 ++++-- TelegramUI/CallListController.swift | 64 ++++++++----- TelegramUI/CallListControllerNode.swift | 84 +++++++++++++++++- TelegramUI/CallListNodeEntries.swift | 10 +++ TelegramUI/ChatController.swift | 71 ++++++++------- .../ChatItemGalleryFooterContentNode.swift | 6 +- TelegramUI/ChatMediaInputNode.swift | 8 +- .../GalleryThumbnailContainerNode.swift | 30 ++----- TelegramUI/LegacyController.swift | 30 ++++++- TelegramUI/LegacyInstantVideoController.swift | 2 +- TelegramUI/LegacyLocationController.swift | 44 ++++++--- TelegramUI/MediaPlayer.swift | 2 + TelegramUI/OpenChatMessage.swift | 20 ++--- TelegramUI/PeerMediaCollectionEmptyNode.swift | 1 + TelegramUI/ShareActionButtonNode.swift | 2 +- TelegramUI/ShareControllerNode.swift | 52 +++++++---- TelegramUI/SharePeersContainerNode.swift | 4 + TelegramUI/TelegramController.swift | 2 +- TelegramUI/UserInfoController.swift | 5 +- 30 files changed, 325 insertions(+), 139 deletions(-) diff --git a/Images.xcassets/Avatar/EditAvatarIcon.imageset/SettingsCameraIcon@2x.png b/Images.xcassets/Avatar/EditAvatarIcon.imageset/SettingsCameraIcon@2x.png index 370abd3c85592d5215fd1726bafcc3eabb8f850e..a643b384f0aef14d8a759251ddad3c68f088612a 100644 GIT binary patch delta 628 zcmV-)0*n342aE-fBYy%@NklT-SJ0W@bJ);)wfAI5TfH+5h|g;GH{jcK31O3xEQ;owm|n=pm|Y)!2e0n*b=J zjqpT03e#HTO)7A+lbXW_$KiB>ICSv;Mf5)cp*Z&3PliG85@C5p zkJ9;cI4zdL^|zy{>5BS9C`@;}NCIxyZ|F{1+=CrUv1;E23i3Y*xXT>e!8zDWI$zGD z1y0RijrIL#A%8@WYqZ!oSf1(R)qbo0jkNEMdU)X*WPi`qz%|muYv}SDw-`^%||-(SqDFui|?6 zv(EIp7c}Cf9{)pyJH4vG!yTGKl@C-#+X6A&~i;Yj{Zg;(b#e7Rj%G~f{4xLRXpy| z=eTpOhS)(~&Px&heYtbJnk!JdR z<9)+%oHsM?-X}F3_~xE>?>XN&=e~E}yWGiS0wtf%H-L-aC4X21wTf)zBj1L=G{W3T zpb0sfuz_j>@$wn&3i9M=$sV z>P?h$uZqZ`sDG;GpjjPtD2wELcpX1iz`s_+H*y+skX}iY`{fLGWG?0BX$HPq;3Q}R zO%Br4r!m=7lEEl*j0P(iy0KXmlGegrfa?-yijs(HW#eo=(Uy~>h2*qG@^JwZ^iN`Y zCY#O9M?TPM|8xMfxxN|NX>5MGYfWM+`aN(g>T|6gr+@2Czec;o>G6#U36%Tz7w-bP zK9;vZx!G2q#)=<3bln|s1c_!PL|)+B5=|5OdGNCaLfH%$zZR~=m*RNutLq`m$4x;TiMg@m=hCL3FTiKM;5$`dhF&N+@Bgm%u)_^tkOo ze-E+=Ie#+xAo|FZig1o+X?=?jh2Cm?QXJ=$^rYfj$Ws&Ol+k_F)5g(qo9@c0Zy|3C z?IEN4st1ju_jO1&iBx!vM*(^U$+?6s($I^|>+G$Tlr096ro4f4i#BVPXsI$ELx8 z^V~tJ!-%c!I?ZNIYh)b8zBF0?0@t_-`7u9^OnHO6Vg~zx&QR-3lykqUVL5l*8Bn)> aEBpc4QA(A0K}Y=n0000 z=J>q6an1wM@Q~W9AbXC#BmG7KxZXQWIh7+V`G?3a`J7UmUvfF6Ain@~NE5!53tScj zDd+3&>@%rK%1D0zgMG+I)sRs$7$Ii=8O9Fo#>oCryBS$ zJBGqRmMv*as=c!>SmScGf_D}q&3YkqPuzk2!sQBNmYi8XNR1sA+$0T2Q_jydXVaQr zPx=4NJtMI{A%GJy>Uu~1WTXw^*zobKKv~xkhB<*puYVD^xB+Q}BjED}JMOR-t!8cg zi~?{)`ic|4y?}T9RskNADaA0^N$GCbJ#`VZOq7Soxt3 zs$Yv)B{yDjR3i(#Ii&`(W`Tax2Ipl%YI^m~G?+E=8a;#zL$BVE2DAEJqoebuV zU{>2}^nV;O%r^)ZfM^PZPdZWvuvc)#^Af@$KY*t$L0;S&q&X`8R@x#8EG|Z9lBhu2p6!5MuAxZ z>$AiOk7TA@`B&$GyLx!#UowjLWs*i7zW~>m*fG-X1D1Bj4WSsBZAUqHJ4*aUC)?;0 ezlF}W(ESBQ)+8)mo4uR>0000Px)bV)=(RCodHn@?y|R}{uG3eg5Z!J-IN zTy@bxbkS}b97V`VD0F2PDP+@SyDW4S+$iWq&8A`&b|nbyMhK17g`2huD>PtNH7%5u zmbNtb4>kRLGw(gmyEDnWdGDS#6FVP#dH3%*=YI3voqO+_jDL;QgtfJ`X?PXhhfiS@ zsK4yU-gHev@OQ&tcET^9CuD!8|7aXNFn|eo+fyZDTYe@Shf~ZnL7ak*O<+o&>eZVz z3#LyU$%S4qxz{sFD^xn@#~V4O(M;N~=IhKxaaE~n6zQzNc5BxF-O6eH2Ib$vK|B@@ z@cTBQEq9BpN`G0`!B#PdP!dQ&Nw7gdLP;5du+m4DMH%SadLGuBAvsJo<7;Z#0C zUI9IllM+ai`O7TS3gc_SZOK*nej$Bkb!TbDAJn?&8NP49CHNiu5&j9an4EyS;p^}% zcoa^-t*{?7nQy*MJ22iOq%{xqi)D%RFTLts9{i5abMP_)`K@Tw#JZQZAHa{`o`U_h zrQc?1kADq)>uVy-a9cI>F=cPuLLmyC{1X9)pMAeyI6M^VZMs zd-xT+M*qLL=0Ja)kbVi@$yIn{B$hL7Vt;!WJAY++;d%HF7J=HSz2>bPDBBKiL05P` zhI&Lg&c-P_3I8fNGW9!|Q;Q5`I_vNCNS1Wj?&c|$Cg0o~MP85n>9*_2e`Tj(MI)~{ ztjMHa%DJalb9Nv~6Jac&(!rkY;zQrIF7TS*wT?Q-g#0ia4AJY+xsv81Uf;*h=czsX zXn$Gq6D+z8OS|^GY8mf=bAeErl+SxOl3fJKCjG>(7QE5nA(pl`G`|@KE#$oG9r9H&b>4Ak-=5-+S6Mx20IR+20bj)IL*k&Bl+A3LuaoD<2X%Dgf z2qM`ZQfem%s>)>cV$m5yWj7@;=b7GWH=u2kAXtWX@C9@K%*PbgmH7(B#! zHHhk$An%pcagK$Kq@%(^ES;HK+_4$Qw9dUO!Z>VQskDb!dbwbsgS$Eq`U&Hx9Djp{ zSlQ}5iS8O#t*|{I`|@KE#$oG9r9H%&3O2Udf?O&UUGYwrf~}4=)>Fm17i;Mu)>d5W zb(O^&nQY%?D z2Y)RG%r9OT#2Ne~gV~g4@cIx&Z-00CycKWKzT-TLvQ78i&})LXbBNMK-$);(?Q2l` zI_>qeRk{O*;*E4&Vlm#<&xzh+LcXg^3~+bqz2sFGeQVP#K96-)H$N)VfWBdX(SAiE zdx>2x{;2cLvZK*DOs}iz_SSOr1xPQ~Rn2vjN zM`}}i6lx0-ePxc#B3PAJgZ!Cfksybq&9dgK#bJqN$m3Ylouz+2(Kl3`ll_qE(h*+! zr`>#&>oJ_~s2&wq1?HtwLnYi@<6N%8rt%}?6wuGWU;H}?sy$Hwya08}jnLLXdbEU+ zAgw#gDwE7%vy>f&2jC7{|00s7R~g?5)R$hzztH^$(7y00000NkvXXu0mjf DDmm6` diff --git a/Images.xcassets/Avatar/SavedMessagesIcon.imageset/SavedMessagesIcon@2x.png b/Images.xcassets/Avatar/SavedMessagesIcon.imageset/SavedMessagesIcon@2x.png index de460c24ee9184766192385fbd03f4d71e8ae71b..59ea1ee4b407afc8aba432488e96d3e44e4e2baa 100644 GIT binary patch delta 344 zcmV-e0jK_z1@Z!rBYy!oNklyHn?^;mna`#6XH6T#11J}EYtOt$EX?&%?;?r2Yn z1@@G_Dd)AQ5x*?hdhAf~4C=Bc#hb0mx{8Ngm-P%b*kFSVHhvRx2Zd8@H=g)gavw; zI|D^OJBznIIdRpE542a?j){O^((%P1MzvK qFKQ~&#dYk{P<+m4U70VgPx%Pf0{URA>e5nLSG!Q51$VYMRusae^bYUe3vREV{S-;lj$nHjl zanId3GdK(Pz`J+mo{#(Pd(Uu-8$7F9tyatPI0JH^t3I8Dp??C2y&wqon`9t63vNM{ zZ5?t!H$S56E(n_cAe#>Z&^nQ=mo~^c*@lEgAuK)HDV7t$S^B^<;kr7B?ERndgta}j z`n|$d1+{#yu%?jJ?-jNxsO5WwHHEBxudr1?E#E7wDP;9~g{=x|`Ceg7A*Bv=u_lZzK$Y|F`umD!8Bv4j))G~G z?^0pdqIM{2j)7AUWo0=&qX?IoA8t`MRTpdbfdde>NWvweUZ%>(p*S3#gOw2Dd%~aU zlQ)bXfJT;f+xsS|uiu`S4dgFdVb_&~<$ZMKAK1bxt|Iu+-eSudII|BnF zi>HfYNCo5DOGg_II|#5|)K5z@yZuvr$C+DVXJU3URV+W0-L>i^Q|79s?>v`<&Y9d} zxjpGbovDwvmT-X1NfC=pEnL3SE-W}LWF_olaF9jV=Yk=SGy)Opw7o0#z4sJ!?3jMY zVNOTV9#Qv2E|VTDHW5_XwC6y$>%6z>r?~d7Iw?ZXm#nqw|Ndq ikLwOEZU{O0SdxEF0~g=svk|~RWbkzLb6Mw<&;$Sy@EA(~ literal 896 zcmeAS@N?(olHy`uVBq!ia0vp^PCy*U!3HGL#0u^LDaPU;cPEB*=VV?oFfdzrx;TbZ zFuskk%@zq1xjXR>v$V67>l$H?CSQfJjoUl^I|%%5y0|~#h|$uf{SMOJ8^v{9q`5gW z#2sB@&P%@IE^e1Bx4SpHu>4%>=`+>F@6Xw$pTARXzTsq=>g@B+ryFkNk8fW;RZsu- z2Xls+#{H)@y$Kfd5o1$$pmc8jf&A8;qV{&|`>Yk7NEbeHk>Ff!xbM_S&YSEBd;N^` zBr>!Tvs47E426H|Y@YRDVl(G$QLRVES~xi_eD^%E)L_m3vf~$vMP)oW)*N2)FZuDY4un#(7{lrQB>alf#=Nsy-&w0N1W`xThrbpF3 zWbLP*4;(|EoJI53r83(*${Il37YWD5JHt!{`w5dH(y^zcJVYbzjvRv-#mG2YG zLS}jhSWOWJZFJ}_v7xf?D+lR*n$fE>jk&>nOAhU59)j~TlQm3hS+3|vNmfEldPstkGGQ6JH*yX_d2pXt-q%l zraCW)FJ*&r@3r1o*k1e?!CHmvjoTmqLw|zFd z+sa|)%Kso|(j;@M)C;A_r(4g87iZtu(OQw)$r1ZVQ0~sJ#D@!4_MPFZYx%Ku_eC?8 zFYknc1q1rHjXqr$``jCUdzs6I#od=wr%m=VWs|H_bgy}~?}C_MVH?Mv$DUX83KPR7 z<~iJ3DE-vTTW9MxeWe=`8y&l@ZMl**@kdi)0(02yH&+D>Bc{)o(fs*?g7W_a!@$1> zAITTV9&Y}7gmeFmHCqIg=R`Eio-Zg%S}bqzZk-7;YrzpN^|V`@@x8%6qJOYGxm>YZ z^GM98;KRETZ}_k5$o{Y^YU31z+bX_qo;=Wh!+4ynE--u{FiSCby85}Sb4q9e0L^@q AQvd(} diff --git a/Images.xcassets/Bot Payments/ApplePayLogo.imageset/Apple_Pay_Payment_Mark@2x.png b/Images.xcassets/Bot Payments/ApplePayLogo.imageset/Apple_Pay_Payment_Mark@2x.png index 066e8125a18e6f3a07224b74398c94ec37eb2399..a916b243fbbbf5507eb9b6ae0009030e81456970 100644 GIT binary patch delta 1571 zcmXw&c{J2(7{`CEx{0Dzem2S!x(k2xE^3 zDf?Km8`)_?Nd_~v%y56Vy5~ISIiK@=zTfwK|9BtvPWF}u5V|-30FpIGe*pNa=NQq} zT5j)Nu%J*VsQ+P5k&}b|(m~h@f&A7%M(^7PgTX*WUS3`qO#K?|1p%D>a{ib4KMS%v z$WSO4H#9W-ZJ@(J$L|;f^51c5++yuQ9B5(zCW&_92!L`5;y)=Iu)iA_@hCoy$$T6e^DrkTJBo47-Obe+VPg|(&(vmb+siWB?yG# zjT_XZ-Sii3tM({V1ez*{f1r9c?o+Q!16}d&OsQVIejqW@%|r zC=@O%2nq}G@i<&jQPJw^>a%B0i3I$mOBcD^o(tza4D=86_V#8xPk;Zum&@h)`}?8I z&GdA25)zoy>p}bA@Q~ZL3i9*f<6>Q0oD~%m2zY!`Q)3sstMlp8l=B{xe@@!@czdm_ ztrZp)oOX1e(}Pt|$oBTO$;pY1j`rcwDJX$`*y zY|y>;2k5KQiwP3yAJk+V7baUmzp@BiMlwHD1qd9|@5CB z*&ls2vHJ7Eb+>U}F>+r7eZqw$?RYay_{nuNT8h$SZLXy=QO%j^-`aUK2p>FGs>OvOqz zN1R1vkfcWEwoTLo<_a6!N2^)BTu*&^?URD$VPdj@wwI6yqz$@rlvgh35Z31n=jh>Y z8w4^~>#YxT3Kl5S?m%Q8M-GscOjqtw{cs_tt*Wus#7XL^v6cnXz!iI=Zm!kV_qB8>*BtL7>Bp-I^<^gf6BSBSs13eG10*+)`B zP+jF4;#yc<(l(^s6P1e9DcuiZ=w!?#%Q?hxQ!SFu%d*sm`uez5mPW>Fyj{Po+{Sci zrH*p6B|cPITv^)oJI2$}QrAbcor#DLh$Gij? z3EZx3UFo5}PY0_2VyQTfhgiE#Ylnj!Rwp7EhGM8a*fw&SeWdvUc{x5e@$Q)OqZtB3 zw_-(+eXC0-nbls6`mTJAuw=s#Y}zt4?M-;#tfwL|rZBnsiYX8$Zj@)aynC~{J?Rr2PKkMO@p(~FEB<<17Hz_+BOV`xcBcS1Miq-xE#=LjKz1Bf zZ#%U_yZLdKO|dkZ!>i|3CeE(2dw#4tXAF`Sd5A?`nL-jR5MjfIUM_zoTgMe{sw?{# zE$`N!=eI7#kZqph87rEM^(I*7pADF;2{&ovZ-di>o0)=dP>+-$S`EKIEExIxivmFx Q1?~udvnFHmEuC-w3kxvj00000 literal 2473 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 A+yDRo literal 1634 zcmV-o2A%ndP)Px*97#k$R9FekSzTycMHId>yW5b^2P0T<10waI5%N+Sd`J}y8iH8V{Ol&(U9tEW zT0~PtQEa0|p%_7}K8k^El5W~8g-RbP1k|bs5y8Cl!3V9bq@wMElA7Jz@%!%FN$%d= zy?ZxJLI*Z;@67r6?m1^>&Y1{ngKcIef3Iy9pHj-&hhJO>6}K#bADb#DB?>}V{4Gcs zJ2yPs_p_dB?5}8qd~R-T*Yfh}VWsRo@O3o83))2?WWKpMa8k1MJcUcg@_cSEESrXngl-LE~QrEwr$-F>-Gp?KY_kg^ND}J z{a!4lCX&e{o`YLmfZ6P9zh&8rOm~nH3?t|Fs_1aG@VXv6%HN zT)ym=DE$MpmH~&ayAI1ezS-cYlQJorI%o)2XNAEk0Me~wAY(Kd?MkJR7yTcxS?`oh z9R{QhsdDLMi1mU{XFy`}>$Vb{7sk|7t_LxB0b?+j81ycjH4Abp(W_Mv^yqehBT!e@ zD8q{wG#1V{i1h>aIc_Cft(z5d@W?P0!_`x#W?$7>YX5a(@)9xVJy+8@7UT)RO_)R6 zN@|MT$2@R!21^*MiH-#Ul^|7mNJO?sEhI7wy1tI0c!_ z29#~9w*m4M^Iomjx<#H?c%+iXu^`O)G+%f^u)o;`7mY*7UcVfGd>0^J@{fDp4F>nT zwaDojzj%-D0Cm_bQH6tkZNRn?VZvbJ$o6u^z`d77?e5k&{^# zGvRuS{`oK<@e%*+*kSDj?e~8ETJHu6vC+kUkdSG5Ery_RO|BjbW-FiNxr!JW(4%s)t!>wS z+P*c^+o;z#z-q3c#_zLo$i3RHVV*0GmyLohfnFyQO})#y{Hp@v;c zIW7jcS90;`bc@#!h#ge`;j1U)p)~Gj=z3I)AWLl-KBg(IU z;Q&-$yaEl!B$OC2Zpw$H>u&GMKGl0DIsJ@1qK43%G|p;(y8`S|xS@r!c@jS}nK- z1Hwit#D0VU;iK!0&2R;)D!R_t2KHfqL$r8s8wOMtU03Wo6fh@eXh{aMCXQgv@=Al* z8>}G5A%5Wkcld){Y+?%*z;PwPYzAEvuo_(l4Q6lg6}K<}+rffahCWQh081f*H((;} zu@>~R#6H6WoPS~=_>Tu`;0LTmJxnd{A- z^Ss1Uj^0Ak`L6uXxxjZeTF%U@FRIOKc1WFx+5WE5tvz1H%Dkqkfpi7BN5vV^P1v zW0UxaMJvq!|KJapOm8ivrQj4CyA&Le6r6(p0ev{vH&AyhhX4Qo07*qoM6N<$g54JQ Az5oCK delta 2470 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 literal 1152 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bYNg$6c6wTaRqWGO4S?&F^=Fgws zHgR@q|IEHst3ZbI&Hn%YKadMDvv20ARjZ~>UDe({y920wB3KY;3&>cYAv4=1fY=>< zK&$5fEdq*kOax0!nB5O{4A8)iNpr!*&IH;8WPo&m5ztH^w|&Cwwh2HJ=70o&=1iE? zK5-7%g)`eHfVF@OpVbO9b|O#&Wzr%J0U0EpMCYv^52?V7GD-HFfi@* zba4!+V0=5Rb<$%ufwtRc?jO>5KWo~|nUZHD>i$3Xu+d6Q^_;pqv1i^KSZDTa6T?m0+8eE&hMW1>7g@4c zh@54V`CGW=i@UDu;ZD2x^XAT& ze$A)367Iz{J6X3sdL;Hvum56Cb@p4iO@BWa6B*4PS&CC8ulu__XWqw_kE4JJS^&z51&$GxFu* z#VyPJDAt}y>tOpeUF)1!{AjjgLXnrlJ@i}(7qFAi*NI{?=1Yj` zce=99$^0!EwQWD^`db#vA%=?o*5n=Bx$WM9%xdW}|M`cnt?KyrC^zHAJ+(bM_00B_ z-ThXdUibDQXZnG-1=j?9x3j(9$5^~H>+|=JsSDN~zMJWMbGBCT#4WRUohQyX{kb~7 zQc8Mp&$1`iC7w;Isjt0VSNk#Zq++asxdS7rZ& g_8X1rAK!l-USFCWY$5Cu4a_eLp00i_>zopr0Pw&&*#H0l diff --git a/Images.xcassets/Chat/Input/Media/SettingsIcon.imageset/StickerKeyboardSettingsIcon@3x.png b/Images.xcassets/Chat/Input/Media/SettingsIcon.imageset/StickerKeyboardSettingsIcon@3x.png index 54b04299e54072494572f46b87b62c5f938c1e07..e081b297d4d8b585a3a1fb5ddfe31047db5a0b58 100644 GIT binary patch literal 2357 zcmaJ@dpJ~iA0I`{q(L*PC9N?iVyVw=L9fA4J+SxLuVMCc2cWm!(s!nBET?3ESMlr0T7x<5d)w&GJzNf1q7nRjctGr z1Tx)4!jAwWxGSkbg$ye&`e3y(r2!3r`1om+0-+QDQ89p6BKJiPUAlrsNkqQr)nqQ7 zt7HIil7J)?5SA3g7bZ!CULv&LGL(;&Y9NpSpa7+n#mm)HtuOi$FV(O&nsI26Q+}b9t3S8N&@h}o z@_ojNzMc#5t5}>N8Juyv|26I@lR-nq?HBPH4qxOC$PJoT8N}`Rm;ZAJ#H@;@`;HEyXm&CtWBg1yOQ~~_r-EE2Dp|HTqbHAlk_i`-*3~=;ys#Q{#=?? zYfZgql8d0v|HJ#()ki7lJ7)`L(ua^vYo{@Hzwa_JYhpEB)U|)}YW(rECo4EQSuWW9 z)AWkF2t;67?1y!3<@R{92p$R#Gq2X$YdqrhP8Y;fOWh{ODra`Q(-!$G{ez;xk^F{h z6-W!Hel9X`*5DFK3<2vFJU(fvevf*s-}V$~!-~GBeeZ5VDtTQ6z%xaiV8sKfG>w zL%FcuwWfHoEbZgBN8HDRPhFOi)6Z!tPgJgXywjp$glyW;(1Jl!1)UiPy}@WY591&g zbfO)?%|@`WD2|rsXVpEzWd{%Bte|I+ZM%3{@j-{HH7z!m!ziRdyZf5J=Sx{(vViOR zQ|aBC&4)LzpRE6-k-TV4G7rB0q7$s^#Kt2z4YS8tyKx6ovR zek@NebE3AQ+XsS5LiX_O#9d8|Yu+~goOq_XV$w1?c*db3r<96^_g6~xWIqOfd-)W} z{!nOAA-h(+<_GeB;+^x#jyFT!=u#GsrW`b>DPXRTlv#BJ_jyRU%?m}p=<5309=s~K z^O(8pho>_t!qPKi;WnxqE%7D9+pd7>)_=1QPG_ySY;QyAn?@V>H6OMxy4QI_mF^s} zBD9LesIPs*^v<$FbOq=PF9{3-wXGL4myB&o_@<2Y8VndHEw?nAOApr%1wm`0&Wl=$ z(_bm3-STo8-G`oeO?ZrwJKkPN^Fs{X73M=R{p~XfGJQ@q_W>FFxoc0dGPEw|3VYm- z75^Nbnw(c1{9-Np_9Qc8`b(#o;TaJ-DUj4Pqygx&CGS&Brk6oKMEg%(8PBgN`jygp) zsCx=KttQIC0&^J2@cof?d3jYA1qC~{Cf7wxf8T+QyhfF_mE1Mm``f~aqO&W7bI#oF zYb|Z*Yqd}0vld@I|S zy<%;$IFd4!mwL1z%id$uLqG5BF*}dZdCwNE!encA9QKRtCl?j8m_FA9Mt!RTq?yFl z55+r9#KE&*6MlUw-0c!1{Ht4!X9C;aC5leDgd8--;i&f}+~B@FyjPeSKjh8YFkMpO zVAA;E@9nNvXxti+Nvr>eMDasntxvuqzws^V{^%cWokH(8?t_emko*UjC3Vka#vd6D LD~MT3k52y^_I0}N literal 1807 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 diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index 840aaf8996..9ba11c024b 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -7,7 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 09310D2B213ECC840020033A /* Lottie.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09310D2A213ECC830020033A /* Lottie.framework */; }; + 091EB4EB213F48B4005284DE /* Vision.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 094981C52138D73B00A10660 /* Vision.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 091EB4F0213F4C4C005284DE /* Lottie.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091EB4EF213F4C4C005284DE /* Lottie.framework */; }; 09310D2C213ED5FB0020033A /* anim_read.json in Resources */ = {isa = PBXBuildFile; fileRef = 09310D14213BC5DE0020033A /* anim_read.json */; }; 09310D2D213ED5FB0020033A /* anim_pin.json in Resources */ = {isa = PBXBuildFile; fileRef = 09310D15213BC5DE0020033A /* anim_pin.json */; }; 09310D2E213ED5FB0020033A /* anim_unmute.json in Resources */ = {isa = PBXBuildFile; fileRef = 09310D16213BC5DE0020033A /* anim_unmute.json */; }; @@ -20,7 +21,6 @@ 0941A9A0210B057200EBE194 /* OpenInActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */; }; 0941A9A4210B0E2E00EBE194 /* OpenInAppIconResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */; }; 0941A9A6210B822D00EBE194 /* OpenInOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A5210B822D00EBE194 /* OpenInOptions.swift */; }; - 094981C62138D73B00A10660 /* Vision.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 094981C52138D73B00A10660 /* Vision.framework */; }; 09797873210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09797872210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift */; }; 0979787C210642CB0077D77F /* WebEmbedPlayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787B210642CB0077D77F /* WebEmbedPlayerNode.swift */; }; 0979787E210646C00077D77F /* YoutubeEmbedImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787D210646C00077D77F /* YoutubeEmbedImplementation.swift */; }; @@ -108,7 +108,6 @@ D02D60B1206C189900FEFE1E /* SecureIdPlaintextFormController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02D60B0206C189900FEFE1E /* SecureIdPlaintextFormController.swift */; }; D02D60B3206C18A600FEFE1E /* SecureIdPlaintextFormControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02D60B2206C18A600FEFE1E /* SecureIdPlaintextFormControllerNode.swift */; }; D02D60C8206E705D00FEFE1E /* SecureIdValueFormPhoneItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02D60C7206E705D00FEFE1E /* SecureIdValueFormPhoneItem.swift */; }; - D02DADBF2138D76F00116225 /* Vision.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D02DADBE2138D76F00116225 /* Vision.framework */; }; D02F4AE91FCF370B004DFBAE /* ChatMessageInteractiveMediaBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02F4AE81FCF370B004DFBAE /* ChatMessageInteractiveMediaBadge.swift */; }; D02F4AF01FD4C46D004DFBAE /* SystemVideoContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02F4AEF1FD4C46D004DFBAE /* SystemVideoContent.swift */; }; D0380DA9204E9C81000414AB /* SecretMediaPreviewFooterContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0380DA8204E9C81000414AB /* SecretMediaPreviewFooterContentNode.swift */; }; @@ -1017,6 +1016,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 091EB4E9213F475A005284DE /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 091EB4EC213F48BE005284DE /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 091EB4EF213F4C4C005284DE /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 09310D14213BC5DE0020033A /* anim_read.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_read.json; sourceTree = ""; }; 09310D15213BC5DE0020033A /* anim_pin.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_pin.json; sourceTree = ""; }; 09310D16213BC5DE0020033A /* anim_unmute.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_unmute.json; sourceTree = ""; }; @@ -1026,9 +1028,6 @@ 09310D1A213BC5DE0020033A /* anim_ungroup.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_ungroup.json; sourceTree = ""; }; 09310D1B213BC5DE0020033A /* anim_group.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_group.json; sourceTree = ""; }; 09310D1C213BC5DE0020033A /* anim_mute.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_mute.json; sourceTree = ""; }; - 09310D26213BD84E0020033A /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 09310D28213BD8810020033A /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 09310D2A213ECC830020033A /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInActionSheetController.swift; sourceTree = ""; }; 0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInAppIconResources.swift; sourceTree = ""; }; 0941A9A5210B822D00EBE194 /* OpenInOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInOptions.swift; sourceTree = ""; }; @@ -2120,10 +2119,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D02DADBF2138D76F00116225 /* Vision.framework in Frameworks */, + 091EB4F0213F4C4C005284DE /* Lottie.framework in Frameworks */, + 091EB4EB213F48B4005284DE /* Vision.framework in Frameworks */, D00ACA4B20222C280045D427 /* libtgvoip.framework in Frameworks */, D07BCBFE1F2B792300ED97AA /* LegacyComponents.framework in Frameworks */, - 094981C62138D73B00A10660 /* Vision.framework in Frameworks */, D053B4371F1A9CA000E2D58A /* WebKit.framework in Frameworks */, D09E63B21F11289A003444CD /* PassKit.framework in Frameworks */, D09E63B01F1010FE003444CD /* Contacts.framework in Frameworks */, @@ -2158,6 +2157,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 091EB4E8213F3D86005284DE /* Recovered References */ = { + isa = PBXGroup; + children = ( + 094981C52138D73B00A10660 /* Vision.framework */, + ); + name = "Recovered References"; + sourceTree = ""; + }; 09310D13213BC5DE0020033A /* Animations */ = { isa = PBXGroup; children = ( @@ -3003,6 +3010,9 @@ D08D45281D5E340200A7428A /* Frameworks */ = { isa = PBXGroup; children = ( + 091EB4EF213F4C4C005284DE /* Lottie.framework */, + 091EB4EC213F48BE005284DE /* Lottie.framework */, + 091EB4E9213F475A005284DE /* Lottie.framework */, D02DADBE2138D76F00116225 /* Vision.framework */, D00ACA4C20222C280045D427 /* libtgvoip.framework */, D057C5412004215B00990762 /* Lottie.framework */, @@ -4367,6 +4377,7 @@ D0FC408C1D5B8E7500261D9D /* TelegramUITests */, D0FC40801D5B8E7400261D9D /* Products */, D08D45281D5E340200A7428A /* Frameworks */, + 091EB4E8213F3D86005284DE /* Recovered References */, ); sourceTree = ""; }; diff --git a/TelegramUI/CallListController.swift b/TelegramUI/CallListController.swift index a10dc1e398..2204ae128e 100644 --- a/TelegramUI/CallListController.swift +++ b/TelegramUI/CallListController.swift @@ -26,6 +26,8 @@ public final class CallListController: ViewController { private var presentationData: PresentationData private var presentationDataDisposable: Disposable? + private let peerViewDisposable = MetaDisposable() + private let segmentedTitleView: ItemListControllerSegmentedTitleView private var isEmpty: Bool? @@ -86,6 +88,7 @@ public final class CallListController: ViewController { deinit { self.createActionDisposable.dispose() self.presentationDataDisposable?.dispose() + self.peerViewDisposable.dispose() } private func updateThemeAndStrings() { @@ -239,30 +242,45 @@ public final class CallListController: ViewController { } private func call(_ peerId: PeerId, began: (() -> Void)? = nil) { - let callResult = self.account.telegramApplicationContext.callManager?.requestCall(peerId: peerId, endCurrentIfAny: false) - if let callResult = callResult { - if case let .alreadyInProgress(currentPeerId) = callResult { - if currentPeerId == peerId { - began?() - self.account.telegramApplicationContext.navigateToCurrentCall?() - } else { - let presentationData = self.presentationData - let _ = (self.account.postbox.transaction { transaction -> (Peer?, Peer?) in - return (transaction.getPeer(peerId), transaction.getPeer(currentPeerId)) - } |> deliverOnMainQueue).start(next: { [weak self] peer, current in - if let strongSelf = self, let peer = peer, let current = current { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { - if let strongSelf = self { - let _ = strongSelf.account.telegramApplicationContext.callManager?.requestCall(peerId: peerId, endCurrentIfAny: true) - began?() - } - })]), in: .window(.root)) - } - }) + self.peerViewDisposable.set((self.account.viewTracker.peerView(peerId) |> take(1) |> deliverOnMainQueue).start(next: { [weak self] view in + if let strongSelf = self { + guard let peer = peerViewMainPeer(view) else { + return + } + + if let cachedUserData = view.cachedData as? CachedUserData, cachedUserData.callsPrivate { + let presentationData = strongSelf.account.telegramApplicationContext.currentPresentationData.with { $0 } + + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } + + let callResult = strongSelf.account.telegramApplicationContext.callManager?.requestCall(peerId: peerId, endCurrentIfAny: false) + if let callResult = callResult { + if case let .alreadyInProgress(currentPeerId) = callResult { + if currentPeerId == peerId { + began?() + strongSelf.account.telegramApplicationContext.navigateToCurrentCall?() + } else { + let presentationData = strongSelf.presentationData + let _ = (strongSelf.account.postbox.transaction { transaction -> (Peer?, Peer?) in + return (transaction.getPeer(peerId), transaction.getPeer(currentPeerId)) + } |> deliverOnMainQueue).start(next: { [weak self] peer, current in + if let strongSelf = self, let peer = peer, let current = current { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { + if let strongSelf = self { + let _ = strongSelf.account.telegramApplicationContext.callManager?.requestCall(peerId: peerId, endCurrentIfAny: true) + began?() + } + })]), in: .window(.root)) + } + }) + } + } else { + began?() + } } - } else { - began?() } - } + })) } } diff --git a/TelegramUI/CallListControllerNode.swift b/TelegramUI/CallListControllerNode.swift index 637c2c3835..5b2141fa47 100644 --- a/TelegramUI/CallListControllerNode.swift +++ b/TelegramUI/CallListControllerNode.swift @@ -177,11 +177,15 @@ final class CallListControllerNode: ASDisplayNode { private let callListDisposable = MetaDisposable() private let listNode: ListView + private let emptyTextNode: ASTextNode private let call: (PeerId) -> Void private let openInfo: (PeerId) -> Void private let emptyStateUpdated: (Bool) -> Void + private let emptyStatePromise = Promise() + private let emptyStateDisposable = MetaDisposable() + init(account: Account, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (PeerId) -> Void, openInfo: @escaping (PeerId) -> Void, emptyStateUpdated: @escaping (Bool) -> Void) { self.account = account self.mode = mode @@ -195,6 +199,11 @@ final class CallListControllerNode: ASDisplayNode { self.listNode = ListView() + self.emptyTextNode = ASTextNode() + self.emptyTextNode.alpha = 0.0 + self.emptyTextNode.isLayerBacked = true + self.emptyTextNode.displaysAsynchronously = false + super.init() self.setViewBlock({ @@ -202,6 +211,7 @@ final class CallListControllerNode: ASDisplayNode { }) self.addSubnode(self.listNode) + self.addSubnode(self.emptyTextNode) switch self.mode { case .tab: @@ -339,10 +349,22 @@ final class CallListControllerNode: ASDisplayNode { self.callListDisposable.set(appliedTransition.start()) self.callListLocationAndType.set(self.currentLocationAndType) + + let emptySignal = self.emptyStatePromise.get() |> distinctUntilChanged + let typeSignal = self.callListLocationAndType.get() |> map { locationAndType -> CallListViewType in + return locationAndType.type + } |> distinctUntilChanged + + self.emptyStateDisposable.set((combineLatest(emptySignal, typeSignal, self.statePromise.get()) |> deliverOnMainQueue).start(next: { [weak self] isEmpty, type, state in + if let strongSelf = self { + strongSelf.updateEmptyPlaceholder(theme: state.theme, strings: state.strings, type: type, hidden: !isEmpty) + } + })) } deinit { self.callListDisposable.dispose() + self.emptyStateDisposable.dispose() } func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { @@ -356,12 +378,50 @@ final class CallListControllerNode: ASDisplayNode { self.listNode.backgroundColor = theme.list.blocksBackgroundColor } + self.updateEmptyPlaceholder(theme: theme, strings: strings, type: self.currentLocationAndType.type, hidden: self.emptyTextNode.isHidden) + self.updateState { return $0.withUpdatedPresentationData(theme: theme, strings: strings) } } } + private let textFont = Font.regular(16.0) + + func updateEmptyPlaceholder(theme: PresentationTheme, strings: PresentationStrings, type: CallListViewType, hidden: Bool) { + let alpha: CGFloat = hidden ? 0.0 : 1.0 + let previousAlpha = self.emptyTextNode.alpha + self.emptyTextNode.alpha = alpha + self.emptyTextNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.2) + + if !hidden { + let type = self.currentLocationAndType.type + let string: String + if type == .missed { + string = strings.Calls_NoMissedCallsPlacehoder + } else { + string = strings.Calls_NoCallsPlaceholder + } + let color: UIColor + + switch self.mode { + case .tab: + self.backgroundColor = theme.chatList.backgroundColor + self.listNode.backgroundColor = theme.chatList.backgroundColor + color = theme.list.freeTextColor + case .navigation: + self.backgroundColor = theme.list.blocksBackgroundColor + self.listNode.backgroundColor = theme.list.blocksBackgroundColor + color = theme.list.freeTextColor + } + + self.emptyTextNode.attributedText = NSAttributedString(string: string, font: textFont, textColor: color, paragraphAlignment: .center) + if let layout = self.containerLayout { + self.updateLayout(layout.0, navigationBarHeight: layout.1, transition: .immediate) + } + } + } + func updateState(_ f: (CallListNodeState) -> CallListNodeState) { let state = f(self.currentState) if state != self.currentState { @@ -380,6 +440,7 @@ final class CallListControllerNode: ASDisplayNode { index = MessageIndex.absoluteUpperBound() } self.currentLocationAndType = CallListNodeLocationAndType(location: .changeType(index: index), type: type) + self.emptyStatePromise.set(.single(false)) self.callListLocationAndType.set(self.currentLocationAndType) } } @@ -420,7 +481,9 @@ final class CallListControllerNode: ASDisplayNode { if let strongSelf = self { strongSelf.callListView = transition.callListView - strongSelf.emptyStateUpdated(transition.callListView.filteredEntries.isEmpty) + let empty = countMeaningfulCallListEntries(transition.callListView.filteredEntries) == 0 + strongSelf.emptyStateUpdated(empty) + strongSelf.emptyStatePromise.set(.single(empty)) if !strongSelf.didSetReady { strongSelf.didSetReady = true @@ -445,6 +508,23 @@ final class CallListControllerNode: ASDisplayNode { } } + func updateLayout(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + var insets = layout.insets(options: [.input]) + insets.top += max(navigationBarHeight, layout.insets(options: [.statusBar]).top) + insets.left += layout.safeInsets.left + insets.right += layout.safeInsets.right + if self.mode == .navigation { + insets.top += 64.0 + } + + let size = layout.size + let contentRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom)) + + + let textSize = self.emptyTextNode.measure(CGSize(width: size.width - 20.0, height: size.height)) + transition.updateFrame(node: self.emptyTextNode, frame: CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - textSize.width) / 2.0), y: contentRect.minY + floor((contentRect.height - textSize.height) / 2.0)), size: textSize)) + } + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { self.containerLayout = (layout, navigationBarHeight) @@ -456,6 +536,8 @@ final class CallListControllerNode: ASDisplayNode { self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0) + updateLayout(layout, navigationBarHeight: navigationBarHeight, transition: transition) + var duration: Double = 0.0 var curve: UInt = 0 switch transition { diff --git a/TelegramUI/CallListNodeEntries.swift b/TelegramUI/CallListNodeEntries.swift index 149e26f058..4d3aea5dad 100644 --- a/TelegramUI/CallListNodeEntries.swift +++ b/TelegramUI/CallListNodeEntries.swift @@ -187,3 +187,13 @@ func callListNodeEntriesForView(_ view: CallListView, state: CallListNodeState, } return result } + +func countMeaningfulCallListEntries(_ entries: [CallListNodeEntry]) -> Int { + var count: Int = 0 + for entry in entries { + if case .setting = entry.stableId {} else { + count += 1 + } + } + return count +} diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index fb1757738b..13d08067d4 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -276,38 +276,38 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin } } } - return selectedNode - }, addToTransitionSurface: { view in - guard let strongSelf = self else { - return - } - strongSelf.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: strongSelf.chatDisplayNode.historyNode.view) - }, openUrl: { url in - self?.openUrl(url, concealed: false) - }, openPeer: { peer, navigation in - self?.openPeer(peerId: peer.id, navigation: navigation, fromMessage: nil) - }, callPeer: { peerId in - self?.controllerInteraction?.callPeer(peerId) - }, enqueueMessage: { message in - self?.sendMessages([message]) - }, sendSticker: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { fileReference in - self?.controllerInteraction?.sendSticker(fileReference) - } : nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in - if let strongSelf = self { - strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { entry in - if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction { - var messageIdAndMedia: [MessageId: [Media]] = [:] - - if let entry = entry, entry.index == centralIndex { - messageIdAndMedia[message.id] = [galleryMedia] - } - - controllerInteraction.hiddenMedia = messageIdAndMedia - - strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? ChatMessageItemView { - itemNode.updateHiddenMedia() - } + } + return selectedNode + }, addToTransitionSurface: { view in + guard let strongSelf = self else { + return + } + strongSelf.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: strongSelf.chatDisplayNode.historyNode.view) + }, openUrl: { url in + self?.openUrl(url, concealed: false) + }, openPeer: { peer, navigation in + self?.openPeer(peerId: peer.id, navigation: navigation, fromMessage: nil) + }, callPeer: { peerId in + self?.controllerInteraction?.callPeer(peerId) + }, enqueueMessage: { message in + self?.sendMessages([message]) + }, sendSticker: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { fileReference in + self?.controllerInteraction?.sendSticker(fileReference) + } : nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in + if let strongSelf = self { + strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { entry in + if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction { + var messageIdAndMedia: [MessageId: [Media]] = [:] + + if let entry = entry, entry.index == centralIndex { + messageIdAndMedia[message.id] = [galleryMedia] + } + + controllerInteraction.hiddenMedia = messageIdAndMedia + + strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ChatMessageItemView { + itemNode.updateHiddenMedia() } } } @@ -667,6 +667,13 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin return } + if let cachedUserData = strongSelf.peerView?.cachedData as? CachedUserData, cachedUserData.callsPrivate { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } + let callResult = account.telegramApplicationContext.callManager?.requestCall(peerId: peer.id, endCurrentIfAny: false) if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult { if currentPeerId == peer.id { diff --git a/TelegramUI/ChatItemGalleryFooterContentNode.swift b/TelegramUI/ChatItemGalleryFooterContentNode.swift index 56a3a7f683..9497571774 100644 --- a/TelegramUI/ChatItemGalleryFooterContentNode.swift +++ b/TelegramUI/ChatItemGalleryFooterContentNode.swift @@ -112,7 +112,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { } } - var scrubberView: ChatVideoGalleryItemScrubberView? { + var scrubberView: ChatVideoGalleryItemScrubberView? = nil { didSet { if let scrubberView = self.scrubberView { self.view.addSubview(scrubberView) @@ -153,8 +153,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { self.playbackControlButton = HighlightableButtonNode() self.playbackControlButton.isHidden = true - self.scrubberView = ChatVideoGalleryItemScrubberView() - super.init() self.view.addSubview(self.deleteButton) @@ -288,7 +286,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { if !self.textNode.isHidden { let sideInset: CGFloat = 8.0 + leftInset let topInset: CGFloat = 8.0 - let textBottomInset: CGFloat = 8.0 + contentInset + let textBottomInset: CGFloat = 8.0 let textSize = self.textNode.measure(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude)) panelHeight += textSize.height + topInset + textBottomInset self.textNode.frame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: textSize) diff --git a/TelegramUI/ChatMediaInputNode.swift b/TelegramUI/ChatMediaInputNode.swift index 35e1c18900..cce03385c0 100644 --- a/TelegramUI/ChatMediaInputNode.swift +++ b/TelegramUI/ChatMediaInputNode.swift @@ -454,11 +454,11 @@ final class ChatMediaInputNode: ChatInputNode { } else if collectionId.namespace == ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue { strongSelf.setCurrentPane(.trending, transition: .animated(duration: 0.25, curve: .spring)) } else if collectionId.namespace == ChatMediaInputPanelAuxiliaryNamespace.savedStickers.rawValue { - strongSelf.setCurrentPane(.stickers, transition: .animated(duration: 0.25, curve: .spring)) + strongSelf.setCurrentPane(.stickers, transition: .animated(duration: 0.25, curve: .spring), collectionIdHint: collectionId.namespace) strongSelf.currentStickerPacksCollectionPosition = .navigate(index: nil, collectionId: collectionId) strongSelf.itemCollectionsViewPosition.set(.single(.navigate(index: nil, collectionId: collectionId))) } else if collectionId.namespace == ChatMediaInputPanelAuxiliaryNamespace.recentStickers.rawValue { - strongSelf.setCurrentPane(.stickers, transition: .animated(duration: 0.25, curve: .spring)) + strongSelf.setCurrentPane(.stickers, transition: .animated(duration: 0.25, curve: .spring), collectionIdHint: collectionId.namespace) strongSelf.currentStickerPacksCollectionPosition = .navigate(index: nil, collectionId: collectionId) strongSelf.itemCollectionsViewPosition.set(.single(.navigate(index: nil, collectionId: collectionId))) } else if collectionId.namespace == ChatMediaInputPanelAuxiliaryNamespace.peerSpecific.rawValue { @@ -894,7 +894,7 @@ final class ChatMediaInputNode: ChatInputNode { self.view.addGestureRecognizer(panRecognizer) } - private func setCurrentPane(_ pane: ChatMediaInputPaneType, transition: ContainedViewLayoutTransition) { + private func setCurrentPane(_ pane: ChatMediaInputPaneType, transition: ContainedViewLayoutTransition, collectionIdHint: Int32? = nil) { if let index = self.paneArrangement.panes.index(of: pane), index != self.paneArrangement.currentIndex { let previousGifPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs self.paneArrangement = self.paneArrangement.withIndexTransition(0.0).withCurrentIndex(index) @@ -912,6 +912,8 @@ final class ChatMediaInputNode: ChatInputNode { case .stickers: if let highlightedStickerCollectionId = self.inputNodeInteraction.highlightedStickerItemCollectionId { self.setHighlightedItemCollectionId(highlightedStickerCollectionId) + } else if let collectionIdHint = collectionIdHint { + self.setHighlightedItemCollectionId(ItemCollectionId(namespace: collectionIdHint, id: 0)) } case .trending: self.setHighlightedItemCollectionId(ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue, id: 0)) diff --git a/TelegramUI/GalleryThumbnailContainerNode.swift b/TelegramUI/GalleryThumbnailContainerNode.swift index 6003f76824..fcbac4e3e2 100644 --- a/TelegramUI/GalleryThumbnailContainerNode.swift +++ b/TelegramUI/GalleryThumbnailContainerNode.swift @@ -44,9 +44,9 @@ private final class GalleryThumbnailItemNode: ASDisplayNode { } } -final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDelegate { +final class GalleryThumbnailContainerNode: ASDisplayNode { let groupId: Int64 - private let scrollNode: ASScrollNode + private let contentNode: ASDisplayNode private(set) var items: [GalleryThumbnailItem] = [] private var itemNodes: [GalleryThumbnailItemNode] = [] @@ -55,13 +55,11 @@ final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDelegate { init(groupId: Int64) { self.groupId = groupId - self.scrollNode = ASScrollNode() - + self.contentNode = ASDisplayNode() + super.init() - - self.scrollNode.view.delegate = self - - self.addSubnode(self.scrollNode) + + self.addSubnode(self.contentNode) } func updateItems(_ items: [GalleryThumbnailItem], centralIndex: Int, progress: CGFloat) { @@ -88,7 +86,7 @@ final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDelegate { for itemNode in itemNodes { if itemNode.supernode == nil { - self.scrollNode.addSubnode(itemNode) + self.contentNode.addSubnode(itemNode) } } for itemNode in self.itemNodes { @@ -121,7 +119,7 @@ final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDelegate { func updateLayout(size: CGSize, centralIndex: Int, progress: CGFloat, transition: ContainedViewLayoutTransition) { self.currentLayout = size - self.scrollNode.frame = CGRect(origin: CGPoint(), size: size) + self.contentNode.frame = CGRect(origin: CGPoint(), size: size) let spacing: CGFloat = 2.0 let centralSpacing: CGFloat = 8.0 let itemHeight: CGFloat = 42.0 @@ -205,16 +203,4 @@ final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDelegate { delay += 0.01 } } - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - - } - - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { - - } - - func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { - - } } diff --git a/TelegramUI/LegacyController.swift b/TelegramUI/LegacyController.swift index 9cd028cf20..e2344f5553 100644 --- a/TelegramUI/LegacyController.swift +++ b/TelegramUI/LegacyController.swift @@ -5,6 +5,7 @@ import LegacyComponents public enum LegacyControllerPresentation { case custom case modal(animateIn: Bool) + case navigation } private func passControllerAppearanceAnimated(in: Bool, presentation: LegacyControllerPresentation) -> Bool { @@ -316,12 +317,18 @@ public class LegacyController: ViewController { return self.sizeClass.signal()! } - public init(presentation: LegacyControllerPresentation, theme: PresentationTheme?, initialLayout: ContainerViewLayout? = nil) { + public init(presentation: LegacyControllerPresentation, theme: PresentationTheme?, presentationData: PresentationData? = nil, initialLayout: ContainerViewLayout? = nil) { self.sizeClass.set(SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber)) self.presentation = presentation self.validLayout = initialLayout - super.init(navigationBarPresentationData: nil) + let navigationBarPresentationData: NavigationBarPresentationData? + if let presentationData = presentationData, case .navigation = presentation { + navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData) + } else { + navigationBarPresentationData = nil + } + super.init(navigationBarPresentationData: navigationBarPresentationData) if let theme = theme { self.statusBar.statusBarStyle = theme.rootController.statusBar.style.style @@ -401,7 +408,7 @@ public class LegacyController: ViewController { self.presentationCompleted?() } self.legacyController.viewDidAppear(animated && animateIn) - case .custom: + case .custom, .navigation: self.legacyController.viewDidAppear(animated) self.presentationCompleted?() } @@ -429,6 +436,20 @@ public class LegacyController: ViewController { super.containerLayoutUpdated(layout, transition: transition) self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition) + if let legacyTelegramController = self.legacyController as? TGViewController { + var duration: TimeInterval = 0.0 + if case let .animated(transitionDuration, _) = transition { + duration = transitionDuration + } + + var orientation = UIInterfaceOrientation.portrait + if layout.size.width > layout.size.height { + orientation = .landscapeRight + } + + legacyTelegramController._updateInset(for: orientation, force: false, notify: true) + legacyTelegramController.layoutController(for: layout.size, duration: duration) + } let updatedSizeClass: UIUserInterfaceSizeClass if case .regular = layout.metrics.widthClass { updatedSizeClass = .regular @@ -459,6 +480,9 @@ public class LegacyController: ViewController { //controller.didDismiss() } self.presentingViewController?.dismiss(animated: false, completion: completion) + + case .navigation: + break } } diff --git a/TelegramUI/LegacyInstantVideoController.swift b/TelegramUI/LegacyInstantVideoController.swift index 620e5ac75c..cf3d280745 100644 --- a/TelegramUI/LegacyInstantVideoController.swift +++ b/TelegramUI/LegacyInstantVideoController.swift @@ -25,7 +25,7 @@ final class InstantVideoController: LegacyController { private var dismissedVideo = false - override init(presentation: LegacyControllerPresentation, theme: PresentationTheme?, initialLayout: ContainerViewLayout? = nil) { + override init(presentation: LegacyControllerPresentation, theme: PresentationTheme?, presentationData: PresentationData? = nil, initialLayout: ContainerViewLayout? = nil) { self.audioStatus = InstantVideoControllerRecordingStatus(micLevel: self.micLevelValue.get()) super.init(presentation: presentation, theme: theme, initialLayout: initialLayout) diff --git a/TelegramUI/LegacyLocationController.swift b/TelegramUI/LegacyLocationController.swift index 695a6e43e8..07ed54cfb3 100644 --- a/TelegramUI/LegacyLocationController.swift +++ b/TelegramUI/LegacyLocationController.swift @@ -114,7 +114,7 @@ private func telegramMap(for location: TGLocationMediaAttachment) -> TelegramMed return TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, geoPlace: nil, venue: mapVenue, liveBroadcastingTimeout: nil) } -func legacyLocationController(message: Message, mapMedia: TelegramMediaMap, account: Account, openPeer: @escaping (Peer) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, stopLiveLocation: @escaping () -> Void, showActions: @escaping (TelegramMediaMap, Bool) -> Void) -> ViewController { +func legacyLocationController(message: Message, mapMedia: TelegramMediaMap, account: Account, modal: Bool, openPeer: @escaping (Peer) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, stopLiveLocation: @escaping () -> Void, openUrl: @escaping (String) -> Void) -> ViewController { let legacyAuthor: AnyObject? = message.author.flatMap(makeLegacyPeer) let legacyLocation = TGLocationMediaAttachment() @@ -126,7 +126,7 @@ func legacyLocationController(message: Message, mapMedia: TelegramMediaMap, acco let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - let legacyController = LegacyController(presentation: .modal(animateIn: true), theme: presentationData.theme) + let legacyController = LegacyController(presentation: .navigation, theme: presentationData.theme, presentationData: presentationData) let legacyMessage = makeLegacyMessage(message) @@ -219,18 +219,40 @@ func legacyLocationController(message: Message, mapMedia: TelegramMediaMap, acco let searchTheme = theme.rootController.activeNavigationSearchBar controller.pallete = TGLocationPallete(backgroundColor: listTheme.plainBackgroundColor, selectionColor: listTheme.itemHighlightedBackgroundColor, separatorColor: listTheme.itemPlainSeparatorColor, textColor: listTheme.itemPrimaryTextColor, secondaryTextColor: listTheme.itemSecondaryTextColor, accentColor: listTheme.itemAccentColor, destructiveColor: listTheme.itemDestructiveColor, locationColor: UIColor(rgb: 0x008df2), liveLocationColor: UIColor(rgb: 0xff6464), iconColor: searchTheme.backgroundColor, sectionHeaderBackgroundColor: theme.chatList.sectionHeaderFillColor, sectionHeaderTextColor: theme.chatList.sectionHeaderTextColor, searchBarPallete: TGSearchBarPallete(dark: theme.overallDarkAppearance, backgroundColor: searchTheme.backgroundColor, highContrastBackgroundColor: searchTheme.backgroundColor, textColor: searchTheme.inputTextColor, placeholderColor: searchTheme.inputPlaceholderTextColor, clearIcon: generateClearIcon(color: theme.rootController.activeNavigationSearchBar.inputClearButtonColor), barBackgroundColor: searchTheme.backgroundColor, barSeparatorColor: searchTheme.separatorColor, plainBackgroundColor: searchTheme.backgroundColor, accentColor: searchTheme.accentColor, accentContrastColor: searchTheme.backgroundColor, menuBackgroundColor: searchTheme.backgroundColor, segmentedControlBackgroundImage: nil, segmentedControlSelectedImage: nil, segmentedControlHighlightedImage: nil, segmentedControlDividerImage: nil), avatarPlaceholder: nil) - controller.modalMode = true - let navigationController = TGNavigationController(controllers: [controller])! - legacyController.bind(controller: navigationController) - controller.navigation_setDismiss({ [weak legacyController] in - legacyController?.dismiss() - }, rootController: nil) - controller.presentActionsMenu = { legacyLocation, directions in - if let location = legacyLocation { + controller.modalMode = modal + controller.presentActionsMenu = { [weak legacyController] legacyLocation, directions in + if let strongLegacyController = legacyController, let location = legacyLocation { let map = telegramMap(for: location) - showActions(map, directions) + + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let shareAction = OpenInControllerAction(title: presentationData.strings.Conversation_ContextMenuShare, action: { + strongLegacyController.present(ShareController(account: account, subject: .mapMedia(map), externalShare: true), in: .window(.root), with: nil) + }) + + strongLegacyController.present(OpenInActionSheetController(postbox: account.postbox, applicationContext: account.telegramApplicationContext, theme: presentationData.theme, strings: presentationData.strings, item: .location(map, withDirections: directions), additionalAction: shareAction, openUrl: openUrl), in: .window(.root), with: nil) } } + + controller.onViewDidAppear = { [weak controller] in + if let strongController = controller { + strongController.locationMapView.interactiveTransitionGestureRecognizerTest = { point -> Bool in + return point.x > 36.0 + } + } + } + + if modal { + let navigationController = TGNavigationController(controllers: [controller])! + legacyController.bind(controller: navigationController) + controller.navigation_setDismiss({ [weak legacyController] in + legacyController?.dismiss() + }, rootController: nil) + } else { + legacyController.navigationItem.title = controller.navigationItem.title + + legacyController.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationShareIcon(theme), style: .plain, target: controller, action: #selector(controller.actionsButtonPressed)) + legacyController.bind(controller: controller) + } return legacyController } diff --git a/TelegramUI/MediaPlayer.swift b/TelegramUI/MediaPlayer.swift index ed4fae85d5..138136b769 100644 --- a/TelegramUI/MediaPlayer.swift +++ b/TelegramUI/MediaPlayer.swift @@ -727,9 +727,11 @@ private final class MediaPlayerContext { f?() case .stop: self.stoppedAtEnd = true + self.seek(timestamp: 0.0, action: .pause) self.pause(lostAudioSession: false) case let .action(f): self.stoppedAtEnd = true + self.seek(timestamp: 0.0, action: .pause) self.pause(lostAudioSession: false) f() case let .loopDisablingSound(f): diff --git a/TelegramUI/OpenChatMessage.swift b/TelegramUI/OpenChatMessage.swift index e55707aab7..add38dbad3 100644 --- a/TelegramUI/OpenChatMessage.swift +++ b/TelegramUI/OpenChatMessage.swift @@ -151,7 +151,7 @@ func chatMessagePreviewControllerData(account: Account, message: Message, standa return nil } -func openChatMessage(account: Account, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?, dismissInput: @escaping () -> Void, present: @escaping (ViewController, Any?) -> Void, transitionNode: @escaping (MessageId, Media) -> (ASDisplayNode, () -> UIView?)?, addToTransitionSurface: @escaping (UIView) -> Void, openUrl: @escaping (String) -> Void, openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void, callPeer: @escaping (PeerId) -> Void, enqueueMessage: @escaping (EnqueueMessage) -> Void, sendSticker: ((FileMediaReference) -> Void)?, setupTemporaryHiddenMedia: @escaping (Signal, Int, Media) -> Void) -> Bool { +func openChatMessage(account: Account, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?, modal: Bool = false, dismissInput: @escaping () -> Void, present: @escaping (ViewController, Any?) -> Void, transitionNode: @escaping (MessageId, Media) -> (ASDisplayNode, () -> UIView?)?, addToTransitionSurface: @escaping (UIView) -> Void, openUrl: @escaping (String) -> Void, openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void, callPeer: @escaping (PeerId) -> Void, enqueueMessage: @escaping (EnqueueMessage) -> Void, sendSticker: ((FileMediaReference) -> Void)?, setupTemporaryHiddenMedia: @escaping (Signal, Int, Media) -> Void) -> Bool { if let mediaData = chatMessageGalleryControllerData(account: account, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, synchronousLoad: false) { switch mediaData { case let .url(url): @@ -195,21 +195,21 @@ func openChatMessage(account: Account, message: Message, standalone: Bool, rever return true case let .map(mapMedia): dismissInput() - present(legacyLocationController(message: message, mapMedia: mapMedia, account: account, openPeer: { peer in + + let controller = legacyLocationController(message: message, mapMedia: mapMedia, account: account, modal: modal, openPeer: { peer in openPeer(peer, .info) }, sendLiveLocation: { coordinate, period in let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, geoPlace: nil, venue: nil, liveBroadcastingTimeout: period)), replyToMessageId: nil, localGroupingKey: nil) enqueueMessage(outMessage) }, stopLiveLocation: { account.telegramApplicationContext.liveLocationManager?.cancelLiveLocation(peerId: message.id.peerId) - }, showActions: { media, directions in - let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - let shareAction = OpenInControllerAction(title: presentationData.strings.Conversation_ContextMenuShare, action: { - present(ShareController(account: account, subject: .mapMedia(media), externalShare: true), nil) - }) - - present(OpenInActionSheetController(postbox: account.postbox, applicationContext: account.telegramApplicationContext, theme: presentationData.theme, strings: presentationData.strings, item: .location(media, withDirections: directions), additionalAction: shareAction, openUrl: openUrl), nil) - }), nil) + }, openUrl: openUrl) + + if modal { + present(controller, nil) + } else { + navigationController?.pushViewController(controller) + } return true case let .stickerPack(reference): let controller = StickerPackPreviewController(account: account, stickerPack: reference, parentNavigationController: navigationController) diff --git a/TelegramUI/PeerMediaCollectionEmptyNode.swift b/TelegramUI/PeerMediaCollectionEmptyNode.swift index ba091bdb20..2e3d2508f2 100644 --- a/TelegramUI/PeerMediaCollectionEmptyNode.swift +++ b/TelegramUI/PeerMediaCollectionEmptyNode.swift @@ -42,6 +42,7 @@ final class PeerMediaCollectionEmptyNode: ASDisplayNode { self.textNode = ASTextNode() self.textNode.isLayerBacked = true self.textNode.displaysAsynchronously = false + self.textNode.isHidden = true self.activityIndicator = ActivityIndicator(type: .custom(theme.list.itemSecondaryTextColor, 22.0, 2.0), speed: .regular) diff --git a/TelegramUI/ShareActionButtonNode.swift b/TelegramUI/ShareActionButtonNode.swift index 8b7d40c409..1bf865d4f6 100644 --- a/TelegramUI/ShareActionButtonNode.swift +++ b/TelegramUI/ShareActionButtonNode.swift @@ -57,7 +57,7 @@ final class ShareActionButtonNode: HighlightTrackingButtonNode { let _ = badgeApply() let backgroundSize = CGSize(width: max(22.0, badgeLayout.size.width + 10.0 + 1.0), height: 22.0) - let backgroundFrame = CGRect(origin: CGPoint(x: self.titleNode.frame.maxX + 6.0, y: self.bounds.size.height - 38.0), size: backgroundSize) + let backgroundFrame = CGRect(origin: CGPoint(x: self.titleNode.frame.maxX + 6.0, y: self.bounds.size.height - 39.0), size: backgroundSize) self.badgeBackground.frame = backgroundFrame self.badgeLabel.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(backgroundFrame.midX - badgeLayout.size.width / 2.0), y: backgroundFrame.minY + 3.0), size: badgeLayout.size) diff --git a/TelegramUI/ShareControllerNode.swift b/TelegramUI/ShareControllerNode.swift index 085a10e6f5..8f689a97f8 100644 --- a/TelegramUI/ShareControllerNode.swift +++ b/TelegramUI/ShareControllerNode.swift @@ -131,6 +131,12 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate self.actionSeparatorNode.displaysAsynchronously = false self.actionSeparatorNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemSeparatorColor + if self.defaultAction == nil { + self.actionButtonNode.alpha = 0.0 + self.actionsBackgroundNode.alpha = 0.0 + self.actionSeparatorNode.alpha = 0.0 + } + super.init() self.controllerInteraction = ShareControllerInteraction(togglePeer: { [weak self] peer, search in @@ -155,17 +161,28 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate strongSelf.peersContentNode?.updateFoundPeers() } - let inputNodeAlpha: CGFloat = strongSelf.controllerInteraction!.selectedPeers.isEmpty ? 0.0 : 1.0 - if !strongSelf.inputFieldNode.alpha.isEqual(to: inputNodeAlpha) { - let previousAlpha = strongSelf.inputFieldNode.alpha - strongSelf.inputFieldNode.alpha = inputNodeAlpha - strongSelf.inputFieldNode.layer.animateAlpha(from: previousAlpha, to: inputNodeAlpha, duration: inputNodeAlpha.isZero ? 0.18 : 0.32) - - if inputNodeAlpha.isZero { - strongSelf.inputFieldNode.deactivateInput() + func updateActionNodesAlpha(_ nodes: [ASDisplayNode], alpha: CGFloat) { + for node in nodes { + if !node.alpha.isEqual(to: alpha) { + let previousAlpha = node.alpha + node.alpha = alpha + node.layer.animateAlpha(from: previousAlpha, to: alpha, duration: alpha.isZero ? 0.18 : 0.32) + + if let inputNode = node as? ShareInputFieldNode, alpha.isZero { + inputNode.deactivateInput() + } + } } } + let actionNodes: [ASDisplayNode] + if strongSelf.defaultAction == nil { + actionNodes = [strongSelf.inputFieldNode, strongSelf.actionsBackgroundNode, strongSelf.actionButtonNode, strongSelf.actionSeparatorNode] + } else { + actionNodes = [strongSelf.inputFieldNode] + } + updateActionNodesAlpha(actionNodes, alpha: strongSelf.controllerInteraction!.selectedPeers.isEmpty ? 0.0 : 1.0) + strongSelf.updateButton() strongSelf.peersContentNode?.updateSelectedPeers() @@ -197,7 +214,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate self.wrappingScrollNode.addSubnode(self.cancelButtonNode) self.cancelButtonNode.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside) - self.actionButtonNode.addTarget(self, action: #selector(self.installActionButtonPressed), forControlEvents: .touchUpInside) + self.actionButtonNode.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside) self.wrappingScrollNode.addSubnode(self.contentBackgroundNode) @@ -315,10 +332,15 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: insets.top), size: CGSize(width: width, height: maximumContentHeight)) let contentFrame = contentContainerFrame.insetBy(dx: 0.0, dy: 0.0) - var bottomGridInset = buttonHeight - + var bottomGridInset: CGFloat = 0 + + var actionButtonHeight: CGFloat = 0 + if self.defaultAction != nil || !self.controllerInteraction!.selectedPeers.isEmpty { + actionButtonHeight = buttonHeight + bottomGridInset += actionButtonHeight + } + let inputHeight = self.inputFieldNode.updateLayout(width: contentContainerFrame.size.width, transition: transition) - if !self.controllerInteraction!.selectedPeers.isEmpty { bottomGridInset += inputHeight } @@ -336,7 +358,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate transition.updateFrame(node: self.actionsBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentContainerFrame.size.height - bottomGridInset), size: CGSize(width: contentContainerFrame.size.width, height: bottomGridInset))) - transition.updateFrame(node: self.actionButtonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentContainerFrame.size.height - buttonHeight), size: CGSize(width: contentContainerFrame.size.width, height: buttonHeight))) + transition.updateFrame(node: self.actionButtonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentContainerFrame.size.height - actionButtonHeight), size: CGSize(width: contentContainerFrame.size.width, height: buttonHeight))) transition.updateFrame(node: self.inputFieldNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentContainerFrame.size.height - bottomGridInset), size: CGSize(width: contentContainerFrame.size.width, height: inputHeight))) @@ -403,7 +425,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate self.cancel?() } - @objc func installActionButtonPressed() { + @objc func actionButtonPressed() { if self.controllerInteraction!.selectedPeers.isEmpty { if let defaultAction = self.defaultAction { defaultAction.action() @@ -626,10 +648,10 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate if self.controllerInteraction!.selectedPeers.isEmpty { if let defaultAction = self.defaultAction { self.actionButtonNode.setTitle(defaultAction.title, with: Font.regular(20.0), with: self.presentationData.theme.actionSheet.standardActionTextColor, for: .normal) + self.actionButtonNode.badge = nil } else { self.actionButtonNode.setTitle(self.presentationData.strings.ShareMenu_Send, with: Font.medium(20.0), with: self.presentationData.theme.actionSheet.disabledActionTextColor, for: .normal) } - self.actionButtonNode.badge = nil } else { self.actionButtonNode.setTitle(self.presentationData.strings.ShareMenu_Send, with: Font.medium(20.0), with: self.presentationData.theme.actionSheet.standardActionTextColor, for: .normal) self.actionButtonNode.badge = "\(self.controllerInteraction!.selectedPeers.count)" diff --git a/TelegramUI/SharePeersContainerNode.swift b/TelegramUI/SharePeersContainerNode.swift index 0a61e1bda5..46259f20b8 100644 --- a/TelegramUI/SharePeersContainerNode.swift +++ b/TelegramUI/SharePeersContainerNode.swift @@ -171,6 +171,10 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { self.shareButtonNode.addTarget(self, action: #selector(self.sharePressed), forControlEvents: .touchUpInside) } + deinit { + self.disposable.dispose() + } + private func enqueueTransition(_ transition: ShareGridTransaction, firstTime: Bool) { self.enqueuedTransitions.append((transition, firstTime)) diff --git a/TelegramUI/TelegramController.swift b/TelegramUI/TelegramController.swift index eb6b8bac8a..cd16dcbdb6 100644 --- a/TelegramUI/TelegramController.swift +++ b/TelegramUI/TelegramController.swift @@ -16,7 +16,7 @@ private func presentLiveLocationController(account: Account, peerId: PeerId, con return transaction.getMessage(id) } |> deliverOnMainQueue).start(next: { [weak controller] message in if let message = message, let strongController = controller { - let _ = openChatMessage(account: account, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongController.navigationController as? NavigationController, dismissInput: { + let _ = openChatMessage(account: account, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongController.navigationController as? NavigationController, modal: true, dismissInput: { controller?.view.endEditing(true) }, present: { c, a in controller?.present(c, in: .window(.root), with: a) diff --git a/TelegramUI/UserInfoController.swift b/TelegramUI/UserInfoController.swift index 41808eecf9..71473057b1 100644 --- a/TelegramUI/UserInfoController.swift +++ b/TelegramUI/UserInfoController.swift @@ -942,10 +942,7 @@ public func userInfoController(account: Account, peerId: PeerId) -> ViewControll presentControllerImpl?(c, a) }), nil) }) - - let peerView = Promise() - peerView.set(account.viewTracker.peerView(peerId)) - + let deviceContacts: Signal<[(DeviceContactStableId, DeviceContactBasicData)], NoError> = peerView.get() |> map { peerView -> String in if let peer = peerView.peers[peerId] as? TelegramUser {