From 25a09c545191ca70fe5bea0ed2f3be2fb0d93208 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Jun 2022 22:22:21 +0300 Subject: [PATCH 1/2] Add manual account deletion --- Telegram/Telegram-iOS/Resources/Delete1.tgs | Bin 0 -> 12755 bytes Telegram/Telegram-iOS/Resources/Delete2.tgs | Bin 0 -> 12966 bytes Telegram/Telegram-iOS/Resources/Delete3.tgs | Bin 0 -> 17039 bytes Telegram/Telegram-iOS/Resources/Delete4.tgs | Bin 0 -> 8877 bytes Telegram/Telegram-iOS/Resources/Delete5.tgs | Bin 0 -> 15902 bytes .../Telegram-iOS/Resources/chunk2.ch2.q1.mp4 | Bin 44333 -> 0 bytes .../Telegram-iOS/en.lproj/Localizable.strings | 19 +- .../Sources/ChatListRecentPeersListItem.swift | 2 +- .../Navigation/NavigationController.swift | 2 +- .../Sources/HorizontalPeerItem.swift | 26 +- .../Sources/InviteLinkHeaderItem.swift | 12 +- .../Sources/GiftAvatarComponent.swift | 3 +- .../Sources/SelectablePeerNode.swift | 4 +- submodules/SettingsUI/BUILD | 1 + .../ChangePhoneNumberControllerNode.swift | 1 - .../Sources/DeleteAccountDataController.swift | 404 +++++++++++++++-- .../Sources/DeleteAccountFooterItem.swift | 32 +- .../DeleteAccountOptionsController.swift | 92 +++- .../Sources/DeleteAccountPeersItem.swift | 296 +++++++++++++ .../Sources/DeleteAccountPhoneItem.swift | 413 ++++++++++++++++++ .../PrivacyAndSecurityController.swift | 28 +- .../TwoStepVerificationUnlockController.swift | 2 +- .../PresentationResourcesSettings.swift | 1 + .../AppBadge@3x.png | Bin .../Contents.json | 0 .../DeleteTwoStepAuth.imageset/Contents.json | 12 + .../Menu/DeleteTwoStepAuth.imageset/Icon.pdf | 114 +++++ 27 files changed, 1383 insertions(+), 81 deletions(-) create mode 100644 Telegram/Telegram-iOS/Resources/Delete1.tgs create mode 100644 Telegram/Telegram-iOS/Resources/Delete2.tgs create mode 100644 Telegram/Telegram-iOS/Resources/Delete3.tgs create mode 100644 Telegram/Telegram-iOS/Resources/Delete4.tgs create mode 100644 Telegram/Telegram-iOS/Resources/Delete5.tgs delete mode 100644 Telegram/Telegram-iOS/Resources/chunk2.ch2.q1.mp4 create mode 100644 submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift create mode 100644 submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift rename submodules/TelegramUI/Images.xcassets/Components/{BadgeTEst.imageset => AppBadge.imageset}/AppBadge@3x.png (100%) rename submodules/TelegramUI/Images.xcassets/Components/{BadgeTEst.imageset => AppBadge.imageset}/Contents.json (100%) create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Icon.pdf diff --git a/Telegram/Telegram-iOS/Resources/Delete1.tgs b/Telegram/Telegram-iOS/Resources/Delete1.tgs new file mode 100644 index 0000000000000000000000000000000000000000..5a62b5603fae1993f8dd79acb852c97914204068 GIT binary patch literal 12755 zcmZvD1#BHL*JZ=Z%=E(4Bn>k(%*;*0%=E&{%*@Qp%*;*l+Ayc#7v}Bv{VT1syJP9< z_}Fu0UBj{E8M0{jFaLQUp)P#&;)$f1j(%XgbY@J%^t|31c{B#FkeVnEQL|J2ODvzAui*H3kL`}#%g-%e7d(Y; zrYH6tudn6Z4oM`JTkl?vD4)+0{=_%JAHI(YuHQD^H$FbVf&LG{tsmjSevjM!FT36S zAEEwl%SytN^;UkxGmhVl_p|}(cKrkoLojEc zfd8-Xi{%nsSiixshUEkK>tK$_?uQ79JB-}A5;IOaLSeUpUmMblB-Z{VYhrFETm*Wz z4ST{c{f3=Jls8Zo3T-N{5sCfYScv8m{%pjb36D5vNiq1TZu!317{G zvSogH*X}Foo(F<_KaRI-^0&_gldL`um$QEeeZKFPqj~iA+sA*bmB)X+XrwKzw(BfX zf5VMe_Q_S&{ax5{Q3`1x{_cww7o)PuhWKopB-gw%J<4+&o)ahwIc{d-oG*S0ej-egGQv?Hw6s^<36{S+yUmj-oKSAIN@8yA|YC}uob4BIgdPal+4cQwJnI7?LkJ8pZPE_>-e|85VHCQF05!mJ2#v`OYaA$L; zCq^u{@HS4p)@MNuQ`YM@O=N)}Mr~3etxFo7#x0{%sS@FeBiypYk9HCEi|2tZz!DInEMy*l}Z=5+wmT-Hjf4R9ooHfb^%6Bhp!BZ^sx>Bd-$Z7@m5_#xF( zH}O%NT2Tv9?udy35KrR^%`&E>EpRWI*ASDyWJrn2CPN0(z;`)yJ79J`waZMZjN9m( zDyoqjMaGrO;-PpyV#2TIX)CO54iwNg8w*6ar>>`%h@SpVu5G$+43U2rETo_g4d;}P zXCofmnVN20k)3PA2IC+alazXM#0zIyrIT2RoffX+l(DB8CM04;b?3;9zNl4$t>B9E z;Z_<&uMJZ-=f$b$9gCdt&hwl)=biJQiv@0jB#)G!xb8`ra?y+(kWw+!pTGjt0G@K8 zg?L48cLPZju<`PN;(_`#_MQd~L5M?iB!Pe~P$U6ueGsRRvztWrMA2asFt{Css^e+^ zKan8TMzzHBC#yK?UM6r<%~D8|jefksp%FH+&yyvsBIQnR z{K5^+NRS~#-0`Xud3V}KX75xpaxRv;f``-RL47{euXo-TOKz79RqO|&sd3b1le-fd zs2gDNLE$C@&G!jQD>@&qXT&!DN0{-&DGn@D=Y^E@ynSsm`q?z-(QKvq3ieG`M??-2 z@2hrLGB>ghI4nne9~gdw?8V9~Ok&C1u&)Gu-}89Cpfc!LI$S4f=3VLV&PR44gw*bo zL1{JQi2@1;S#`NVT;CkZYxvi5-G*@y=UORo8=B^`Z-ARjOxL@I^ad@LoYpMO`zLH9 z8)MrR_~ebqJ7W06Xvp2mHAU9p#vY?@ByH-c%(Z(Ib?V0}X_Y?-*KnoI%@B$b^Hs)* zBhg0-upCb#TnZmuf zuQBB`++dbs(Jq)}H^^e_(T|qv27RpCKRPlosO;z(N}3ZG7-EBSP~1Qv6|#`7-8>?D z+ZD2PgUQ+n%^4ii)~!+GBpR#6z%>66#%%afINB1hC7?0dg60R}Zz;1$F1F1HbS9x& zFyv~qkd9+W$^>GZi_WN=p(ncC3M@`X;6}m#R*E)fuhe+1I&5vmmd;#lfUms3r+H5M z$R1C8l9(C-Q!|?%Tckb#`N8rI)>)`$;Bm4J;e0}?nKFD44kD}EuTf^Bl<>b)kXf`p z$m@ZeB`f&V6Drn{g*lnYZhzkIn-ZH{Y$IrFD@c#UX8Navu=awp+fivlU>ceI?O=oG zgDr&iD%fJgjPQ#R%odBEboQvjBcH)5Qngq|!B}k76 zoF1!}gODkGoZA#QXxcbfE^z&($#j!jik>95k`(S?ruORi#m9jrYU*Zeach7c)*zY# zd)5-~WcbJm7&4A>b{UUJ{t{G^WiXz(F;1Eqiogt=>zl`J%j$4;o22bZJFj@ z?6v(OXvvy40-+a8s<&PtY8vhUVL;5$Tx;0)m1==&b)hZzDsTJtAY?BB&2xfZKiO4k zc#*$1*d1+wQ;(BISzxjwE8bK9_@uw-84vPrGckW&RpinNisl`dvtB42$&lW04ksI% z@tL;f%S6V5c6Vc18a2n>QhK*6U&+%}Nkq+jqbDr&5B+}F6*1UJ(B$|?1Q|`zMuDcq zX&k+H_+1aQA*I0~qttMU5U*wHZ=Smch4mqe8_=NfX*U7kNHgH=5tBS--L=^y|0ROf zP{O6b?Y`*;* zCh}X-U3}v<`BI?wK-)xkpxU|2SKVf81%^gRTFmM$OQWiWeGb#rv@MdtHOMgZ4rX=F zuWI4Vquh~7J)%1?6h*c;iinv8v&lU_W+LRUJ|8IN95i_ks8aa9#4EwT1RQev zPNF5-)N_$LfJ^>WSkCAa88TSZ*pkJI`@7K~w^euyqZg;8I69UJHuIOg1j}z4SA@kG zQcuJ`FZD*jU+!#Vb&FE|Rc~~T+Zw*Iokj0`JI%%}`?Bf#- zh61SP82BKISxzE2{V0gbil^8utbT|}8Do^7^AWhQ^F_z(F%g8)6Koz+A^#d5^*zkP zba5Xe*BW6nBl2?L?h5gKdSKfE+FvyH!TMT)c@2Y5?fm=T3&x2U`FDgj3u*@)Z#ka@ z*bQM<+v7V2SJzD5(59M$cdc#SI_%A?CQ~C7e+eDIRzKF;HVRVlHL$mgc1AMU_qR3Z z?}uf)r46T!j?(+Xa;gl6<>@@GSVmj+XuD$e*d7|zM0gVmgqafveHJN)-K-y*6UFWY z;5BVk>quhH@2CsYXbSIKrK%UEK?RLgstoTa?1`D5u7Wk>riA*{2ov>m4gxFk!&7AN#ML7GHUtYPu?JlIm) zo7$IBEG0|Og_5TyegaW{Jh9h`sngiBmv&-v;>t+K2o6g)Q@vMRujtBaLN|DAK(PDv zJu6!h#|@4SIar}HR72>oTeE2gbH>Q9&@c}Ez9BmU^@WPf6*I2+*kGn2A;D)09;I3y zP^&u;$VNp%&{`aGp5JpxqEnQVGlp&)wMNHjZ5u%O8^Qv%2-RI)LFY|((mRdsM z^5C#cjgT=O@39~3AYS`);eP%|-4l|xKc%$)i`9L8T5i+DdEVmCxlbthu%~aa21i|n zgQdIG8fV$uV*Re?Oq~nzEu4m`4?~IINYT8-tjrw&A}2F+D(qY11& zg`Q8{%f+x75luI5OFAn#5*_k*8|&S^7&H13a?V3G$4;Jw;8$My394`~Pn*{kp6^t1 z4xBcu5Umj{$I{1jj6-Vb9KbDm(~b-$bC0gEtq9AO!He>($w$LeSte@X+QZF72XDME z$a#_Qe@|)wKqrnw+EZ`2?BxMe5H;<`pQvYpz684yG&z-?ch$0oC6AN=wtFmhx#bIr zsz{D&6w$LvV#s#55S-#wg2i`aa{>He?HX3Oi5>-}Vggh1ELtiaKhG1}CG6-4c0dqt70D1Ye*pTd)|lbUPu7nnR~CVI&zmFJ>7BLa$|C2n=9o?S|v!_j+=r#x>7JOcIW9>hYZ`@u9JaT#y{?wup;PZ|n_;&TsjeY=(hBBfN#bh0|q6 zqh~+AWJaylZ-*eG5F?#^Dw_rWq3Lq2S6yf|`cI21Fk8N0)o<`qLL{Epp#RnAf-{$z zw7pT5q&^D~Vs15+#K;sPwt)xQhz9pmb|F|pA9Uc!ni|a;0&mXk5!3;+rC7>V&G}Eh ztPTUfB0aUd`BmZ;xs9n+Vh#u&Ur*tEwect%3vvWdY`gE@6+M2p9&t#5#=qFJr~H16n=r;V^72UEb=R7kGvodckw+S3BGZwgXv^L%ay}G$|crE4fC`X zAQvtfp%>{q=ag*jV5@I+CYw#mbayQ`+z7!%YDz%i;$=HiVhJb-smNSa7*mIug2AoW%{URe~Nso0P)Ax6_Nd*?= z79e6oFe^%UH`!o9fe-4A_-wfvy9R`xSZa-ql5=A+OJ}4o+n&uMb zRVfbok=if0r%uI{s8?rukb9{UXIothu=D~;nHXrp!#-3NhFZ6z;?aA!GmDbO8)F6< zohi8ZT~mv6M$SsW>N6f4Y&{`27QH0uB!U720aW=7i5DdD@C;|@sOHV<9qz3It-pm7 z?M7-jpY6)D9}tDaC<1hAo;YA9Nl@n}%9e`VZ1qFabo8^IZ zHLIMeeLGxO;x2IGnyCKCU zSH$!~Q^f+rl`T4(BwW*(wO4M~MZ~I9dxSS_Z`Ju>Rvda|$;P_cZ`(pgf-fcUiYG_a zT0Dv|`2i{zjnV#$Y3tf%*Uy8-J{-ZwBxVFV3GUvlrPtaY#bz%w4Qa)U$8rSPkc&;8 zXG`VsQ>My%{odS^Xgbndb}&m)GEMF~S(`3bT^eOLrq~kS7+ve=j3*z`P7d56=|L1a zJLm>Ph~xG!%q4!Ge#61CCGG%~I{HdnqKJJ7&CTq6ZB#AdI2 z#jdSsh4EBZH9UjOt2&27pz=93l>W*;=z2bzGKC-IO zOs}n9vH`6QJQJi0_;n)JinI4|xt5+3=BM}M6BIuAJ;^sW`TQ(2v_pxV_(d~O0HF5l z8x^wuQ%#W&CQ3$5dJV?fbeYFcJ`B;pfwS)^)W)>ibGr(c;prVen!;n%ShMTrzF(~! zU3eMa4^Pq_JMt)+G|#R*5B7Kq8s;ErYXsTcR7^ly9DyRmh~@sj5dQ8-HNIH*?`n4G z(JEV~l|HsoPs(l3mrMHJ=>3~|wB(Ft&~UsUk*Bp@Cc%1>H7FB27dSBziZK?lGoPu7;zKIywX3-(lnb!$H+viPb zxNoG+oO(&y@70}J;6J%%qKon5rrt4mEkP%v9Aa?=D^(9LvO?djte__*p?+)8J$ zK7ddE>1LvjOli#5_`n@dAyeLbELritX=U?oRn8APKW3lyYsA+w%sE+0JS+Szd?nKv ze9NkMceZ}NP(+4UuXfiIfJgb-$qQm7OMVx+sEM3+S@%xIL_h%E=@dpxlw7q*h~bI> zzV9Sq7D}~AfLts4T$OLLc(!d8$>)$jpvf#lyqlgAFJ)w@_0J5Tt9x3Qu4bH=Dxvdp>MN(`g`m%dC_>DfqxVjoAJl~85H`_WAz8LiW z0uM8H1WmrRi8*4i7?$m}r~Ev^O!M(XwWs7Iv^O6|vugu^BQ^W$EEiptZPMOM_^2K; zFpYf#SVkBn2KrWcq(a^3P#f#cj=8ur0B8~Cg(l~rGMx+%$S?J3H7E(k$0ue5YuCcl7@`GCH>IqMmve3nU9E3H?+?wGD`G- z%;?X4!z?X<9wn{t8j2WpL@V1!HE4H9%l5QXkhPZe*)B)yloJf(y$A+u#hjQi99i}@ zf5wIZUa-Dh*zyO(uobFc3c#>e6STHO>+^Q>FZ(nRDRmo@8ZnU(SmNyAz0z-&g`BYB0F)7UB;)WBbkBWmdC+8Otz@FF)um*mh1a-NA+AGkMq9~A>qk(1Tc zDE5nzhr&$C*qKSRzqt{ze{GDjS8&r+EFotfT~8{n9HS(oG8{TcKxE?B1xS)-P*jDi z*BW&!R7aL5PA&4rUI&|-+_++G%4VVgGL0F>@I6DOh-OW*TMU14LLZL?F*$k#8~3Vn zth{1lhDILyFkNj}l`Ybu36PBn1|07lTt<+O^58p0)u#_#exu!t^Gv5QJdN<6-%C7; zXixsZ?rO_NMRtgVx<%svg%fQ(P=TNyWH-3c{ez4>80zY~DNF?C4lTjryT%CnZ1|3j z#mHzRYl)6-^x7VRM&A%zxDw**Pa0Hj8lHNcrGyfummU&7E_ES0Sw{BpyTKo7(=8&Z z-0@CrW*SJU_l2;^dR!1PI92O1ZO(MqYBFb&)G|doixl#|b<_Cdu(K@`myA~cP=9uS z)EeW5I74nQfz`6(04=?W7K5dGZjc2;D={2{C)(57sOc?P{87C~fL{7^rUr1+6xq+n|_WZC>lcZWqyWxOjk}wCAGcxwvYXEP&UmrETY) zmzKYdCBC|J|1Z?e-@5BHCpTOzi;$tX02lc6!%?_&yk6)`ln8a{%sj`j`g-!O<~*hx z%n9_&$^J>N@r#Q*jhbOO+CqxA^WQH)yFs$43i}*UUe8$)9?jSxUHJ6w%lppbURZlF zHWh46{Hw3*$;S3x)-o7qSyyOx-!LtXcTRpD@WZMr<1~$Q!6TOlWH$_EE&G1GezPZO za5Qs)KIGUHwf`c*`@L`Z zL0Lu!T+N`g)>Jq=F~W-TMLRu`ikdy0+!IaAe`kBxd`3W&F7Z_*1Mj7KjLL{pGr2wy{%_b%?UC)-3e*mA6@Gz1vkNoTD?bT z2Qwb0A9O1?9t>4?Z1v~djba!aHtphuNUVwv=P>9z^ z?HD+)*AUlyfF5qc2LY{Sa#u&IgHp8a#%w;6Yqvv*pO=+|G=1UJ4G``4bUTB_zE!mS zeRKj7#usaTG5_SPJXN|kyVjWXE|}ec?T{8hV9kqlAymZYge zO(4G**1_IJ>6fU)l6x`SQyXj@-4TOnQ2c8EIRI)* zBd|qkx}>8Co8k}jwZaX9*FA)h@XefO=*dBD{Z?y_4(*0?V2;kcJExisU&9a%h3VVV zT|R@mq&4D7?y&TI8B~dWy}RGv_vRA=b!kUzO6oja?komE?i`&t8yQPgAqh`O7=8iA z_ruc6CL$VGa!rvVM6ZnyR*gg!h&f?zvpq#pB=Hu3XW1KL8qKHjWEZq0(|h(g0;)Wp z^v3<4OJXBi=hb{c%T0aftl@KSiiihf+kMZ+Xu^HM?~daab|@SWeKTrK;Lfb0=?xfq zL>8_;&R-fr4}CoO;{4O&zoe=073Q#AR=Yeb4{;?Xtc<^GA~_8G)DCr}{4Qub5{ikt z&;%VREzOEV-~*1s=xfBuJh0fsxQo)cX}2Q~Kmn`aUI=e&5^dfWG$wNNdM+96Y4i>t zyS?ihS$6ud{JV=yXWO~%>QvV?Vlf?tbfHL3%b2!2?z3n4iEyH-58Y*jadPy%K(DRf z)l5K^`{$rT`VlE7%aG8~KIyy#xt5~E0r}cLL-7xpQvO4xRBH+rvr=_Ab6Lsyyt#>~ z|L#m1kW<#q52nmYLkn`!1D!#E;FQXOGv7#5)j7DIYznKu$6cI*iFtm?KY8n9is-MK zee1%h&u0XY0MtW!>2S|YzmM?b?(3TY3MXf@KaioyZX5Dv_c5tUGOcSA+G_7mCs%ZK z@k1DW^hb2L1S*OWh?X?TfLF_fiLa|6C8&{aNcU0399tS2o&)ek)O0cZcnCLD*^tsT z3p-1tEnZn8`#jH~+9;ktAu~3fznfF$OdZ@*%?M{=g;fZ(PhUON%xvdbhmhbnow>uH zO&y#G(iOap)e@u-Sbovq@!Xnbumc4$SXX|hCz{h^TgDWrCE}_xNNcXLc<+eKGHTqK z)gX5WG;qQ8@7~6E+`32ZamnH`b#e_$>~b(2G}J2g2?hN$7J6oEqmSvLIH$JPrD(|} z#rqn0UJ~T{`sWYX*5{L9(DEws5?T9WC%hJ~69jV1qZO|FOjML_M^D{t`$@R-N)0H{ zsnPn{N+ZAfWO^rq%NR+ac6hzQj?r#If`Hh^jA!fpH*6-Lms1}*IgC_01xE28grdlf z^MAI$$6eUx5L9!JQ1&@ye0seX3#*Mzy{h0As2Gq|*#2gHpi0cK6&sRdQ?Y2&LSXgp zqPWt)OgfI8BJ+x&A^nlof5$_|i&J>S?Hgm^R#mt$K;t|TAjv?vBn}VD$h8MwG?Cx% zyxi$*Amae_3=`IE(j^P87s>=O#xE(R;3Vl78-E^;b;S7H_VMMu^TU zBH)#81H&FVeYQ`Uf#N_04Q3Cb>1qNq{n#Si8yUDug5zB>qf* zH?-=@(?f~ztzDWnihvX5pOSdMB$iiG<=-nnb;@vQLr*aC>MX4C=IT7Wuno~=bhnp> zFYUM<#rSlc+)LOd&u2`ozmUua5X7uOJ5EG9zA|}35Xtdqfa>ntW9MXk1=;MdZg)H1 zE4Ml)oS(=iy~%ew@?>#B^w9FpzTG}o1+Ua{D8&$WyB*Od?&r`-(Ke0)8rKB9m!V8& zGf*=5Q5*e*J>z1V{2rnGiTSVvyfj(;5`D;=5^+X5q|4&IA0^F&U(V=*F&7NT0__WW_gKU~p5Nq=uzLM(B)HW)?)`rk{~r=qeUs-jHsdSVa?DUz5|Iwb zEgZ2K5{#Z)85VrC^F@0+|L7ImXb$&&KsnpZ%k_V7oF}~mO*V#myE~un=J^hLby#S@ zd3RX+t4XvvEXe=)kNx*TvdKRJ@Q?VPqW^N?-iAkT$?j2P{%@!4WPU#Riu3_BZ;Eqe zmYe#mM$Sz0B0|x+T^>is+AtFYKCodUiu|8(>YSpN4$?%jokL69n+heg^9dr(< zvhC`@E-+2S+iyx$+eM;P4`t(YQ~VwaCgnQVVyAo8AxdwoWS3mc`sc{~mo?y)kyD}} zOqWQj$j*|c5&bDjRhd%iIXEI*-r;3TmqO|S@25M#tBwCHQ!OF3YE!N(KgO0dUq|Ks zBUDoq^~YrY)Jmi-cWy%LpC(O+{|9~l50d zm~Dxq%d@yuC22rk-7rN%q7XLN{`;c2*51|k z>%O+m!Oge&Oz%I`D#42;XtmwX@Aats0`K*r3 zlN?!*D~dII(-L|nrPJT|G`+j=Z(DK=LM3XX8#F;d7W(1V808R=QW0lIvxyt!uuQR+ z8UYLudU_YAu@95uG|4d)%PA7;unq2qolJ^&i-x9GR+*@~j>107A#BBK2oHrg#Ua&W zAq=U&t5-YHMCtHUg+*?g56<5W>Qq79-?Z%-vo+q)L$+ddv3c&5whDFLA$`+`cWbrF zZu$Hx{oKv1`@cx-E)!XmuvexI5YQNwY=uWxbUk_eDE(YgAL+FG5bU?YWwxERQgEa>Oy}p3n>C~?rTU=<_zZ5b2jt$uQ;Uh&l8R?@_!OOHpS=12iUKQ zkSYo@=5Umj@XiyM7pxu!{v5W+6=tF{Yur7e>zn#9t~h;*jN!Lz)UqD)O#(|lQ`#{j zkB0-V99eh6X?M>$$b$$JgNVe34J-66Ha#Wz^(HN_)7Qz0K$P)CY>@^Ih$x>`Q>xw* ztkI$8)Q!{r|1ppHjP?aOr2%zXh&diKp&=b@JE%`fcqTej+M(-We@JDhJG3K7H+|Y> z#wThrVZ#^3Vo87QLb69xhyS+IncbnTtE&8VGdv&hU}{IzM8}+Fk&z)Ce3?+{RVA)p zLi5^5aGN%|^HW=H)lNMhpn|6CYK_b9$wf*5@g0(++=a%pc#eRJldbFKyjKEkNTJ+N z`0VarM?ar3XN^VY2ilw(SwlU1_u6J=RrRg`%9XXJ)^IoB*01ZjFb~e2>fQLtuva_I zx7%+2H|H)y(u7L$Bi%SNSIvQ7j{AX7A{|DcFLCvnW)3=TzmxbDrKcr+rFe+<&{ya! z?KSZF4&TbkLZ9SnF>%wwiB=;beicB+c|talw%SkiEv-Zf3}$~kGqP#;I|x$@RrMWa zGJe>BdZp%TpQ1X#s$+tApaue-t|Z{cqRGMrmp~@NWTtV_jdt;#EUvXI#95A7(+w{XEGKP&of`*ZH`?5!&Lu1V6?BskIanNBMZ-6_ zD3Qc!eRvO>vE}OPf z?9DeUR|icuKY!oKeYKdIg*lr+_sVL+}g;as_kMEvhMSiSG;>C|c{ay0=P$N2P^oCreI_+H2K*x|y)bu`ar^Y25N z#jj6=&|SBz)Zq;F!5hljoyrCaY?Y$DH^~fbFwdHB2ZXa#0yi>;Pc>iKX=kH#DbKW! zYl#(*cVDLYtGv%Nj{S6q%3qkpW}my?%7RGU$X$pM#WgBTsdgE zuF!v<&fPFtR8IaesmrSHO#&!oswdUEDkmsOK?7gmgWe_%>e8~LRIn4{CYLz{Q$x$t zakEu*;APLC=itgr-%kZEl&rEIJp~8OI*sVIYc4?mk3z{Wq2vSG80N+2_A^&1aUD!s zMlx!;gLi=O5GR>>XUL%{*pC?C*~E3vAL#^~;e+)Zs-}Um5Ja5h{o(8ZzEmo|Ehb%| zEF&iaHe8@(L5xou^A3~FrH=VRtU?Snp?}OhIYA3{{W=pz%ac*#YJ^>E!hS~o-}m`9 zFAfS5@E0|0`*Jh(kYAyikj9gvVRw`d-}*}gnOTDrCwmJ7H}4mH8N3;^gq7U$yY?1w z;;ZY>-IEqSV!egki+i*0u+yEs!bnVH?uy*SAjr?dcgFKKa==}VhEE>TAvhQi)H*2q z2NJf=z;@dw4VxE}+V`+khPb#_pb6^(Hx6rE7Ep^XQfL3zHDt zu3bcMV_BLt{@+$pmZadMcrh(FQ+q@yqfz182@#12nG){j0Qr)@1O}PjLxy_68_jFf zFt@Jg>Ys=D<24)w-=zvQbd;xlPEVM0WzP9)>?NR0i z+tP*Z&)3j%Ht?_B3h&$L0GOm%5l-`j_ygH;H13}ZMz^wm-oSDrOiXVuTXqJQSIT8K z;n_SdF<|Uu9xtDFS&C%L$OK>m$B@BfhN;7#*%XuKiqKh|%Ys^nNRPRTZ*Jzlw!&4kWR)^Ns QzE8AX#rC2s0OXhd1q#sd`~Uy| literal 0 HcmV?d00001 diff --git a/Telegram/Telegram-iOS/Resources/Delete2.tgs b/Telegram/Telegram-iOS/Resources/Delete2.tgs new file mode 100644 index 0000000000000000000000000000000000000000..d17ced8b0723cc3b332781d68e60f05e84d07056 GIT binary patch literal 12966 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 YFBJX;UJ4W-^v8EfIHh-;3Iy2y0Z=DWBme*a literal 0 HcmV?d00001 diff --git a/Telegram/Telegram-iOS/Resources/Delete3.tgs b/Telegram/Telegram-iOS/Resources/Delete3.tgs new file mode 100644 index 0000000000000000000000000000000000000000..c65577a62a679f028c313c05707c8915f3368d4c GIT binary patch literal 17039 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 RPT#LDN^RUf=qHdM{~tD-Km`B* literal 0 HcmV?d00001 diff --git a/Telegram/Telegram-iOS/Resources/chunk2.ch2.q1.mp4 b/Telegram/Telegram-iOS/Resources/chunk2.ch2.q1.mp4 deleted file mode 100644 index b513f5f83d10d3deb32a930c4bfb7f46f79a32c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44333 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 HE9-v&JnZ1n diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 53975e99e0..6a132802cb 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7770,6 +7770,8 @@ Sorry for the inconvenience."; "DeleteAccount.DeleteMyAccountTitle" = "Delete My Account"; "DeleteAccount.DeleteMyAccount" = "Delete My Account"; +"DeleteAccount.SavedMessages" = "Saved"; + "DeleteAccount.ComeBackLater" = "Come Back Later"; "DeleteAccount.Continue" = "Continue"; @@ -7778,10 +7780,18 @@ Sorry for the inconvenience."; "DeleteAccount.GroupsAndChannelsTitle" = "Your Groups and Channels"; "DeleteAccount.GroupsAndChannelsText" = "The groups and channels you created will either get new admins or become orphaned."; -"DeleteAccount.GroupsAndChannelsInfo" = "You can transfer group and channel ownership to other users via Chat Info > Edit > Admins. [More info]()"; +"DeleteAccount.GroupsAndChannelsInfo" = "You can transfer group and channel ownership to other users via Chat Info > Edit > Admins."; "DeleteAccount.MessageHistoryTitle" = "Your Message History"; -"DeleteAccount.MessageHistoryText" = "Your chat partners will keep their message history with you, including the messages you shared in secret chats.\n\nYou can remove any messages for both sides at any time, but this will not be possible if you delete your account. [More info]()"; +"DeleteAccount.MessageHistoryText" = "Your chat partners will keep their message history with you, including the messages you shared in secret chats.\n\nYou can remove any messages for both sides at any time, but this will not be possible if you delete your account."; + +"DeleteAccount.DeleteMessagesURL" = "https://telegram.org/faq#q-can-i-delete-my-messages"; + +"DeleteAccount.EnterPhoneNumber" = "Enter Your Phone Number"; +"DeleteAccount.InvalidPhoneNumberError" = "Invalid phone number. Please try again."; + +"DeleteAccount.EnterPassword" = "Enter Your Password"; +"DeleteAccount.InvalidPasswordError" = "Invalid password. Please try again."; "DeleteAccount.ConfirmationAlertTitle" = "Proceed to Delete Your Account?"; "DeleteAccount.ConfirmationAlertText" = "Deleting your account will permanently delete your data!\n\nIt is imposible to reverse this action!"; @@ -7801,3 +7811,8 @@ Sorry for the inconvenience."; "Premium.Gift.Years_1" = "%@ Year"; "Premium.Gift.Years_any" = "%@ Years"; + +"Premium.GiftedTitle.3Month" = "[%@]() has gifted you a 3-month subscription for Telegram Premium"; +"Premium.GiftedTitle.6Month" = "[%@]() has gifted you a 6-month subscription for Telegram Premium"; +"Premium.GiftedTitle.12Month" = "[%@]() has gifted you a 12-month subscription for Telegram Premium"; +"Premium.GiftedDescription" = "You now have access to additional features."; diff --git a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift index 4bef020a8b..fd1bc9b262 100644 --- a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift @@ -120,7 +120,7 @@ class ChatListRecentPeersListItemNode: ListViewItemNode { peersNode = currentPeersNode peersNode.updateThemeAndStrings(theme: item.theme, strings: item.strings) } else { - peersNode = ChatListSearchRecentPeersNode(context: item.context, theme: item.theme, mode: .list, strings: item.strings, peerSelected: { peer in + peersNode = ChatListSearchRecentPeersNode(context: item.context, theme: item.theme, mode: .list(compact: false), strings: item.strings, peerSelected: { peer in self?.item?.peerSelected(peer) }, peerContextAction: { peer, node, gesture in self?.item?.peerContextAction(peer, node, gesture) diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index eaacde87bd..43e43813bc 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -1271,7 +1271,7 @@ open class NavigationController: UINavigationController, ContainableController, let badgeNode = ASImageNode() badgeNode.displaysAsynchronously = false - badgeNode.image = UIImage(bundleImageName: "Components/BadgeTest") + badgeNode.image = UIImage(bundleImageName: "Components/AppBadge") self.badgeNode = badgeNode self.displayNode.addSubnode(badgeNode) } diff --git a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift index 8f5d5d0928..5051bbe78f 100644 --- a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift +++ b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift @@ -12,7 +12,7 @@ import ContextUI import AccountContext public enum HorizontalPeerItemMode { - case list + case list(compact: Bool) case actionSheet } @@ -25,13 +25,13 @@ public final class HorizontalPeerItem: ListViewItem { let context: AccountContext public let peer: EnginePeer let action: (EnginePeer) -> Void - let contextAction: (EnginePeer, ASDisplayNode, ContextGesture?) -> Void + let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)? let isPeerSelected: (EnginePeer.Id) -> Bool let customWidth: CGFloat? let presence: EnginePeer.Presence? let unreadBadge: (Int32, Bool)? - public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, context: AccountContext, peer: EnginePeer, presence: EnginePeer.Presence?, unreadBadge: (Int32, Bool)?, action: @escaping (EnginePeer) -> Void, contextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, customWidth: CGFloat?) { + public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, context: AccountContext, peer: EnginePeer, presence: EnginePeer.Presence?, unreadBadge: (Int32, Bool)?, action: @escaping (EnginePeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, customWidth: CGFloat?) { self.theme = theme self.strings = strings self.mode = mode @@ -111,11 +111,6 @@ public final class HorizontalPeerItemNode: ListViewItemNode { item.action(item.peer) } } - self.peerNode.contextAction = { [weak self] node, gesture in - if let item = self?.item { - item.contextAction(item.peer, node, gesture) - } - } } override public func didLoad() { @@ -186,10 +181,25 @@ public final class HorizontalPeerItemNode: ListViewItemNode { if let strongSelf = self { strongSelf.item = item strongSelf.peerNode.theme = itemTheme + if case let .list(compact) = item.mode { + strongSelf.peerNode.compact = compact + } else { + strongSelf.peerNode.compact = false + } strongSelf.peerNode.setup(context: item.context, theme: item.theme, strings: item.strings, peer: EngineRenderedPeer(peer: item.peer), numberOfLines: 1, synchronousLoad: synchronousLoads) strongSelf.peerNode.frame = CGRect(origin: CGPoint(), size: itemLayout.size) strongSelf.peerNode.updateSelection(selected: item.isPeerSelected(item.peer.id), animated: false) + if let contextAction = item.contextAction { + strongSelf.peerNode.contextAction = { [weak item] node, gesture in + if let item = item { + contextAction(item.peer, node, gesture) + } + } + } else { + strongSelf.peerNode.contextAction = nil + } + let badgeBackgroundWidth: CGFloat if let currentBadgeBackgroundImage = currentBadgeBackgroundImage { strongSelf.badgeBackgroundNode.image = currentBadgeBackgroundImage diff --git a/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift b/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift index 6c7a0d04db..e5bdbe1cde 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift @@ -113,8 +113,9 @@ class InviteLinkHeaderItemNode: ListViewItemNode { let makeTextLayout = TextNode.asyncLayout(self.textNode) return { item, params, neighbors in - let leftInset: CGFloat = 28.0 + params.leftInset - let topInset: CGFloat = 124.0 + let leftInset: CGFloat = 24.0 + params.leftInset + let iconSize = CGSize(width: 140.0, height: 140.0) + let topInset: CGFloat = iconSize.height - 4.0 let spacing: CGFloat = 5.0 let attributedTitle = NSAttributedString(string: item.title ?? "", font: titleFont, textColor: item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) @@ -123,9 +124,9 @@ class InviteLinkHeaderItemNode: ListViewItemNode { return (TelegramTextAttributes.URL, contents) })) - let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedTitle, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedTitle, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) var contentSize = CGSize(width: params.width, height: topInset + textLayout.size.height) if let _ = item.title { @@ -138,13 +139,12 @@ class InviteLinkHeaderItemNode: ListViewItemNode { return (layout, { [weak self] in if let strongSelf = self { if strongSelf.item == nil { - strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.animationName), width: 192, height: 192, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) + strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.animationName), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) strongSelf.animationNode.visibility = true } strongSelf.item = item strongSelf.accessibilityLabel = attributedText.string - let iconSize = CGSize(width: 128.0, height: 128.0) strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: -10.0), size: iconSize) strongSelf.animationNode.updateLayout(size: iconSize) diff --git a/submodules/PremiumUI/Sources/GiftAvatarComponent.swift b/submodules/PremiumUI/Sources/GiftAvatarComponent.swift index 9069fab68b..f5fe398dd5 100644 --- a/submodules/PremiumUI/Sources/GiftAvatarComponent.swift +++ b/submodules/PremiumUI/Sources/GiftAvatarComponent.swift @@ -275,9 +275,8 @@ class GiftAvatarComponent: Component { } self.hasIdleAnimations = component.hasIdleAnimations - let avatarSize = CGSize(width: 100.0, height: 100.0) - self.avatarNode.setSignal(peerAvatarCompleteImage(account: component.context.account, peer: component.peer, size: avatarSize, font: avatarPlaceholderFont(size: 78.0), fullSize: true)) + self.avatarNode.setSignal(peerAvatarCompleteImage(account: component.context.account, peer: component.peer, size: avatarSize, font: avatarPlaceholderFont(size: 43.0), fullSize: true)) self.avatarNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - avatarSize.width) / 2.0), y: 63.0), size: avatarSize) return availableSize diff --git a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift index 48643b1b3b..c590dfa764 100644 --- a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift +++ b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift @@ -84,6 +84,8 @@ public final class SelectablePeerNode: ASDisplayNode { private var peer: EngineRenderedPeer? + public var compact = false + public var theme: SelectablePeerNodeTheme = SelectablePeerNodeTheme(textColor: .black, secretTextColor: .green, selectedTextColor: .blue, checkBackgroundColor: .white, checkFillColor: .blue, checkColor: .white, avatarPlaceholderColor: .white) { didSet { if !self.theme.isEqual(to: oldValue) { @@ -147,7 +149,7 @@ public final class SelectablePeerNode: ASDisplayNode { let text: String var overrideImage: AvatarNodeImageOverride? if peer.peerId == context.account.peerId { - text = strings.DialogList_SavedMessages + text = self.compact ? strings.DeleteAccount_SavedMessages : strings.DialogList_SavedMessages overrideImage = .savedMessagesIcon } else if peer.peerId.isReplies { text = strings.DialogList_Replies diff --git a/submodules/SettingsUI/BUILD b/submodules/SettingsUI/BUILD index 3e2c5fb18d..6f77d2566e 100644 --- a/submodules/SettingsUI/BUILD +++ b/submodules/SettingsUI/BUILD @@ -102,6 +102,7 @@ swift_library( "//submodules/PaymentMethodUI:PaymentMethodUI", "//submodules/PremiumUI:PremiumUI", "//submodules/InviteLinksUI:InviteLinksUI", + "//submodules/HorizontalPeerItem:HorizontalPeerItem", ], visibility = [ "//visibility:public", diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift index 9903e8b9ff..41ea069963 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift @@ -286,5 +286,4 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode { @objc func countryPressed() { self.selectCountryCode?() } - } diff --git a/submodules/SettingsUI/Sources/DeleteAccountDataController.swift b/submodules/SettingsUI/Sources/DeleteAccountDataController.swift index 9c1886c473..8396eb6eb5 100644 --- a/submodules/SettingsUI/Sources/DeleteAccountDataController.swift +++ b/submodules/SettingsUI/Sources/DeleteAccountDataController.swift @@ -13,25 +13,47 @@ import AlertUI import PresentationDataUtils import UrlHandling import InviteLinksUI +import CountrySelectionUI +import PhoneInputNode private struct DeleteAccountDataArguments { let context: AccountContext let openLink: (String) -> Void + let selectCountryCode: () -> Void + let updatePassword: (String) -> Void + let proceed: () -> Void } private enum DeleteAccountDataSection: Int32 { + case header case main } +private enum DeleteAccountEntryTag: Equatable, ItemListItemTag { + case password + + func isEqual(to other: ItemListItemTag) -> Bool { + if let other = other as? DeleteAccountEntryTag { + return self == other + } else { + return false + } + } +} + + private enum DeleteAccountDataEntry: ItemListNodeEntry, Equatable { case header(PresentationTheme, String, String, String) - - case peers(PresentationTheme, [Peer]) + case peers(PresentationTheme, [EnginePeer]) + case phone(PresentationTheme, PresentationStrings) + case password(PresentationTheme, String) case info(PresentationTheme, String) var section: ItemListSectionId { switch self { - case .header, .peers, .info: + case .header: + return DeleteAccountDataSection.header.rawValue + case .peers, .info, .phone, .password: return DeleteAccountDataSection.main.rawValue } } @@ -43,7 +65,11 @@ private enum DeleteAccountDataEntry: ItemListNodeEntry, Equatable { case .peers: return 1 case .info: + return 2 + case .phone: return 3 + case .password: + return 4 } } @@ -56,7 +82,7 @@ private enum DeleteAccountDataEntry: ItemListNodeEntry, Equatable { return false } case let .peers(lhsTheme, lhsPeers): - if case let .peers(rhsTheme, rhsPeers) = rhs, lhsTheme === rhsTheme, arePeerArraysEqual(lhsPeers, rhsPeers) { + if case let .peers(rhsTheme, rhsPeers) = rhs, lhsTheme === rhsTheme, lhsPeers == rhsPeers { return true } else { return false @@ -67,6 +93,19 @@ private enum DeleteAccountDataEntry: ItemListNodeEntry, Equatable { } else { return false } + case let .phone(lhsTheme, lhsStrings): + if case let .phone(rhsTheme, rhsStrings) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings { + return true + } else { + return false + } + case let .password(lhsTheme, lhsPlaceholder): + if case let .password(rhsTheme, rhsPlaceholder) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder { + return true + } else { + return false + } + } } @@ -80,14 +119,26 @@ private enum DeleteAccountDataEntry: ItemListNodeEntry, Equatable { case let .header(theme, animation, title, text): return InviteLinkHeaderItem(context: arguments.context, theme: theme, title: title, text: text, animationName: animation, sectionId: self.section, linkAction: nil) case let .peers(_, peers): - return ItemListTextItem(presentationData: presentationData, text: .plain(peers.first?.debugDisplayTitle ?? ""), sectionId: self.section) + return DeleteAccountPeersItem(context: arguments.context, theme: presentationData.theme, strings: presentationData.strings, peers: peers, sectionId: self.section) case let .info(_, text): return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) + case .phone: + return DeleteAccountPhoneItem(theme: presentationData.theme, strings: presentationData.strings, value: (nil, nil, ""), sectionId: self.section, selectCountryCode: { + arguments.selectCountryCode() + }, updated: { _ in + + }) + case let .password(_, placeholder): + return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(), text: "", placeholder: placeholder, type: .password, returnKeyType: .done, tag: DeleteAccountEntryTag.password, sectionId: self.section, textUpdated: { value in + arguments.updatePassword(value) + }, action: { + arguments.proceed() + }) } } } -private func deleteAccountDataEntries(presentationData: PresentationData, mode: DeleteAccountDataMode, peers: [Peer]) -> [DeleteAccountDataEntry] { +private func deleteAccountDataEntries(presentationData: PresentationData, mode: DeleteAccountDataMode, peers: [EnginePeer]) -> [DeleteAccountDataEntry] { var entries: [DeleteAccountDataEntry] = [] let headerTitle: String @@ -96,24 +147,45 @@ private func deleteAccountDataEntries(presentationData: PresentationData, mode: switch mode { case .peers: - headerAnimation = "" + headerAnimation = "Delete1" headerTitle = presentationData.strings.DeleteAccount_CloudStorageTitle headerText = presentationData.strings.DeleteAccount_CloudStorageText case .groups: - headerAnimation = "" + headerAnimation = "Delete2" headerTitle = presentationData.strings.DeleteAccount_GroupsAndChannelsTitle headerText = presentationData.strings.DeleteAccount_GroupsAndChannelsText case .messages: - headerAnimation = "" + headerAnimation = "Delete3" headerTitle = presentationData.strings.DeleteAccount_MessageHistoryTitle headerText = presentationData.strings.DeleteAccount_MessageHistoryText + case .phone: + headerAnimation = "Delete4" + headerTitle = presentationData.strings.DeleteAccount_EnterPhoneNumber + headerText = "" + case .password: + headerAnimation = "Delete5" + headerTitle = presentationData.strings.DeleteAccount_EnterPassword + headerText = "" } entries.append(.header(presentationData.theme, headerAnimation, headerTitle, headerText)) - entries.append(.peers(presentationData.theme, peers)) - if case .groups = mode { - entries.append(.info(presentationData.theme, presentationData.strings.DeleteAccount_GroupsAndChannelsInfo)) + switch mode { + case .peers: + if !peers.isEmpty { + entries.append(.peers(presentationData.theme, peers)) + } + case .groups: + if !peers.isEmpty { + entries.append(.peers(presentationData.theme, peers)) + entries.append(.info(presentationData.theme, presentationData.strings.DeleteAccount_GroupsAndChannelsInfo)) + } + case .messages: + break + case .phone: + entries.append(.phone(presentationData.theme, presentationData.strings)) + case .password: + entries.append(.password(presentationData.theme, presentationData.strings.LoginPassword_PasswordPlaceholder)) } return entries @@ -121,55 +193,150 @@ private func deleteAccountDataEntries(presentationData: PresentationData, mode: enum DeleteAccountDataMode { case peers - case groups + case groups([EnginePeer]) case messages + case phone + case password } -func deleteAccountDataController(context: AccountContext, mode: DeleteAccountDataMode) -> ViewController { +private struct DeleteAccountDataState: Equatable { + var password: String + var isLoading: Bool + + static func == (lhs: DeleteAccountDataState, rhs: DeleteAccountDataState) -> Bool { + return lhs.password == rhs.password && lhs.isLoading == rhs.isLoading + } +} + +func deleteAccountDataController(context: AccountContext, mode: DeleteAccountDataMode, twoStepAuthData: TwoStepVerificationAccessConfiguration?) -> ViewController { + let initialState = DeleteAccountDataState(password: "", isLoading: false) + let statePromise = ValuePromise(initialState, ignoreRepeated: true) + let stateValue = Atomic(value: initialState) + let updateState: ((DeleteAccountDataState) -> DeleteAccountDataState) -> Void = { f in + statePromise.set(stateValue.modify { f($0) }) + } + + var presentControllerImpl: ((ViewController) -> Void)? + var pushControllerImpl: ((ViewController) -> Void)? var replaceTopControllerImpl: ((ViewController) -> Void)? var dismissImpl: (() -> Void)? + var updateCodeImpl: (() -> Void)? + + var activateInputImpl: (() -> Void)? + var dismissInputImpl: (() -> Void)? + + if case .phone = mode { + loadServerCountryCodes(accountManager: context.sharedContext.accountManager, engine: context.engine, completion: { + updateCodeImpl?() + }) + } + var updateCountryCodeImpl: ((Int32, String) -> Void)? + var proceedImpl: (() -> Void)? + let arguments = DeleteAccountDataArguments(context: context, openLink: { _ in + }, selectCountryCode: { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let controller = AuthorizationSequenceCountrySelectionController(strings: presentationData.strings, theme: presentationData.theme) + controller.completeWithCountryCode = { code, name in + updateCountryCodeImpl?(Int32(code), name) + activateInputImpl?() + } + dismissInputImpl?() + pushControllerImpl?(controller) + }, updatePassword: { password in + updateState { current in + var updated = current + updated.password = password + return updated + } + }, proceed: { + proceedImpl?() }) - let peers: Signal<[Peer], NoError> = .single([]) - + let preloadedGroupPeers = Promise<[EnginePeer]>([]) + + let peers: Signal<[EnginePeer], NoError> + switch mode { + case .peers: + peers = combineLatest( + context.engine.peers.recentPeers() + |> map { recentPeers -> [EnginePeer] in + if case let .peers(peers) = recentPeers { + return peers.map { EnginePeer($0) } + } else { + return [] + } + }, + context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + ) |> map { recentPeers, accountPeer -> [EnginePeer] in + var peers: [EnginePeer] = [] + if let accountPeer = accountPeer { + peers.append(accountPeer) + } + peers.append(contentsOf: recentPeers.prefix(9)) + return peers + } + + preloadedGroupPeers.set(context.engine.peers.adminedPublicChannels(scope: .all) + |> map { peers -> [EnginePeer] in + return peers.map { EnginePeer($0) } + }) + case let .groups(preloadedPeers): + peers = .single(preloadedPeers.shuffled()) + default: + peers = .single([]) + } + let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, - peers + peers, + statePromise.get() ) - |> map { presentationData, peers -> (ItemListControllerState, (ItemListNodeState, Any)) in + |> map { presentationData, peers, state -> (ItemListControllerState, (ItemListNodeState, Any)) in let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { dismissImpl?() }) - let footerItem = DeleteAccountFooterItem(theme: presentationData.theme, title: presentationData.strings.DeleteAccount_ComeBackLater, secondaryTitle: presentationData.strings.DeleteAccount_Continue, action: { + var focusItemTag: DeleteAccountEntryTag? + var buttonTitle: String + switch mode { + case .phone: + buttonTitle = "" + case .password: + buttonTitle = "" + focusItemTag = .password + default: + buttonTitle = presentationData.strings.DeleteAccount_ComeBackLater + } + + let rightNavigationButton: ItemListNavigationButton? + if state.isLoading { + rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {}) + } else { + rightNavigationButton = nil + } + + let footerItem = DeleteAccountFooterItem(theme: presentationData.theme, title: buttonTitle, secondaryTitle: presentationData.strings.DeleteAccount_Continue, action: { dismissImpl?() }, secondaryAction: { - let nextMode: DeleteAccountDataMode? - switch mode { - case .peers: - nextMode = .groups - case .groups: - nextMode = .messages - case .messages: - nextMode = nil - } - - if let nextMode = nextMode { - let controller = deleteAccountDataController(context: context, mode: nextMode) - replaceTopControllerImpl?(controller) - } + proceedImpl?() }) - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.DeleteAccount_DeleteMyAccountTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: deleteAccountDataEntries(presentationData: presentationData, mode: mode, peers: peers), style: .blocks, footerItem: footerItem) + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.DeleteAccount_DeleteMyAccountTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: deleteAccountDataEntries(presentationData: presentationData, mode: mode, peers: peers), style: .blocks, focusItemTag: focusItemTag, footerItem: footerItem) return (controllerState, (listState, arguments)) } let controller = ItemListController(context: context, state: signal, tabBarItem: nil) + presentControllerImpl = { [weak controller] c in + controller?.present(c, in: .window(.root)) + } + pushControllerImpl = { [weak controller] c in + controller?.push(c) + } replaceTopControllerImpl = { [weak controller] c in if let navigationController = controller?.navigationController as? NavigationController { navigationController.pushViewController(c, completion: { [weak navigationController, weak controller, weak c] in @@ -184,7 +351,172 @@ func deleteAccountDataController(context: AccountContext, mode: DeleteAccountDat dismissImpl = { [weak controller] in let _ = controller?.dismiss() } - + updateCodeImpl = { [weak controller] in + controller?.forEachItemNode { itemNode in + if let itemNode = itemNode as? DeleteAccountPhoneItemNode { + itemNode.updateCountryCode() + } + } + } + + activateInputImpl = { [weak controller] in + controller?.forEachItemNode { itemNode in + if let itemNode = itemNode as? DeleteAccountPhoneItemNode { + itemNode.activateInput() + } + } + } + dismissInputImpl = { [weak controller] in + controller?.view.endEditing(true) + } + controller.didAppear = { firstTime in + if !firstTime { + return + } + activateInputImpl?() + } + + updateCountryCodeImpl = { [weak controller] code, name in + controller?.forEachItemNode { itemNode in + if let itemNode = itemNode as? DeleteAccountPhoneItemNode { + itemNode.updateCountryCode(code: code, name: name) + } + } + } + + proceedImpl = { [weak controller] in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + let action: ([EnginePeer]) -> Void = { preloadedPeers in + let nextMode: DeleteAccountDataMode? + switch mode { + case .peers: + if !preloadedPeers.isEmpty { + nextMode = .groups(preloadedPeers) + } else { + nextMode = .messages + } + case .groups: + nextMode = .messages + case .messages: + nextMode = .phone + case .phone: + if let twoStepAuthData = twoStepAuthData, case .set = twoStepAuthData { + nextMode = .password + } else { + nextMode = nil + } + case .password: + nextMode = nil + } + + if let nextMode = nextMode { + let controller = deleteAccountDataController(context: context, mode: nextMode, twoStepAuthData: twoStepAuthData) + replaceTopControllerImpl?(controller) + } else { + presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.DeleteAccount_ConfirmationAlertTitle, text: presentationData.strings.DeleteAccount_ConfirmationAlertText, actions: [TextAlertAction(type: .destructiveAction, title: presentationData.strings.DeleteAccount_ConfirmationAlertDelete, action: { + updateState { current in + var updated = current + updated.isLoading = true + return updated + } + + let accountId = context.account.id + let accountManager = context.sharedContext.accountManager + let _ = (context.engine.auth.deleteAccount(reason: "Manual") + |> deliverOnMainQueue).start(error: { _ in + updateState { current in + var updated = current + updated.isLoading = false + return updated + } + + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) + }, completed: { + dismissImpl?() + let _ = logoutFromAccount(id: accountId, accountManager: accountManager, alreadyLoggedOutRemotely: true).start() + }) + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: { + dismissImpl?() + })])) + } + } + + switch mode { + case .peers: + let _ = (preloadedGroupPeers.get() + |> take(1) + |> deliverOnMainQueue).start(next: { peers in + action(peers) + }) + case .phone: + var phoneNumber: String? + controller?.forEachItemNode { itemNode in + if let itemNode = itemNode as? DeleteAccountPhoneItemNode { + phoneNumber = itemNode.phoneNumber + } + } + + if let phoneNumber = phoneNumber, phoneNumber.count > 4 { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> deliverOnMainQueue) + .start(next: { accountPeer in + if let accountPeer = accountPeer, case let .user(user) = accountPeer, var phone = user.phone { + if !phone.hasPrefix("+") { + phone = "+\(phone)" + } + if phone != phoneNumber { + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.DeleteAccount_InvalidPhoneNumberError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) + return + } + action([]) + } + }) + } + case .password: + let state = stateValue.with { $0 } + if !state.password.isEmpty { + updateState { current in + var updated = current + updated.isLoading = true + return updated + } + + let _ = (context.engine.auth.requestTwoStepVerifiationSettings(password: state.password) + |> deliverOnMainQueue).start(error: { error in + updateState { current in + var updated = current + updated.isLoading = false + return updated + } + + let text: String + switch error { + case .limitExceeded: + text = presentationData.strings.LoginPassword_FloodError + case .invalidPassword: + text = presentationData.strings.DeleteAccount_InvalidPasswordError + default: + text = presentationData.strings.Login_UnknownError + } + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) + }, completed: { + updateState { current in + var updated = current + updated.isLoading = false + return updated + } + + action([]) + }) + return + } + + default: + action([]) + } + } + return controller } diff --git a/submodules/SettingsUI/Sources/DeleteAccountFooterItem.swift b/submodules/SettingsUI/Sources/DeleteAccountFooterItem.swift index 208bd12c34..b5b9f04b34 100644 --- a/submodules/SettingsUI/Sources/DeleteAccountFooterItem.swift +++ b/submodules/SettingsUI/Sources/DeleteAccountFooterItem.swift @@ -44,8 +44,9 @@ final class DeleteAccountFooterItem: ItemListControllerFooterItem { final class DeleteAccountFooterItemNode: ItemListControllerFooterItemNode { private let backgroundNode: NavigationBackgroundNode private let separatorNode: ASDisplayNode + private let clipNode: ASDisplayNode private let buttonNode: SolidRoundedButtonNode - private let secondaryButtonNode: HighlightTrackingButtonNode + private let secondaryButtonNode: HighlightableButtonNode private var validLayout: ContainerViewLayout? @@ -64,16 +65,20 @@ final class DeleteAccountFooterItemNode: ItemListControllerFooterItemNode { self.backgroundNode = NavigationBackgroundNode(color: item.theme.rootController.tabBar.backgroundColor) self.separatorNode = ASDisplayNode() + self.clipNode = ASDisplayNode() + self.clipNode.clipsToBounds = true + self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: .black, foregroundColor: .white), height: 50.0, cornerRadius: 11.0, gloss: true) - self.secondaryButtonNode = HighlightTrackingButtonNode() + self.secondaryButtonNode = HighlightableButtonNode() super.init() self.addSubnode(self.backgroundNode) self.addSubnode(self.separatorNode) - self.addSubnode(self.buttonNode) - self.addSubnode(self.secondaryButtonNode) + self.addSubnode(self.clipNode) + self.clipNode.addSubnode(self.buttonNode) + self.clipNode.addSubnode(self.secondaryButtonNode) self.secondaryButtonNode.addTarget(self, action: #selector(self.secondaryButtonPressed), forControlEvents: .touchUpInside) @@ -121,8 +126,19 @@ final class DeleteAccountFooterItemNode: ItemListControllerFooterItemNode { let secondaryButtonSize = self.secondaryButtonNode.measure(CGSize(width: buttonWidth, height: CGFloat.greatestFiniteMagnitude)) var panelHeight: CGFloat = buttonHeight + topInset + spacing + secondaryButtonSize.height + bottomInset + + var buttonOffset: CGFloat = 0.0 let totalPanelHeight: CGFloat + + if (self.buttonNode.title?.isEmpty ?? false) { + buttonOffset = -buttonHeight - topInset + self.buttonNode.alpha = 0.0 + } else { + self.buttonNode.alpha = 1.0 + } + if let inputHeight = layout.inputHeight, inputHeight > 0.0 { + panelHeight += buttonOffset totalPanelHeight = panelHeight + insets.bottom } else { panelHeight += insets.bottom @@ -130,13 +146,15 @@ final class DeleteAccountFooterItemNode: ItemListControllerFooterItemNode { } let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - totalPanelHeight), size: CGSize(width: layout.size.width, height: panelHeight)) - transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInset, y: panelFrame.minY + topInset), size: CGSize(width: buttonWidth, height: buttonHeight))) - - transition.updateFrame(node: self.secondaryButtonNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - secondaryButtonSize.width) / 2.0), y: panelFrame.minY + topInset + buttonHeight + spacing), size: secondaryButtonSize)) transition.updateFrame(node: self.backgroundNode, frame: panelFrame) self.backgroundNode.update(size: panelFrame.size, transition: transition) + transition.updateFrame(node: self.clipNode, frame: panelFrame) + + transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInset, y: topInset + buttonOffset), size: CGSize(width: buttonWidth, height: buttonHeight))) + transition.updateFrame(node: self.secondaryButtonNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - secondaryButtonSize.width) / 2.0), y: topInset + buttonHeight + spacing + buttonOffset), size: secondaryButtonSize)) + transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: panelFrame.origin, size: CGSize(width: panelFrame.width, height: UIScreenPixel))) return panelHeight diff --git a/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift b/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift index 7325104044..bc3d7f50e1 100644 --- a/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift +++ b/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift @@ -15,14 +15,17 @@ import PresentationDataUtils import UrlHandling import AccountUtils import PremiumUI +import PasswordSetupUI private struct DeleteAccountOptionsArguments { let changePhoneNumber: () -> Void let addAccount: () -> Void let setupPrivacy: () -> Void - let setTwoStepAuth: () -> Void + let setupTwoStepAuth: () -> Void let setPasscode: () -> Void let clearCache: () -> Void + let clearSyncedContacts: () -> Void + let deleteChats: () -> Void let contactSupport: () -> Void let deleteAccount: () -> Void } @@ -111,8 +114,8 @@ private enum DeleteAccountOptionsEntry: ItemListNodeEntry, Equatable { arguments.setupPrivacy() }) case let .setTwoStepAuth(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.setPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { - arguments.setTwoStepAuth() + return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteSetTwoStepAuth, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + arguments.setupTwoStepAuth() }) case let .setPasscode(_, title, text): return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteSetPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { @@ -124,11 +127,11 @@ private enum DeleteAccountOptionsEntry: ItemListNodeEntry, Equatable { }) case let .clearSyncedContacts(_, title, text): return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.clearSynced, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { - arguments.clearCache() + arguments.clearSyncedContacts() }) case let .deleteChats(_, title, text): return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteChats, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { - arguments.clearCache() + arguments.deleteChats() }) case let .contactSupport(_, title, text): return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.support, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { @@ -168,14 +171,14 @@ private func deleteAccountOptionsEntries(presentationData: PresentationData, can return entries } -public func deleteAccountOptionsController(context: AccountContext, navigationController: NavigationController, hasTwoStepAuth: Bool) -> ViewController { +public func deleteAccountOptionsController(context: AccountContext, navigationController: NavigationController, hasTwoStepAuth: Bool, twoStepAuthData: TwoStepVerificationAccessConfiguration?) -> ViewController { var pushControllerImpl: ((ViewController) -> Void)? var presentControllerImpl: ((ViewController, Any?) -> Void)? var replaceTopControllerImpl: ((ViewController, Bool) -> Void)? var dismissImpl: (() -> Void)? let supportPeerDisposable = MetaDisposable() - + let arguments = DeleteAccountOptionsArguments(changePhoneNumber: { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.engine.account.peerId)) |> deliverOnMainQueue).start(next: { accountPeer in @@ -224,9 +227,30 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo } }) }, setupPrivacy: { + replaceTopControllerImpl?(makePrivacyAndSecurityController(context: context), false) + }, setupTwoStepAuth: { + if let data = twoStepAuthData { + switch data { + case .set: + break + case let .notSet(pendingEmail): + if pendingEmail == nil { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let controller = TwoFactorAuthSplashScreen(sharedContext: context.sharedContext, engine: .authorized(context.engine), mode: .intro(.init( + title: presentationData.strings.TwoFactorSetup_Intro_Title, + text: presentationData.strings.TwoFactorSetup_Intro_Text, + actionText: presentationData.strings.TwoFactorSetup_Intro_Action, + doneText: presentationData.strings.TwoFactorSetup_Done_Action + ))) - }, setTwoStepAuth: { + replaceTopControllerImpl?(controller, false) + return + } + } + } + let controller = twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: false, data: twoStepAuthData.flatMap({ Signal.single(.access(configuration: $0)) }))) + replaceTopControllerImpl?(controller, false) }, setPasscode: { let _ = passcodeOptionsAccessController(context: context, pushController: { controller in replaceTopControllerImpl?(controller, false) @@ -241,11 +265,44 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo }, clearCache: { pushControllerImpl?(storageUsageController(context: context)) dismissImpl?() + }, clearSyncedContacts: { + replaceTopControllerImpl?(dataPrivacyController(context: context), false) + }, deleteChats: { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + var faqUrl = presentationData.strings.DeleteAccount_DeleteMessagesURL + if faqUrl == "DeleteAccount.DeleteMessagesURL" || faqUrl.isEmpty { + faqUrl = "https://telegram.org/faq#q-can-i-delete-my-messages" + } + let resolvedUrl = resolveInstantViewUrl(account: context.account, url: faqUrl) + + let resolvedUrlPromise = Promise() + resolvedUrlPromise.set(resolvedUrl) + + let openFaq: (Promise) -> Void = { resolvedUrl in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) + presentControllerImpl?(controller, nil) + let _ = (resolvedUrl.get() + |> take(1) + |> deliverOnMainQueue).start(next: { [weak controller] resolvedUrl in + controller?.dismiss() + dismissImpl?() + + context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in + }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in + pushControllerImpl?(controller) + }, dismissInput: {}, contentContext: nil) + }) + } + + openFaq(resolvedUrlPromise) }, contactSupport: { [weak navigationController] in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let supportPeer = Promise() supportPeer.set(context.engine.peers.supportPeerId()) - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - + var faqUrl = presentationData.strings.Settings_FAQ_URL if faqUrl == "Settings.FAQ_URL" || faqUrl.isEmpty { faqUrl = "https://telegram.org/faq#general" @@ -289,7 +346,7 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo }) ]), nil) }, deleteAccount: { - let controller = deleteAccountDataController(context: context, mode: .peers) + let controller = deleteAccountDataController(context: context, mode: .peers, twoStepAuthData: twoStepAuthData) replaceTopControllerImpl?(controller, true) }) @@ -337,7 +394,18 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo } }) } else { - navigationController?.replaceTopController(c, animated: true) + if c is PrivacyAndSecurityControllerImpl { + if let navigationController = navigationController { + if let existing = navigationController.viewControllers.first(where: { $0 is PrivacyAndSecurityControllerImpl }) as? ViewController { + existing.scrollToTop?() + dismissImpl?() + } else { + navigationController.replaceTopController(c, animated: true) + } + } + } else { + navigationController?.replaceTopController(c, animated: true) + } } } dismissImpl = { [weak controller] in diff --git a/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift b/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift new file mode 100644 index 0000000000..f5c1b34c52 --- /dev/null +++ b/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift @@ -0,0 +1,296 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import ItemListUI +import PresentationDataUtils +import HorizontalPeerItem +import AccountContext +import MergeLists + +private struct PeersEntry: Comparable, Identifiable { + let index: Int + let peer: EnginePeer + let theme: PresentationTheme + let strings: PresentationStrings + + var stableId: EnginePeer.Id { + return self.peer.id + } + + static func ==(lhs: PeersEntry, rhs: PeersEntry) -> Bool { + if lhs.index != rhs.index { + return false + } + if lhs.peer != rhs.peer { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + return true + } + + static func <(lhs: PeersEntry, rhs: PeersEntry) -> Bool { + return lhs.index < rhs.index + } + + func item(context: AccountContext) -> ListViewItem { + return HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: .list(compact: true), context: context, peer: self.peer, presence: nil, unreadBadge: nil, action: { _ in }, contextAction: nil, isPeerSelected: { _ in return false }, customWidth: nil) + } +} + +private struct DeleteAccountPeersItemNodeTransition { + let deletions: [ListViewDeleteItem] + let insertions: [ListViewInsertItem] + let updates: [ListViewUpdateItem] + let firstTime: Bool + let animated: Bool +} + +private func preparedPeersTransition(context: AccountContext, from fromEntries: [PeersEntry], to toEntries: [PeersEntry], firstTime: Bool, animated: Bool) -> DeleteAccountPeersItemNodeTransition { + let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) + + let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context), directionHint: .Down) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context), directionHint: nil) } + + return DeleteAccountPeersItemNodeTransition(deletions: deletions, insertions: insertions, updates: updates, firstTime: firstTime, animated: animated) +} + +class DeleteAccountPeersItem: ListViewItem, ItemListItem { + var sectionId: ItemListSectionId + + let context: AccountContext + let theme: PresentationTheme + let strings: PresentationStrings + let peers: [EnginePeer] + + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peers: [EnginePeer], sectionId: ItemListSectionId) { + self.context = context + self.theme = theme + self.strings = strings + self.peers = peers + self.sectionId = sectionId + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = DeleteAccountPeersItemNode() + let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + + node.contentSize = layout.contentSize + node.insets = layout.insets + + Queue.mainQueue().async { + completion(node, { + return (nil, { _ in apply() }) + }) + } + } + } + + func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + if let nodeValue = node() as? DeleteAccountPeersItemNode { + let makeLayout = nodeValue.asyncLayout() + + async { + let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + Queue.mainQueue().async { + completion(layout, { _ in + apply() + }) + } + } + } + } + } +} + +class DeleteAccountPeersItemNode: ListViewItemNode, ItemListItemNode { + private let backgroundNode: ASDisplayNode + private let topStripeNode: ASDisplayNode + private let bottomStripeNode: ASDisplayNode + private let maskNode: ASImageNode + + private let dataPromise = Promise<(AccountContext, [EnginePeer], PresentationTheme, PresentationStrings)>() + private var disposable: Disposable? + + private var item: DeleteAccountPeersItem? + private var layoutParams: ListViewItemLayoutParams? + + private let listView: ListView + private var queuedTransitions: [DeleteAccountPeersItemNodeTransition] = [] + + var tag: ItemListItemTag? { + return self.item?.tag + } + + init() { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + + self.topStripeNode = ASDisplayNode() + self.topStripeNode.isLayerBacked = true + + self.bottomStripeNode = ASDisplayNode() + self.bottomStripeNode.isLayerBacked = true + + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + + self.listView = ListView() + self.listView.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + + super.init(layerBacked: false, dynamicBounce: false) + + self.addSubnode(self.listView) + + let previous: Atomic<[PeersEntry]> = Atomic(value: []) + let firstTime:Atomic = Atomic(value: true) + + self.disposable = (self.dataPromise.get() |> deliverOnMainQueue).start(next: { [weak self] data in + if let strongSelf = self { + let (context, peers, theme, strings) = data + + var entries: [PeersEntry] = [] + for peer in peers { + entries.append(PeersEntry(index: entries.count, peer: peer, theme: theme, strings: strings)) + } + + let animated = !firstTime.swap(false) + + let transition = preparedPeersTransition(context: context, from: previous.swap(entries), to: entries, firstTime: !animated, animated: animated) + strongSelf.enqueueTransition(transition) + } + }) + } + + deinit { + self.disposable?.dispose() + } + + private func enqueueTransition(_ transition: DeleteAccountPeersItemNodeTransition) { + self.queuedTransitions.append(transition) + self.dequeueTransitions() + } + + private func dequeueTransitions() { + while !self.queuedTransitions.isEmpty { + let transition = self.queuedTransitions.removeFirst() + + var options = ListViewDeleteAndInsertOptions() + if transition.firstTime { + options.insert(.PreferSynchronousResourceLoading) + options.insert(.PreferSynchronousDrawing) + options.insert(.Synchronous) + options.insert(.LowLatency) + } else if transition.animated { + options.insert(.AnimateInsertion) + } + self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateOpaqueState: nil, completion: { _ in }) + } + } + + func asyncLayout() -> (_ item: DeleteAccountPeersItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + let currentItem = self.item + + return { item, params, neighbors in + var themeUpdated = false + if currentItem?.theme !== item.theme { + themeUpdated = true + } + print(themeUpdated) + + let contentSize: CGSize + var insets: UIEdgeInsets + let separatorHeight = UIScreenPixel + + contentSize = CGSize(width: params.width, height: 109.0) + insets = itemListNeighborsGroupedInsets(neighbors, params) + + let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) + let layoutSize = layout.size + + return (layout, { [weak self] in + if let strongSelf = self { + strongSelf.item = item + strongSelf.layoutParams = params + + strongSelf.dataPromise.set(.single((item.context, item.peers, item.theme, item.strings))) + + strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor + strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + + if strongSelf.backgroundNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) + } + if strongSelf.topStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) + } + if strongSelf.bottomStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) + } + if strongSelf.maskNode.supernode == nil { + strongSelf.addSubnode(strongSelf.maskNode) + } + + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { + case .sameSection(false): + strongSelf.topStripeNode.isHidden = true + default: + hasTopCorners = true + strongSelf.topStripeNode.isHidden = hasCorners + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = params.leftInset + 16.0 + bottomStripeOffset = -separatorHeight + strongSelf.bottomStripeNode.isHidden = false + default: + bottomStripeInset = 0.0 + bottomStripeOffset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners + } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) + strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) + + let listInsets = UIEdgeInsets(top: params.leftInset, left: 0.0, bottom: params.rightInset, right: 0.0) + strongSelf.listView.bounds = CGRect(x: 0.0, y: 0.0, width: 92.0, height: params.width) + strongSelf.listView.position = CGPoint(x: params.width / 2.0, y: contentSize.height / 2.0) + strongSelf.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: 92.0, height: params.width), insets: listInsets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + + strongSelf.dequeueTransitions() + } + }) + } + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) + } +} diff --git a/submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift b/submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift new file mode 100644 index 0000000000..a36fbca9a2 --- /dev/null +++ b/submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift @@ -0,0 +1,413 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import ItemListUI +import PresentationDataUtils +import PhoneInputNode +import CountrySelectionUI +import CoreTelephony + +private func generateCountryButtonBackground(color: UIColor, strokeColor: UIColor) -> UIImage? { + return generateImage(CGSize(width: 56, height: 44.0 + 6.0), rotatedContext: { size, context in + let arrowSize: CGFloat = 6.0 + let lineWidth = UIScreenPixel + + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(color.cgColor) + context.fill(CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height - arrowSize))) + context.move(to: CGPoint(x: size.width, y: size.height - arrowSize)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - arrowSize)) + context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize, y: size.height)) + context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize - arrowSize, y: size.height - arrowSize)) + context.closePath() + context.fillPath() + + context.setStrokeColor(strokeColor.cgColor) + context.setLineWidth(lineWidth) + + context.move(to: CGPoint(x: size.width, y: size.height - arrowSize - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - arrowSize - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize, y: size.height - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize - arrowSize, y: size.height - arrowSize - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: 15.0, y: size.height - arrowSize - lineWidth / 2.0)) + context.strokePath() + + context.move(to: CGPoint(x: 0.0, y: lineWidth / 2.0)) + context.addLine(to: CGPoint(x: size.width, y: lineWidth / 2.0)) + context.strokePath() + })?.stretchableImage(withLeftCapWidth: 55, topCapHeight: 1) +} + +private func generateCountryButtonHighlightedBackground(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 56.0, height: 44.0 + 6.0), rotatedContext: { size, context in + let arrowSize: CGFloat = 6.0 + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(color.cgColor) + context.fill(CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height - arrowSize))) + context.move(to: CGPoint(x: size.width, y: size.height - arrowSize)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - arrowSize)) + context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize, y: size.height)) + context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize - arrowSize, y: size.height - arrowSize)) + context.closePath() + context.fillPath() + })?.stretchableImage(withLeftCapWidth: 55, topCapHeight: 2) +} + +private func generatePhoneInputBackground(color: UIColor, strokeColor: UIColor) -> UIImage? { + return generateImage(CGSize(width: 82.0, height: 44.0), rotatedContext: { size, context in + let lineWidth = UIScreenPixel + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(color.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(strokeColor.cgColor) + context.setLineWidth(lineWidth) + context.move(to: CGPoint(x: 0.0, y: size.height - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: size.width, y: size.height - lineWidth / 2.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 2.0 + lineWidth / 2.0, y: size.height - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: size.width - 2.0 + lineWidth / 2.0, y: 0.0)) + context.strokePath() + })?.stretchableImage(withLeftCapWidth: 81, topCapHeight: 2) +} + + +class DeleteAccountPhoneItem: ListViewItem, ItemListItem { + let theme: PresentationTheme + let strings: PresentationStrings + let value: (Int32?, String?, String) + let sectionId: ItemListSectionId + let selectCountryCode: () -> Void + let updated: (Int) -> Void + + init(theme: PresentationTheme, strings: PresentationStrings, value: (Int32?, String?, String), sectionId: ItemListSectionId, selectCountryCode: @escaping () -> Void, updated: @escaping (Int) -> Void) { + self.theme = theme + self.strings = strings + self.value = value + self.sectionId = sectionId + self.selectCountryCode = selectCountryCode + self.updated = updated + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = DeleteAccountPhoneItemNode() + let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + + node.contentSize = layout.contentSize + node.insets = layout.insets + + Queue.mainQueue().async { + completion(node, { + return (nil, { _ in apply() }) + }) + } + } + } + + func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + if let nodeValue = node() as? DeleteAccountPhoneItemNode { + let makeLayout = nodeValue.asyncLayout() + + async { + let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + Queue.mainQueue().async { + completion(layout, { _ in + apply() + }) + } + } + } + } + } +} + +class DeleteAccountPhoneItemNode: ListViewItemNode, ItemListItemNode { + private let backgroundNode: ASDisplayNode + private let topStripeNode: ASDisplayNode + private let bottomStripeNode: ASDisplayNode + private let maskNode: ASImageNode + + private let countryButton: ASButtonNode + private let phoneBackground: ASImageNode + private let phoneInputNode: PhoneInputNode + + private var item: DeleteAccountPhoneItem? + private var layoutParams: ListViewItemLayoutParams? + + var preferredCountryIdForCode: [String: String] = [:] + + var tag: ItemListItemTag? { + return self.item?.tag + } + + init() { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + + self.topStripeNode = ASDisplayNode() + self.topStripeNode.isLayerBacked = true + + self.bottomStripeNode = ASDisplayNode() + self.bottomStripeNode.isLayerBacked = true + + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + + self.countryButton = ASButtonNode() + + self.phoneBackground = ASImageNode() + self.phoneBackground.displaysAsynchronously = false + self.phoneBackground.displayWithoutProcessing = true + self.phoneBackground.isLayerBacked = true + + self.phoneInputNode = PhoneInputNode(fontSize: 17.0) + + super.init(layerBacked: false, dynamicBounce: false) + + self.addSubnode(self.phoneBackground) + self.addSubnode(self.countryButton) + self.addSubnode(self.phoneInputNode) + + self.countryButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 15.0, bottom: 4.0, right: 0.0) + self.countryButton.contentHorizontalAlignment = .left + + self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside) + + let processNumberChange: (String) -> Bool = { [weak self] number in + guard let strongSelf = self, let item = strongSelf.item else { + return false + } + if let (country, _) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode) { + let flagString = emojiFlagForISOCountryCode(country.id) + let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(country.id, strings: item.strings) ?? country.name + strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(17.0), with: item.theme.list.itemPrimaryTextColor, for: []) + + let maskFont = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers]) + if let mask = AuthorizationSequenceCountrySelectionController.lookupPatternByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode).flatMap({ NSAttributedString(string: $0, font: maskFont, textColor: item.theme.list.itemPlaceholderTextColor) }) { + strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = nil + strongSelf.phoneInputNode.mask = mask + } else { + strongSelf.phoneInputNode.mask = nil + strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: item.strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: item.theme.list.itemPlaceholderTextColor) + } + return true + } else { + return false + } + } + + self.phoneInputNode.numberTextUpdated = { [weak self] number in + if let strongSelf = self { + let _ = processNumberChange(strongSelf.phoneInputNode.number) + } + } + + self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in + if let strongSelf = self, let item = strongSelf.item { + if let name = name { + strongSelf.preferredCountryIdForCode[code] = name + } + + if processNumberChange(strongSelf.phoneInputNode.number) { + } else if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] { + let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: item.strings) ?? countryName + strongSelf.countryButton.setTitle(localizedName, with: Font.regular(17.0), with: item.theme.list.itemPrimaryTextColor, for: []) + } else if let code = Int(code), let (_, countryName) = countryCodeToIdAndName[code] { + strongSelf.countryButton.setTitle(countryName, with: Font.regular(17.0), with: item.theme.list.itemPrimaryTextColor, for: []) + } else { + strongSelf.countryButton.setTitle(item.strings.Login_CountryCode, with: Font.regular(17.0), with: item.theme.list.itemPrimaryTextColor, for: []) + } + } + } + + self.phoneInputNode.customFormatter = { number in + if let (_, code) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: [:]) { + return code.code + } else { + return nil + } + } + + var countryId: String? = nil + let networkInfo = CTTelephonyNetworkInfo() + if let carrier = networkInfo.subscriberCellularProvider { + countryId = carrier.isoCountryCode + } + + if countryId == nil { + countryId = (Locale.current as NSLocale).object(forKey: .countryCode) as? String + } + + var countryCodeAndId: (Int32, String) = (1, "US") + + if let countryId = countryId { + let normalizedId = countryId.uppercased() + for (code, idAndName) in countryCodeToIdAndName { + if idAndName.0 == normalizedId { + countryCodeAndId = (Int32(code), idAndName.0.uppercased()) + break + } + } + } + + self.phoneInputNode.number = "+\(countryCodeAndId.0)" + } + + @objc private func countryPressed() { + if let item = self.item { + item.selectCountryCode() + } + } + + var phoneNumber: String { + return self.phoneInputNode.number + } + + func updateCountryCode() { + self.phoneInputNode.codeAndNumber = self.phoneInputNode.codeAndNumber + } + + func updateCountryCode(code: Int32, name: String) { + self.phoneInputNode.codeAndNumber = (code, name, self.phoneInputNode.codeAndNumber.2) + } + + func activateInput() { + self.phoneInputNode.numberField.textField.becomeFirstResponder() + } + + func animateError() { + self.phoneInputNode.countryCodeField.layer.addShakeAnimation() + self.phoneInputNode.numberField.layer.addShakeAnimation() + } + + func asyncLayout() -> (_ item: DeleteAccountPhoneItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + let currentItem = self.item + + return { item, params, neighbors in + var updatedCountryButtonBackground: UIImage? + var updatedCountryButtonHighlightedBackground: UIImage? + var updatedPhoneBackground: UIImage? + + if currentItem?.theme !== item.theme { + updatedCountryButtonBackground = generateCountryButtonBackground(color: item.theme.list.itemBlocksBackgroundColor, strokeColor: item.theme.list.itemBlocksSeparatorColor) + updatedCountryButtonHighlightedBackground = generateCountryButtonHighlightedBackground(color: item.theme.list.itemHighlightedBackgroundColor) + updatedPhoneBackground = generatePhoneInputBackground(color: item.theme.list.itemBlocksBackgroundColor, strokeColor: item.theme.list.itemBlocksSeparatorColor) + } + + let contentSize: CGSize + var insets: UIEdgeInsets + let separatorHeight = UIScreenPixel + + let countryButtonHeight: CGFloat = 44.0 + let inputFieldsHeight: CGFloat = 44.0 + + contentSize = CGSize(width: params.width, height: countryButtonHeight + inputFieldsHeight) + insets = itemListNeighborsGroupedInsets(neighbors, params) + + let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) + let layoutSize = layout.size + + return (layout, { [weak self] in + if let strongSelf = self { + strongSelf.item = item + strongSelf.layoutParams = params + + strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor + strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + + if strongSelf.backgroundNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) + } + if strongSelf.topStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) + } + if strongSelf.bottomStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) + } + if strongSelf.maskNode.supernode == nil { + strongSelf.addSubnode(strongSelf.maskNode) + } + + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { + case .sameSection(false): + strongSelf.topStripeNode.isHidden = true + default: + hasTopCorners = true + strongSelf.topStripeNode.isHidden = hasCorners + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = params.leftInset + 16.0 + bottomStripeOffset = -separatorHeight + strongSelf.bottomStripeNode.isHidden = false + default: + bottomStripeInset = 0.0 + bottomStripeOffset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners + } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) + strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) + + if let updatedCountryButtonBackground = updatedCountryButtonBackground { + strongSelf.countryButton.setBackgroundImage(updatedCountryButtonBackground, for: []) + } + if let updatedCountryButtonHighlightedBackground = updatedCountryButtonHighlightedBackground { + strongSelf.countryButton.setBackgroundImage(updatedCountryButtonHighlightedBackground, for: .highlighted) + } + if let updatedPhoneBackground = updatedPhoneBackground { + strongSelf.phoneBackground.image = updatedPhoneBackground + } + + strongSelf.phoneInputNode.countryCodeField.textField.textColor = item.theme.list.itemPrimaryTextColor + strongSelf.phoneInputNode.countryCodeField.textField.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance + strongSelf.phoneInputNode.countryCodeField.textField.tintColor = item.theme.list.itemAccentColor + strongSelf.phoneInputNode.numberField.textField.textColor = item.theme.list.itemPrimaryTextColor + strongSelf.phoneInputNode.numberField.textField.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance + strongSelf.phoneInputNode.numberField.textField.tintColor = item.theme.list.itemAccentColor + + strongSelf.countryButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 15.0, bottom: 4.0, right: 0.0) + + strongSelf.countryButton.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: 44.0 + 6.0)) + strongSelf.phoneBackground.frame = CGRect(origin: CGPoint(x: 0.0, y: 44.0), size: CGSize(width: params.width, height: 44.0)) + + let countryCodeFrame = CGRect(origin: CGPoint(x: 11.0, y: 44.0), size: CGSize(width: 67.0, height: 44.0)) + let numberFrame = CGRect(origin: CGPoint(x: 92.0, y: 44.0), size: CGSize(width: layout.size.width - 70.0 - 8.0, height: 44.0)) + let placeholderFrame = numberFrame.offsetBy(dx: 0.0, dy: 8.0) + + let phoneInputFrame = countryCodeFrame.union(numberFrame) + + strongSelf.phoneInputNode.frame = phoneInputFrame + strongSelf.phoneInputNode.countryCodeField.frame = countryCodeFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY) + strongSelf.phoneInputNode.numberField.frame = numberFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY) + strongSelf.phoneInputNode.placeholderNode.frame = placeholderFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY) + } + }) + } + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) + } +} diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift index f594a23db5..7a070c170a 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift @@ -482,6 +482,10 @@ private func privacyAndSecurityControllerEntries(presentationData: PresentationD return entries } +class PrivacyAndSecurityControllerImpl: ItemListController { + +} + public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil, updatedBlockedPeers: ((BlockedPeersContext?) -> Void)? = nil, updatedHasTwoStepAuth: ((Bool) -> Void)? = nil, focusOnItemTag: PrivacyAndSecurityEntryTag? = nil, activeSessionsContext: ActiveSessionsContext? = nil, webSessionsContext: WebSessionsContext? = nil, blockedPeersContext: BlockedPeersContext? = nil, hasTwoStepAuth: Bool? = nil) -> ViewController { let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true) let stateValue = Atomic(value: PrivacyAndSecurityControllerState()) @@ -492,6 +496,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting var pushControllerImpl: ((ViewController, Bool) -> Void)? var replaceTopControllerImpl: ((ViewController) -> Void)? var presentControllerImpl: ((ViewController) -> Void)? + var getNavigationControllerImpl: (() -> NavigationController?)? let actionsDisposable = DisposableSet() @@ -822,12 +827,26 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting 6 * 30 * 24 * 60 * 60, 365 * 24 * 60 * 60 ] - let timeoutItems: [ActionSheetItem] = timeoutValues.map { value in + var timeoutItems: [ActionSheetItem] = timeoutValues.map { value in return ActionSheetButtonItem(title: timeIntervalString(strings: presentationData.strings, value: value), action: { dismissAction() timeoutAction(value) }) } + timeoutItems.append(ActionSheetButtonItem(title: presentationData.strings.PrivacySettings_DeleteAccountNow, color: .destructive, action: { + dismissAction() + + guard let navigationController = getNavigationControllerImpl?() else { + return + } + + let _ = (combineLatest(twoStepAuth.get(), twoStepAuthDataValue.get()) + |> take(1) + |> deliverOnMainQueue).start(next: { hasTwoStepAuth, twoStepAuthData in + let optionsController = deleteAccountOptionsController(context: context, navigationController: navigationController, hasTwoStepAuth: hasTwoStepAuth ?? false, twoStepAuthData: twoStepAuthData) + pushControllerImpl?(optionsController, true) + }) + })) controller.setItemGroups([ ActionSheetItemGroup(items: timeoutItems), ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) @@ -886,7 +905,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting actionsDisposable.dispose() } - let controller = ItemListController(context: context, state: signal) + let controller = PrivacyAndSecurityControllerImpl(context: context, state: signal) pushControllerImpl = { [weak controller] c, animated in (controller?.navigationController as? NavigationController)?.pushViewController(c, animated: animated) } @@ -896,7 +915,10 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting presentControllerImpl = { [weak controller] c in controller?.present(c, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } - + getNavigationControllerImpl = { [weak controller] in + return (controller?.navigationController as? NavigationController) + } + controller.didAppear = { _ in updateHasTwoStepAuth() } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationUnlockController.swift b/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationUnlockController.swift index 4ab4a52ad1..2d53114931 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationUnlockController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationUnlockController.swift @@ -206,7 +206,7 @@ private func twoStepVerificationUnlockSettingsControllerEntries(presentationData if let pendingEmail = pendingEmail { entries.append(.pendingEmailConfirmInfo(presentationData.theme, presentationData.strings.TwoStepAuth_SetupPendingEmail(pendingEmail.email.pattern).string)) entries.append(.pendingEmailConfirmCode(presentationData.theme, presentationData.strings, presentationData.strings.TwoStepAuth_RecoveryCode, state.emailCode)) - entries.append(.pendingEmailInfo(presentationData.theme, "[" + presentationData.strings.TwoStepAuth_ConfirmationAbort + "]()")) + entries.append(.pendingEmailInfo(presentationData.theme, "[" + presentationData.strings.TwoStepAuth_ConfirmationAbort + "]()")) /*entries.append(.pendingEmailInfo(presentationData.theme, presentationData.strings.TwoStepAuth_ConfirmationText + "\n\n\(pendingEmailAndValue.pendingEmail.pattern)\n\n[" + presentationData.strings.TwoStepAuth_ConfirmationAbort + "]()"))*/ } else { diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift index 6731dbcd62..5910683d1f 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift @@ -91,6 +91,7 @@ public struct PresentationResourcesSettings { public static let changePhoneNumber = renderIcon(name: "Settings/Menu/ChangePhoneNumber") public static let deleteAddAccount = renderIcon(name: "Settings/Menu/DeleteAddAccount") + public static let deleteSetTwoStepAuth = renderIcon(name: "Settings/Menu/DeleteTwoStepAuth") public static let deleteSetPasscode = renderIcon(name: "Settings/Menu/FaceId") public static let deleteChats = renderIcon(name: "Settings/Menu/DeleteChats") public static let clearSynced = renderIcon(name: "Settings/Menu/ClearSynced") diff --git a/submodules/TelegramUI/Images.xcassets/Components/BadgeTEst.imageset/AppBadge@3x.png b/submodules/TelegramUI/Images.xcassets/Components/AppBadge.imageset/AppBadge@3x.png similarity index 100% rename from submodules/TelegramUI/Images.xcassets/Components/BadgeTEst.imageset/AppBadge@3x.png rename to submodules/TelegramUI/Images.xcassets/Components/AppBadge.imageset/AppBadge@3x.png diff --git a/submodules/TelegramUI/Images.xcassets/Components/BadgeTEst.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Components/AppBadge.imageset/Contents.json similarity index 100% rename from submodules/TelegramUI/Images.xcassets/Components/BadgeTEst.imageset/Contents.json rename to submodules/TelegramUI/Images.xcassets/Components/AppBadge.imageset/Contents.json diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Contents.json new file mode 100644 index 0000000000..bfbeb098c2 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Icon.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Icon.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Icon.pdf new file mode 100644 index 0000000000..d771d5afaf --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Icon.pdf @@ -0,0 +1,114 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.345098 0.337255 0.839216 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +-1.000000 -0.000000 -0.000000 1.000000 25.000000 6.000000 cm +1.000000 1.000000 1.000000 scn +6.650000 19.000000 m +2.945000 19.000000 0.000000 16.055000 0.000000 12.350000 c +0.000000 8.645000 2.945000 5.700001 6.650000 5.700001 c +7.500165 5.700001 8.310955 5.861581 9.054688 6.145313 c +10.450001 4.750000 l +12.350000 4.750000 l +12.350000 2.850000 l +14.250000 2.850000 l +14.250000 0.950001 l +14.903126 0.296875 l +15.093125 0.106874 15.300938 0.000000 15.585938 0.000000 c +18.049999 0.000000 l +18.619999 0.000000 19.000000 0.380001 19.000000 0.950001 c +19.000000 3.414062 l +19.000000 3.699062 18.893126 3.906875 18.703125 4.096874 c +12.854687 9.945312 l +13.138419 10.689045 13.299999 11.499835 13.299999 12.350000 c +13.299999 16.055000 10.355000 19.000000 6.650000 19.000000 c +h +5.225000 16.150000 m +6.555000 16.150000 7.600000 15.105000 7.600000 13.775000 c +7.600000 12.445000 6.555000 11.400000 5.225000 11.400000 c +3.895000 11.400000 2.850000 12.445000 2.850000 13.775000 c +2.850000 15.105000 3.895000 16.150000 5.225000 16.150000 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1987 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000002077 00000 n +0000002100 00000 n +0000002273 00000 n +0000002347 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2406 +%%EOF \ No newline at end of file From 7868651acb93f040a55e78567497bf2c8bc50b3f Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Jun 2022 23:32:27 +0300 Subject: [PATCH 2/2] Various fixes --- submodules/TabBarUI/Sources/TabBarNode.swift | 14 +++++++++----- .../Tabs/IconSettings.imageset/Contents.json | 13 ++++++++----- .../Sources/PeerInfo/PeerInfoScreen.swift | 10 +++++----- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/submodules/TabBarUI/Sources/TabBarNode.swift b/submodules/TabBarUI/Sources/TabBarNode.swift index 86ebf72173..0cb0a35bf4 100644 --- a/submodules/TabBarUI/Sources/TabBarNode.swift +++ b/submodules/TabBarUI/Sources/TabBarNode.swift @@ -317,6 +317,8 @@ class TabBarNode: ASDisplayNode { } } + var reduceMotion: Bool = false + var selectedIndex: Int? { didSet { if self.selectedIndex != oldValue { @@ -574,12 +576,14 @@ class TabBarNode: ASDisplayNode { node.contentWidth = max(contentWidth, imageContentWidth) node.isSelected = true - ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 1.0, delay: 0.1) - node.imageNode.layer.animateScale(from: 1.0, to: 0.87, duration: 0.1, removeOnCompletion: false, completion: { [weak node] _ in - node?.imageNode.layer.animateScale(from: 0.87, to: 1.0, duration: 0.14, removeOnCompletion: false, completion: { [weak node] _ in - node?.imageNode.layer.removeAllAnimations() + if !self.reduceMotion && item.item.ringSelection { + ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 1.0, delay: 0.1) + node.imageNode.layer.animateScale(from: 1.0, to: 0.87, duration: 0.1, removeOnCompletion: false, completion: { [weak node] _ in + node?.imageNode.layer.animateScale(from: 0.87, to: 1.0, duration: 0.14, removeOnCompletion: false, completion: { [weak node] _ in + node?.imageNode.layer.removeAllAnimations() + }) }) - }) + } } else { let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/Contents.json index c457392e9f..4163ba603b 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/Contents.json @@ -1,12 +1,15 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "ic_tb_settings.pdf" + "filename" : "ic_tb_settings.pdf", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index ccc3a9f4c8..4fe61f6e60 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -7990,8 +7990,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen { icon = UIImage(bundleImageName: "Chat List/Tabs/IconSettings") } - let tabBarItem: Signal<(String, UIImage?, UIImage?, String?, Bool), NoError> = combineLatest(queue: .mainQueue(), self.context.sharedContext.presentationData, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get(), getServerProvidedSuggestions(account: self.context.account), accountTabBarAvatar, accountTabBarAvatarBadge) - |> map { presentationData, notificationsAuthorizationStatus, notificationsWarningSuppressed, suggestions, accountTabBarAvatar, accountTabBarAvatarBadge -> (String, UIImage?, UIImage?, String?, Bool) in + let tabBarItem: Signal<(String, UIImage?, UIImage?, String?, Bool, Bool), NoError> = combineLatest(queue: .mainQueue(), self.context.sharedContext.presentationData, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get(), getServerProvidedSuggestions(account: self.context.account), accountTabBarAvatar, accountTabBarAvatarBadge) + |> map { presentationData, notificationsAuthorizationStatus, notificationsWarningSuppressed, suggestions, accountTabBarAvatar, accountTabBarAvatarBadge -> (String, UIImage?, UIImage?, String?, Bool, Bool) in let notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed) let phoneNumberWarning = suggestions.contains(.validatePhoneNumber) let passwordWarning = suggestions.contains(.validatePassword) @@ -7999,15 +7999,15 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen { if accountTabBarAvatarBadge > 0 { otherAccountsBadge = compactNumericCountString(Int(accountTabBarAvatarBadge), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) } - return (presentationData.strings.Settings_Title, accountTabBarAvatar?.0 ?? icon, accountTabBarAvatar?.1 ?? icon, notificationsWarning || phoneNumberWarning || passwordWarning ? "!" : otherAccountsBadge, accountTabBarAvatar != nil || presentationData.reduceMotion) + return (presentationData.strings.Settings_Title, accountTabBarAvatar?.0 ?? icon, accountTabBarAvatar?.1 ?? icon, notificationsWarning || phoneNumberWarning || passwordWarning ? "!" : otherAccountsBadge, accountTabBarAvatar != nil, presentationData.reduceMotion) } - self.tabBarItemDisposable = (tabBarItem |> deliverOnMainQueue).start(next: { [weak self] title, image, selectedImage, badgeValue, isAvatar in + self.tabBarItemDisposable = (tabBarItem |> deliverOnMainQueue).start(next: { [weak self] title, image, selectedImage, badgeValue, isAvatar, reduceMotion in if let strongSelf = self { strongSelf.tabBarItem.title = title strongSelf.tabBarItem.image = image strongSelf.tabBarItem.selectedImage = selectedImage - strongSelf.tabBarItem.animationName = isAvatar ? nil : "TabSettings" + strongSelf.tabBarItem.animationName = isAvatar || reduceMotion ? nil : "TabSettings" strongSelf.tabBarItem.ringSelection = isAvatar strongSelf.tabBarItem.badgeValue = badgeValue }