From db2885d7a9e5e04d78365074e3f0b78cec349e9b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 5 Jan 2022 00:52:00 +0300 Subject: [PATCH] Various Improvements --- .../Telegram-iOS/Resources/ClearCache.tgs | Bin 0 -> 9474 bytes .../Telegram-iOS/en.lproj/Localizable.strings | 3 + .../Sources/AnimatedStickerNode.swift | 14 +- .../Sources/AnimationRenderer.swift | 2 +- .../Sources/SoftwareAnimationRenderer.swift | 20 +- .../Sources/CalendarMessageScreen.swift | 2 +- .../Sources/CallListController.swift | 1 + .../Sources/ChatListController.swift | 1 + .../Sources/ChatListControllerNode.swift | 4 +- .../Sources/ContactsController.swift | 1 + .../Sources/DebugController.swift | 1 - .../Source/ActionSheetController.swift | 11 + .../Source/ActionSheetControllerNode.swift | 6 +- .../Source/ActionSheetItemGroupNode.swift | 31 ++ .../ActionSheetItemGroupsContainerNode.swift | 4 + .../Display/Source/TabBarController.swift | 449 +---------------- .../Display/Source/TabBarTapRecognizer.swift | 83 ---- submodules/Display/Source/TextNode.swift | 20 +- submodules/Display/Source/ToolbarNode.swift | 42 +- .../Display/Source/ViewController.swift | 2 +- .../Sources/ItemListControllerNode.swift | 2 +- submodules/Postbox/Sources/MediaBox.swift | 76 ++- .../StorageUsageController.swift | 249 +++++++--- .../Sources/SlotMachineAnimationNode.swift | 4 +- submodules/TabBarUI/BUILD | 23 + .../Sources}/TabBarContollerNode.swift | 13 +- .../TabBarUI/Sources/TabBarController.swift | 451 ++++++++++++++++++ .../Sources}/TabBarNode.swift | 125 +++-- .../Resources/CollectCacheUsageStats.swift | 2 +- .../Resources/TelegramEngineResources.swift | 2 +- .../Sources/ChatMessageBubbleImages.swift | 8 +- .../Sources/ComponentsThemes.swift | 4 +- .../Sources/DefaultDayPresentationTheme.swift | 2 +- .../PresentationThemeEssentialGraphics.swift | 31 +- submodules/TelegramUI/BUILD | 1 + .../Resources/Animations/TabCalls.json | 1 + .../Resources/Animations/TabChats.json | 1 + .../Resources/Animations/TabContacts.json | 1 + .../Resources/Animations/TabSettings.json | 1 + .../TelegramUI/Sources/AppDelegate.swift | 2 +- .../ChatMessageAnimatedStickerItemNode.swift | 8 +- .../Sources/ManagedDiceAnimationNode.swift | 2 +- .../PeerInfoScreenLabeledValueItem.swift | 4 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 9 +- .../Sources/TelegramRootController.swift | 7 +- .../Sources/ChatTextInputAttributes.swift | 90 ++-- submodules/Translate/Sources/Translate.swift | 2 +- .../UINavigationItem+Proxy.h | 3 +- .../UINavigationItem+Proxy.m | 14 +- .../Sources/WallpaperBackgroundNode.swift | 57 +-- 50 files changed, 1081 insertions(+), 811 deletions(-) create mode 100644 Telegram/Telegram-iOS/Resources/ClearCache.tgs delete mode 100644 submodules/Display/Source/TabBarTapRecognizer.swift create mode 100644 submodules/TabBarUI/BUILD rename submodules/{Display/Source => TabBarUI/Sources}/TabBarContollerNode.swift (91%) create mode 100644 submodules/TabBarUI/Sources/TabBarController.swift rename submodules/{Display/Source => TabBarUI/Sources}/TabBarNode.swift (87%) create mode 100644 submodules/TelegramUI/Resources/Animations/TabCalls.json create mode 100644 submodules/TelegramUI/Resources/Animations/TabChats.json create mode 100644 submodules/TelegramUI/Resources/Animations/TabContacts.json create mode 100644 submodules/TelegramUI/Resources/Animations/TabSettings.json diff --git a/Telegram/Telegram-iOS/Resources/ClearCache.tgs b/Telegram/Telegram-iOS/Resources/ClearCache.tgs new file mode 100644 index 0000000000000000000000000000000000000000..ff361db5b8401d5e3dfd758f97390294c02ecd4d GIT binary patch literal 9474 zcmV+dCH>kTiwFP!000021MNL)j~us=f5q6(Jp`**eEC}tO4kDp&_e&EBK-{0Nm8{MCELY`U5a(hS4bl zTL^h2BHwJ7iY|^JnA?5*T^@VK(1bMZsSVWVn7pqFw0`Q=dj9XP_n&@!ccAaDvKbrg!p0Qr|+_a6K5 zX75r0{IB4F*^5#3t zqV1i8(AT^b7k05bEPi4m9&;o5_(q_=(=`&P03^{BXasaP8J~$(umowrV2M_UN?>G* zP}dcqZYVb`H=lIi3}=*dZGQ(R z`Um*+-@mO;lZgpOYZov;i#s24#8VIg$PdFTw+gMARcP3YMhunq4>4k+77^Su~V z#E|vwC-bvR+i%1HWHDKPjTfU4paLuA;HaE;y>SJVUedyWSv=Y(kT}Ler2}sLfm+r}9H(6#0-^)Fh93 z;CF1ZF}5{=mL06{90w~dHCWt&Hu+gkX8zGS;1FB+a2;}@J%jP0XE|Q>xbY&bf@ieI zDs*=16aYoy)j_@--NOU6F~}gtM%)Y9rNzrKQJ8jKk(HM>;`hFa_2gF|i^hhgR7!3(t4V4mcE7KV}Dfkk4bx%qnKa2mGB^ zw)H#LOUh(nB1QzuU(je}9eF0M4j9E@tb;>O77zfBb*6$o-<$gLhbd1kbGWe>2^bbf z{1ehI2#D4l1}od@hJF9XFCRWUdq3`}W=}H_y0{a$2#la4Dn#=}fGFLTR-9a%hKh(e9)7ilL3T z7>gsF%j3OpA3%FDDllqg#eb$_?&aH~%3(A3>=uF@eY{@`n!#tcFth-tsS~l6;SJnDNDU zIF0=ix+}ZQL~DgK%8DCRa;THLUxn7Gnyi{mc`G+fih1BaFks^p#W*ll%BvDS5mUs+ z#)@l1UUDY2fl9M{@Ki~@jYZ&%*hbvxFvzSA$j{3>$z5W$s79kmTqRn96Kr>h0YX4! zWZdLE!wnn0#NVz9Q22WP&pqSt(F=s#C6L&JB^-oT35m9`%1GP`c%J%WhKcV>#00}F z5fmGf473oGbptBN>nScKXTwVafGMrNZHMo0GmHlMl%CEbVS> z%FjWq>{jKc-z!%6d0bDf@^jF=f2-TKUfZ}C`8hxS*r)ho1m|@M^wsGwS zgQjTQ0>zxB!jA<+R;+!9{1r2ZpgkIaJAoL$S>s&v{x)Ngvwz{CSmcdqeHZ9k-$lAI z?Y*#vy%)ZEFD{CYhOKGWdTNO0Kok8Ow%ty3_dKuR@jc0Sw1#}oa~i`arrprKdA6!Z zi{afXU8f@so&BmH=6*rU!`Yalwxu!eO^*v;-fwiyz}!8;?USAiiFoA6Mm!n0pIdZg z=$m$Bn45NGn45NG=&N^SNHP03RCovm3XfGf7nyA}9<8E-eT~P^P~#!K?FJeTC=0RI zcvvN~eqZ5{=xt3&yKz~^5N6gts(OxAe#Mc(pfjmOO5%U})8OYSfe zMY`mE(B*8Fq-7%OWtg;94Gsx6TuXEdqMOsh5ookG?Nwai(Un1dxmO{EuxHuVWAXge~ zzZ}}(T(tYD%*D{|EsOJ`-6vd}j<$Q4$!o$z^-@$XMfFltFU1*3j)YgWN=_e^#XxmR zP8XKN+GeNZba7df8k{fV!f}_eNt)|~+AG!gL5gci1=XX~UQuCPR;Dv%l+I7&&zKH! zR|Tj5XkR``>>6*OmgDDRcla)`^-Q^WJZ92Ilq=ZlCmA@D9XF-D`;=NDtql z>%!f%>muH?>muH?>%v{V>q5@Ah&kXaYdpqhr7s;!wvmI$M0tr`lq{{Zw+*Ws7Gp}h z7fut6#?n>+N=-=n5Xd2(<)LUh_MXl07RiXx^>~`nFQ2k>i$Vf6h z>r}yZdE|0ZmpFvIvz_K+^F&L&O|LhpaZEvXS&8!4zt_S%Z)K^-N-=AUNF6;P%9&e& zV+W-ukthR@G~1{E<>UYg_HdyB;y(C7WU5g<&P$FU=2c1PYnY@#euhkL$h8pC=_bPf zKv~8hB!YES3wU7V065n0SRKugg$TK70k_%#9CWT)V4QO8`6mwYi*hgu*du%+mFr47 zQYuAft#MTa`GBXU@?B}SPNnFq)wZf6AMhJhzANoysuZ2I?pKxM1757kch%)pU0(CJ zyux9ZmpRJiHSO~X2VGcjAmN4%zTpof+~|W&uRWkjxUj-vr(VRf&-wr=ebxspch-kJ z8|}{hS?;V4y%^dEXCHCWvH42Q`Up!}@#6z740wX$E@)8~hJ65g{{+Wf=q0h2J?KFf zhVj?u!U(nC_#+GA&DQIOT5tU5ElL%igFCC6l^(&e-}6yJ+w(6e4Ye@t$riRMIFFmyp1CIVPhK1V@&fW8`J#B#xy@? z?&tg^9pm{`JiEHF1*>?{f>k{2`I!6Sm~S&c7pUS1=hrVaZe>+Gug9zwZ`!ODZ`!OD zuimUyi=NDxGg@oy9I)F}wkvJqs}!9zd$mgP0V`tVyV54dO3_*KXsaY2uxVDls|L_& z0G+`Ain|8T_iuiCD-C~sbN}u)Sy9c%2zvjpjil38_jT1yKkW_|(Oq>h$$p>Ef{$*g z%gi)VBmggIDHekXpcqhq<)ET+P}y=&`EpQ&Vo;$j1{Fd%sLVc8>BN#z4pAc)+GZe%`Ka*e^++rrcNmvcTmLq&D z+J@EWI6@0W&6O5WX-a`FMj~|hO~ZPebiqog<0rU5@6E+%xo0|D;W)f_l%x=rXC?aZ zV4Z~4c(}@<6`$#Fjg$MI%XsnB{!9mqp6P(u8FOtkvZ35yZs6 zCiYCA&VrgKMXh1aGr&X*1qBW-N6SCYLCb zaR8RT#BpF9d2OQ(7{zg*gF{aiP)@Nt(s2N~tmh#4GmOPZz_2)KBqCjYF;k#*x9KeS z^ZloPelFxJFj=EDZPP#e?Zu1l{`lh$fBnmkFAfDuZvlM39!V2Zf!kS*IliNoo}rMI_Mi_-?2cD+MC-GBP@ zvAlOke8-c{A*YWwmhUyv`vNX$B$j}rHws?_N^kPL3>GQ^i!B0+F9J&_0ZVU4y$F`x zOnMnCW*3&B;5&V$cSlY&-*@hGGekS{7~X-|w2$g1B$~`y|6TT{oHmGdM;huh{kdrdk!YSr4TfyA(&^E?EvmhuGnfAh&Klta(`t!Ua>=xWRHiUE@2b*xxzcENb%?f7T^OnnZlp$x z2*ndonj#E9N^PGa+r%?-xN46N^u=k*6*{RmAa*GniF!V%h2@i6FVs?xGp6U6S+n4K zev0InnbWOn&|Xb{wP5-wFhQa$#hzvv%{YRS=kH`xLl)(2fH2QKGKouROfQkHC?=ss zDua4>2-4~4eeC<5A|{dw-lSmZ2xDSyAf$q(@|7JV)AVEjd3q(&*$p29^U-m)>lk;t zig9=A8TYV`aSy8)_qd*M2Uy3zkCPm)bmRe7H1xERp{I2WyvyP#sCmMR`+bunmj|5(1TglvJ2vES9pV28WB-J}~EYOEx=rc*@t4``VR;v6;!snFzuP~HKg++WL68!~Hx8-)5W zW=`y+WX1tAMhe0+*3^P5Nm5evkwJFoOB*Lr^0tIX1xY&=BFj<8Mxt@7Ha)-~+1WYI z+NG0Kh7b!wS?3z^ZG;m@t;vuW&BR#@6|3M3yDrXf>*S2ELe2>5;*7XX&KMgzR>2y3 z<(;maH^wHA)i4LGkvV8(%wbo`9Da?=;aA2SVV%seUu0?xT8ds5bBqlwt6>haM&>Xp zV-C6k=9nEf5vc`0%%hhWpdQGe` z9shFutYKEk8fI0jK~K*b2Sa6+8|q!7jF~jbn7NHIJibxDbCe6r2Tfv()5Z_YcUsHI znvN+-6LoW#GEOLys_ruh9LWir496S>88q4AC^>?VD%cx2?#wAYERYkFhg?YqQvA{o zWG^QKgvKz4L69bfJ>kc+Frj?djAcy^Dv^Ku5Yvc>RI==90b8u%P1v_te%{V2UcxmvK1GyZsp z2vu-ymXsxvYP%UZtMM?@VmypFY$=R+TSiZFKM+++1&R4xBXLU%wDVNl`ojHq6Ga*! zhQo9=%)q%+7o@VtJ0M$TXPd2`j#`+3JK7j-JeWBwFzetByEg7{E9H)`M(zk}d_Gsy(dU9wyOP{%l0?VgZ zuOtc5$A;mPsf3vUlSMO9Dp|z{8%p)0D4B`nFBd?LJG1GmEuMLj0J?$QSrRry1Rb{$ za{i;|HHSqlB-^j#GRi?$RuM`s#--U*WRW0EXu_M^07=FqZ1Xsk4svXx+|5%6Ds(*W zSr+1S!P#WRcEq!0f?pwzX@nH-mpU@6fJ?-jabZ}my;LEf9b!ghk+DFle9i+!MTRFx z6O_^MY--BWHjxukHT*F-#fBYu&>xPR%tIM?Ry2gs<>Y&?tAw5D(@G` z``BAh1^yy|rz-HJ1)i$F7ZrG01-_ub`zr9I1>RSIFDme%3Vb<%-z%0_1^!Zj-}8S~ zfxk%L5vsr!6nIkwzO=xbD)0}|bi#vPg*a2^)!FAFUzJ0B>Re-}9NviQBKz4EJ(7 zK>50BuO|Y@PP{uOT|^JP-I+g6X~*{M1fCMlZU=Fqo#A2ZJ)ExwAqx{x12i%3yB!^* z!`vH%GP@fuhhS}wif8`Xo)DjZ>@F^kchM1rcmZSK+3+kvv1k`Py-w_6NYXt5d>hKu zBzHCKziw2U;lb_g$Tsc2ZiL&ucWB>AYb&v3`{;=8XX~OT{-}!~ad$8*5FnGMq;3V~ z9ZagWjVf4|Pa7l2K$N23c4t{dOtqhMw5cW)>3pCXhV9XAa;94<)kHp*VB#fF_<^^L zyWw}|u6IJZV;3^OvY}ymGKA^bg@c8?HDs|BL|?pi=!;j4efiS`SBi}%IX*DyIJ^To zc^>EQsa}*Duc5pt2j26^jBM;I%f*RZ?5x0hRCr$N#f*LQ*I~v;wn0A4CffE1WYT@?xB%z@v+{KsES$V966m2cHuQpL=|5E!tgBy^9fe zT}}Vy?b183&@PH);ANWZqF6?;^7=)wVNoKk4 ztMj%jg(s^j0m8C)_WHy^INIT)w-AnQIO{Ehryovx3*i}t^WH*u#^JoT5T0>3?=6ET z>fyY%44w$Xd2b;+aX9ZSgr^zKdm}s-vpHode8RrW;d_){3WI# zo!q*>8*>-OKcB|WTL#37oxZnWq({ALlKAP}n_pl5^x+tzM2q_-v~?{(VVEoL`Xp;d zC+ay~#ip_YBH9v!5)|pJb+L_T=^okSXEyyqU0qCxfdFLu!~!WlP4aWe?~@ByjDZ&N z!3qIqC6iVNHY=)bgsg8X)bmzYwZeYE}7K2U65?4@eaNR*dUq9YG8US!0{&HY#?medM>S-tJ z*~hoi*{b#Ntw4WgFIw<2Sh}tHWvy5`8Ka3-ToLN}P@E<>xIN-hMapG+%a8`J z3~lvbUVp=S_4Xv_FLuZtR z`EuK$MDLl#8Wo9VNo_l=hoNC)Oo4CL6Ic-xZ!18mFHkOnVWp%CD3?x31*;6NDjU8Y zsLjQS6dH;C8l-ihLKMT6RX)c(N#3y7kd)1*SAdkeNZq90n5--*Y6@d-?*S)DXBmyPE`|p*LI7NX-H#RTN(<`5OH!sK7eYK%- z&5lQ)a?L~;^?jwGa?M^gWaXTF`ow*$p>ob%ere^LiAwJKT0`ZWy^P(;Is5d%eXXH# z&R!1mEpX06sONLktk18y9RNHJD|o3tm8zWwGGL;2GBmpV={=mBjSV@jl)pZHkS7aN z-!=@XwjpW~mz08BA{@Q5t|nq%nuv|N0U+<+y?Oikrw{m05Xgv7!0hg&0==9~5TQse z_D=DHq z7qC#oxhWKJPGus_Z6PYVGE@$Qs2t10LTf4%xbDgXu7~~5Y|>8?nr$X`=5WH!eB*%? zRo!q$c?#YF+%sRP;QRtsNVU@JkyyHFvqnp@qKa?R< zWXB!LXzZ8n;jl>Z;d9?`U)oMMNaeGpl1q2W^Y6INnoq7_$4&b??%{6RKVk>xJ*<~A z1f>}7fgK<~RE}^y8@aJeesQ4%Ik~7aoO`DryBPTo@6w^YE|{$)oj35>=d*m1zis%A zv_x^+e#AK1&daI9=xv8x@~2ViC>=_h+uPpUjPGuwfuuq3Gi0EYVP2bvVEIeDNH%$q zq7E37k1Ll_0ZfPK9(lr&E`PLa$Yq5Y7CFj9j-(QYK|rj|t;?NTHVckvoBrW%FJ652 z#~*+A>tB9+aW@j?{V8TdG7O#-THlzc73Pw|dj#Y?iyIMfxnpIpVaA^Oh?h3}B=v-| zAGNY5ZgPz1g2)Z*or_>+mc`C4i=A5*yRa;FaZ&6L7R3&68oS+{;1-hM*6Fn6z)f0I z%?W$FO>l~M|7Mf}nyczOOeK1U>5TR66y%^{q$VgsAr-RoJX0HmG>p{Zt(y;x)T+Za zX(dRJKhF=xK`B7X%NsQ=Ds585J0q>ehQ!E=57h!Z25!4fxckidr-yqYw(Y5)d39mX z4F4ak+Xl@!&=v{)-|kk4QpM)TGz1FHLP_Fk5KjesIK9s zx4#e7JbV0!=f_tM>#TqCR1MqES6~_RXjC@ZxfJB%hon`I9I{S< zZLpFGzL5(lDrp0xBACWt>(|(LNn{H7S#U~w(`+m$mkwG41yPcnSR2Y(Q&z65QG}4* z;U~!EbSRfH4kVR{nvG+9ES1)GpfsiP*I$wXLvkEBUy$xdiPhf*6)0>~GN;f)CMhXv zT?Uz{3zF9E2#VSya$-s?@tyiPL2#Gy)U?7FCkdXsDlNgCjp_V%9}W{d zL5R(kc6>$N3=Q(=wpcJkL6Wu}BDsmm+m>BIN;4y?#9c1=cytlT#|DW?^7BYOVNI!% z#fg)}tLmbyF4`kEYOmyp4{v^dcb_G0XV=yiqwWayT9bMG#}|M4!w=lk{kxz4lOMVJ z`=^heU+cSv&Ga%d@^6ZQgL~V(2{RG%86(nu3l1Pj)CmhP84D>(aA3cARgiDUXUG!@ U0y)dJ-wwh51F+xhfeI!908@5W%K!iX literal 0 HcmV?d00001 diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index c88d9f4750..a39bf2409a 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7228,3 +7228,6 @@ Sorry for the inconvenience."; "Contacts.Sort" = "Sort"; "Contacts.Sort.ByName" = "by Name"; "Contacts.Sort.ByLastSeen" = "by Last Seen"; + +"ClearCache.Progress" = "Clearing the Cache • %d%"; +"ClearCache.KeepOpenedDescription" = "Please keep this window open until the clearing is completed."; diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift index 2b0f4cd38b..e72e210770 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift @@ -869,6 +869,9 @@ public final class AnimatedStickerNode: ASDisplayNode { public var isPlayingChanged: (Bool) -> Void = { _ in } + private var overlayColor: (UIColor?, Bool)? = nil + private var size: CGSize? + override public init() { self.queue = sharedQueue self.eventsNode = AnimatedStickerNodeDisplayEvents() @@ -900,10 +903,13 @@ public final class AnimatedStickerNode: ASDisplayNode { self.renderer = SoftwareAnimationRenderer() //self.renderer = MetalAnimationRenderer() #endif - self.renderer?.frame = CGRect(origin: CGPoint(), size: self.bounds.size) + self.renderer?.frame = CGRect(origin: CGPoint(), size: self.size ?? self.bounds.size) if let contents = self.nodeToCopyFrameFrom?.renderer?.contents { self.renderer?.contents = contents } + if let (overlayColor, replace) = self.overlayColor { + self.renderer?.setOverlayColor(overlayColor, replace: replace, animated: false) + } self.nodeToCopyFrameFrom = nil self.addSubnode(self.renderer!) } @@ -1347,10 +1353,12 @@ public final class AnimatedStickerNode: ASDisplayNode { } public func updateLayout(size: CGSize) { + self.size = size self.renderer?.frame = CGRect(origin: CGPoint(), size: size) } - public func setOverlayColor(_ color: UIColor?, animated: Bool) { - self.renderer?.setOverlayColor(color, animated: animated) + public func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) { + self.overlayColor = (color, replace) + self.renderer?.setOverlayColor(color, replace: replace, animated: animated) } } diff --git a/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift index 13b1a19aa4..bee24a8bc8 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift @@ -10,5 +10,5 @@ public enum AnimationRendererFrameType { protocol AnimationRenderer { func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) - func setOverlayColor(_ color: UIColor?, animated: Bool) + func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) } diff --git a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift index 80d97d7407..eddb2d65f0 100644 --- a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift +++ b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift @@ -8,7 +8,8 @@ import YuvConversion final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer { private var highlightedContentNode: ASDisplayNode? private var highlightedColor: UIColor? - + private var highlightReplacesContent = false + func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) { assert(bytesPerRow > 0) queue.async { [weak self] in @@ -53,20 +54,29 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer { } strongSelf.contents = image?.cgImage strongSelf.updateHighlightedContentNode() + if strongSelf.highlightedContentNode?.frame != strongSelf.bounds { + strongSelf.highlightedContentNode?.frame = strongSelf.bounds + } completion() } } } private func updateHighlightedContentNode() { - guard let highlightedContentNode = self.highlightedContentNode, let highlightedColor = self.highlightedColor, let contents = self.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID else { + guard let highlightedContentNode = self.highlightedContentNode, let highlightedColor = self.highlightedColor else { return } - (highlightedContentNode.view as! UIImageView).image = UIImage(cgImage: contents as! CGImage).withRenderingMode(.alwaysTemplate) + if let contents = self.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID { + (highlightedContentNode.view as! UIImageView).image = UIImage(cgImage: contents as! CGImage).withRenderingMode(.alwaysTemplate) + } highlightedContentNode.tintColor = highlightedColor + if self.highlightReplacesContent { + self.contents = nil + } } - - func setOverlayColor(_ color: UIColor?, animated: Bool) { + + func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) { + self.highlightReplacesContent = replace var updated = false if let current = self.highlightedColor, let color = color { updated = !current.isEqual(color) diff --git a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift index dee5e42818..2603dfcd91 100644 --- a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift +++ b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift @@ -1330,7 +1330,7 @@ public final class CalendarMessageScreen: ViewController { selectionToolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: Toolbar(leftAction: nil, rightAction: nil, middleAction: ToolbarAction(title: toolbarText, isEnabled: true, color: .custom(self.selectionState?.dayRange != nil ? self.presentationData.theme.list.itemDestructiveColor : self.presentationData.theme.list.itemDisabledTextColor))), transition: transition) } else { selectionToolbarNode = ToolbarNode( - theme: TabBarControllerTheme( + theme: ToolbarTheme( rootControllerTheme: self.presentationData.theme), displaySeparator: true, left: { diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 0941143254..7d9e4b84e0 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -115,6 +115,7 @@ public final class CallListController: TelegramBaseController { self.tabBarItem.title = self.presentationData.strings.Calls_TabTitle self.tabBarItem.image = icon self.tabBarItem.selectedImage = icon + self.tabBarItem.animationName = "TabCalls" } self.segmentedTitleView.indexUpdated = { [weak self] index in diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index ffdbc52d92..25daec2156 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -206,6 +206,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.tabBarItem.image = icon self.tabBarItem.selectedImage = icon + self.tabBarItem.animationName = "TabChats" let leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) leftBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Edit diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 5c9e5f0d3f..2ff420759d 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -1116,7 +1116,7 @@ final class ChatListControllerNode: ASDisplayNode { self.searchDisplayController?.updatePresentationData(presentationData) if let toolbarNode = self.toolbarNode { - toolbarNode.updateTheme(TabBarControllerTheme(rootControllerTheme: self.presentationData.theme)) + toolbarNode.updateTheme(ToolbarTheme(rootControllerTheme: self.presentationData.theme)) } } @@ -1149,7 +1149,7 @@ final class ChatListControllerNode: ASDisplayNode { transition.updateFrame(node: toolbarNode, frame: tabBarFrame) toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition) } else { - let toolbarNode = ToolbarNode(theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme), displaySeparator: true, left: { [weak self] in + let toolbarNode = ToolbarNode(theme: ToolbarTheme(rootControllerTheme: self.presentationData.theme), displaySeparator: true, left: { [weak self] in self?.toolbarActionSelected?(.left) }, right: { [weak self] in self?.toolbarActionSelected?(.right) diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index 410e37d9f0..6aa57f983a 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -192,6 +192,7 @@ public class ContactsController: ViewController { self.tabBarItem.image = icon self.tabBarItem.selectedImage = icon + self.tabBarItem.animationName = "TabContacts" self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index cd01fdb089..dc121da0b3 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -940,7 +940,6 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay)) entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers)) entries.append(.experimentalBackground(experimentalSettings.experimentalBackground)) - entries.append(.snow(experimentalSettings.snow)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) } diff --git a/submodules/Display/Source/ActionSheetController.swift b/submodules/Display/Source/ActionSheetController.swift index 5d0659cb0d..a4d62aa976 100644 --- a/submodules/Display/Source/ActionSheetController.swift +++ b/submodules/Display/Source/ActionSheetController.swift @@ -1,5 +1,10 @@ import Foundation import UIKit +import AsyncDisplayKit + +public protocol ActionSheetGroupOverlayNode: ASDisplayNode { + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) +} open class ActionSheetController: ViewController, PresentableController, StandalonePresentableController { private var actionSheetNode: ActionSheetControllerNode { @@ -83,4 +88,10 @@ open class ActionSheetController: ViewController, PresentableController, Standal self.actionSheetNode.updateItem(groupIndex: groupIndex, itemIndex: itemIndex, f) } } + + public func setItemGroupOverlayNode(groupIndex: Int, node: ActionSheetGroupOverlayNode) { + if self.isViewLoaded { + self.actionSheetNode.setItemGroupOverlayNode(groupIndex: groupIndex, node: node) + } + } } diff --git a/submodules/Display/Source/ActionSheetControllerNode.swift b/submodules/Display/Source/ActionSheetControllerNode.swift index b53bb2ba1f..a1331ad307 100644 --- a/submodules/Display/Source/ActionSheetControllerNode.swift +++ b/submodules/Display/Source/ActionSheetControllerNode.swift @@ -219,7 +219,11 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate { self.itemGroupsContainerNode.setGroups(groups) } - public func updateItem(groupIndex: Int, itemIndex: Int, _ f: (ActionSheetItem) -> ActionSheetItem) { + func updateItem(groupIndex: Int, itemIndex: Int, _ f: (ActionSheetItem) -> ActionSheetItem) { self.itemGroupsContainerNode.updateItem(groupIndex: groupIndex, itemIndex: itemIndex, f) } + + func setItemGroupOverlayNode(groupIndex: Int, node: ActionSheetGroupOverlayNode) { + self.itemGroupsContainerNode.setItemGroupOverlayNode(groupIndex: groupIndex, node: node) + } } diff --git a/submodules/Display/Source/ActionSheetItemGroupNode.swift b/submodules/Display/Source/ActionSheetItemGroupNode.swift index bc1cadd002..edb551e46f 100644 --- a/submodules/Display/Source/ActionSheetItemGroupNode.swift +++ b/submodules/Display/Source/ActionSheetItemGroupNode.swift @@ -18,6 +18,8 @@ final class ActionSheetItemGroupNode: ASDisplayNode, UIScrollViewDelegate { private var validLayout: CGSize? + private var overlayNode: ActionSheetGroupOverlayNode? + init(theme: ActionSheetControllerTheme) { self.theme = theme @@ -66,6 +68,31 @@ final class ActionSheetItemGroupNode: ASDisplayNode, UIScrollViewDelegate { self.addSubnode(self.clippingNode) } + func setOverlayNode(_ overlayNode: ActionSheetGroupOverlayNode?) { + guard self.overlayNode == nil else { + return + } + + let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) + overlayNode?.alpha = 0.0 + + self.overlayNode = overlayNode + if let overlayNode = overlayNode { + transition.updateAlpha(node: overlayNode, alpha: 1.0) + self.clippingNode.addSubnode(overlayNode) + } else if let overlayNode = self.overlayNode { + overlayNode.removeFromSupernode() + } + + if let size = self.validLayout, let overlayNode = overlayNode { + overlayNode.updateLayout(size: size, transition: .immediate) + } + + for node in self.itemNodes { + transition.updateAlpha(node: node, alpha: 0.0) + } + } + func updateItemNodes(_ nodes: [ActionSheetItemNode], leadingVisibleNodeCount: CGFloat = 1000.0) { for node in self.itemNodes { if !nodes.contains(where: { $0 === node }) { @@ -140,6 +167,10 @@ final class ActionSheetItemGroupNode: ASDisplayNode, UIScrollViewDelegate { self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: -scrollViewContentInsets.top) } + if let overlayNode = self.overlayNode { + overlayNode.updateLayout(size: size, transition: transition) + } + self.updateOverscroll(size: size, transition: transition) return size diff --git a/submodules/Display/Source/ActionSheetItemGroupsContainerNode.swift b/submodules/Display/Source/ActionSheetItemGroupsContainerNode.swift index 966abcc311..aab21e81e1 100644 --- a/submodules/Display/Source/ActionSheetItemGroupsContainerNode.swift +++ b/submodules/Display/Source/ActionSheetItemGroupsContainerNode.swift @@ -112,4 +112,8 @@ final class ActionSheetItemGroupsContainerNode: ASDisplayNode { self.groups[groupIndex] = ActionSheetItemGroup(items: groupItems) } + + func setItemGroupOverlayNode(groupIndex: Int, node: ActionSheetGroupOverlayNode) { + self.groupNodes[groupIndex].setOverlayNode(node) + } } diff --git a/submodules/Display/Source/TabBarController.swift b/submodules/Display/Source/TabBarController.swift index 6f10b84788..23d940d4ac 100644 --- a/submodules/Display/Source/TabBarController.swift +++ b/submodules/Display/Source/TabBarController.swift @@ -3,442 +3,25 @@ import UIKit import AsyncDisplayKit import SwiftSignalKit -public final class TabBarControllerTheme { - public let backgroundColor: UIColor - public let tabBarBackgroundColor: UIColor - public let tabBarSeparatorColor: UIColor - public let tabBarIconColor: UIColor - public let tabBarSelectedIconColor: UIColor - public let tabBarTextColor: UIColor - public let tabBarSelectedTextColor: UIColor - public let tabBarBadgeBackgroundColor: UIColor - public let tabBarBadgeStrokeColor: UIColor - public let tabBarBadgeTextColor: UIColor - public let tabBarExtractedIconColor: UIColor - public let tabBarExtractedTextColor: UIColor - - public init(backgroundColor: UIColor, tabBarBackgroundColor: UIColor, tabBarSeparatorColor: UIColor, tabBarIconColor: UIColor, tabBarSelectedIconColor: UIColor, tabBarTextColor: UIColor, tabBarSelectedTextColor: UIColor, tabBarBadgeBackgroundColor: UIColor, tabBarBadgeStrokeColor: UIColor, tabBarBadgeTextColor: UIColor, tabBarExtractedIconColor: UIColor, tabBarExtractedTextColor: UIColor) { - self.backgroundColor = backgroundColor - self.tabBarBackgroundColor = tabBarBackgroundColor - self.tabBarSeparatorColor = tabBarSeparatorColor - self.tabBarIconColor = tabBarIconColor - self.tabBarSelectedIconColor = tabBarSelectedIconColor - self.tabBarTextColor = tabBarTextColor - self.tabBarSelectedTextColor = tabBarSelectedTextColor - self.tabBarBadgeBackgroundColor = tabBarBadgeBackgroundColor - self.tabBarBadgeStrokeColor = tabBarBadgeStrokeColor - self.tabBarBadgeTextColor = tabBarBadgeTextColor - self.tabBarExtractedIconColor = tabBarExtractedIconColor - self.tabBarExtractedTextColor = tabBarExtractedTextColor - } +public enum TabBarItemSwipeDirection { + case left + case right } -public final class TabBarItemInfo: NSObject { - public let previewing: Bool +public protocol TabBarController: ViewController { + var currentController: ViewController? { get } + var controllers: [ViewController] { get } + var selectedIndex: Int { get set } - public init(previewing: Bool) { - self.previewing = previewing - - super.init() - } + func setControllers(_ controllers: [ViewController], selectedIndex: Int?) - override public func isEqual(_ object: Any?) -> Bool { - if let object = object as? TabBarItemInfo { - if self.previewing != object.previewing { - return false - } - return true - } else { - return false - } - } + func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) - public static func ==(lhs: TabBarItemInfo, rhs: TabBarItemInfo) -> Bool { - if lhs.previewing != rhs.previewing { - return false - } - return true - } -} - -public enum TabBarContainedControllerPresentationUpdate { - case dismiss - case present - case progress(CGFloat) -} - -public protocol TabBarContainedController { - func presentTabBarPreviewingController(sourceNodes: [ASDisplayNode]) - func updateTabBarPreviewingControllerPresentation(_ update: TabBarContainedControllerPresentationUpdate) -} - -open class TabBarController: ViewController { - private var validLayout: ContainerViewLayout? - - private var tabBarControllerNode: TabBarControllerNode { - get { - return super.displayNode as! TabBarControllerNode - } - } - - open override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { - for controller in self.controllers { - controller.updateNavigationCustomData(data, progress: progress, transition: transition) - } - } - - public private(set) var controllers: [ViewController] = [] - - private let _ready = Promise() - override open var ready: Promise { - return self._ready - } - - private var _selectedIndex: Int? - public var selectedIndex: Int { - get { - if let _selectedIndex = self._selectedIndex { - return _selectedIndex - } else { - return 0 - } - } set(value) { - let index = max(0, min(self.controllers.count - 1, value)) - if _selectedIndex != index { - _selectedIndex = index - - self.updateSelectedIndex() - } - } - } - - var currentController: ViewController? - - private let pendingControllerDisposable = MetaDisposable() - - private var theme: TabBarControllerTheme - - public init(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) { - self.theme = theme - - super.init(navigationBarPresentationData: nil) - - self.scrollToTop = { [weak self] in - guard let strongSelf = self else { - return - } - if let controller = strongSelf.currentController { - controller.scrollToTop?() - } - } - } - - required public init(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - deinit { - self.pendingControllerDisposable.dispose() - } - - public func updateTheme(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) { - if self.theme !== theme { - self.theme = theme - if self.isNodeLoaded { - self.tabBarControllerNode.updateTheme(theme) - } - } - } - - private var debugTapCounter: (Double, Int) = (0.0, 0) - - public func sourceNodesForController(at index: Int) -> [ASDisplayNode]? { - return self.tabBarControllerNode.tabBarNode.sourceNodesForController(at: index) - } - - public func frameForControllerTab(controller: ViewController) -> CGRect? { - if let index = self.controllers.firstIndex(of: controller) { - return self.tabBarControllerNode.tabBarNode.frameForControllerTab(at: index).flatMap { self.tabBarControllerNode.tabBarNode.view.convert($0, to: self.view) } - } else { - return nil - } - } - - public func isPointInsideContentArea(point: CGPoint) -> Bool { - if point.y < self.tabBarControllerNode.tabBarNode.frame.minY { - return true - } - return false - } - - public func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) { - self.tabBarControllerNode.updateIsTabBarEnabled(value, transition: transition) - } - - public func updateIsTabBarHidden(_ value: Bool, transition: ContainedViewLayoutTransition) { - self.tabBarControllerNode.tabBarHidden = value - if let layout = self.validLayout { - self.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .slide)) - } - } - - override open func loadDisplayNode() { - self.displayNode = TabBarControllerNode(theme: self.theme, itemSelected: { [weak self] index, longTap, itemNodes in - if let strongSelf = self { - if longTap, let controller = strongSelf.controllers[index] as? TabBarContainedController { - controller.presentTabBarPreviewingController(sourceNodes: itemNodes) - return - } - - if strongSelf.selectedIndex == index { - let timestamp = CACurrentMediaTime() - if strongSelf.debugTapCounter.0 < timestamp - 0.4 { - strongSelf.debugTapCounter.0 = timestamp - strongSelf.debugTapCounter.1 = 0 - } - - if strongSelf.debugTapCounter.0 >= timestamp - 0.4 { - strongSelf.debugTapCounter.0 = timestamp - strongSelf.debugTapCounter.1 += 1 - } - - if strongSelf.debugTapCounter.1 >= 10 { - strongSelf.debugTapCounter.1 = 0 - - strongSelf.controllers[index].tabBarItemDebugTapAction?() - } - } - if let validLayout = strongSelf.validLayout { - var updatedLayout = validLayout - - var tabBarHeight: CGFloat - var options: ContainerViewLayoutInsetOptions = [] - if validLayout.metrics.widthClass == .regular { - options.insert(.input) - } - let bottomInset: CGFloat = validLayout.insets(options: options).bottom - if !validLayout.safeInsets.left.isZero { - tabBarHeight = 34.0 + bottomInset - } else { - tabBarHeight = 49.0 + bottomInset - } - updatedLayout.intrinsicInsets.bottom = tabBarHeight - - strongSelf.controllers[index].containerLayoutUpdated(updatedLayout, transition: .immediate) - } - let startTime = CFAbsoluteTimeGetCurrent() - strongSelf.pendingControllerDisposable.set((strongSelf.controllers[index].ready.get() - |> deliverOnMainQueue).start(next: { _ in - if let strongSelf = self { - let readyTime = CFAbsoluteTimeGetCurrent() - startTime - if readyTime > 0.5 { - print("TabBarController: controller took \(readyTime) to become ready") - } - - if strongSelf.selectedIndex == index { - if let controller = strongSelf.currentController { - if longTap { - controller.longTapWithTabBar?() - } else { - controller.scrollToTopWithTabBar?() - } - } - } else { - strongSelf.selectedIndex = index - } - } - })) - } - }, contextAction: { [weak self] index, node, gesture in - guard let strongSelf = self else { - return - } - if index >= 0 && index < strongSelf.controllers.count { - strongSelf.controllers[index].tabBarItemContextAction(sourceNode: node, gesture: gesture) - } - }, swipeAction: { [weak self] index, direction in - guard let strongSelf = self else { - return - } - if index >= 0 && index < strongSelf.controllers.count { - strongSelf.controllers[index].tabBarItemSwipeAction(direction: direction) - } - }, toolbarActionSelected: { [weak self] action in - self?.currentController?.toolbarActionSelected(action: action) - }, disabledPressed: { [weak self] in - self?.currentController?.tabBarDisabledAction() - }) - - self.updateSelectedIndex() - self.displayNodeDidLoad() - } - - public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { - let alpha = max(0.0, min(1.0, alpha)) - transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.backgroundNode, alpha: alpha, delay: 0.15) - transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.separatorNode, alpha: alpha, delay: 0.15) - } - - private func updateSelectedIndex() { - if !self.isNodeLoaded { - return - } - - self.tabBarControllerNode.tabBarNode.selectedIndex = self.selectedIndex - - if let currentController = self.currentController { - currentController.willMove(toParent: nil) - self.tabBarControllerNode.currentControllerNode = nil - currentController.removeFromParent() - currentController.didMove(toParent: nil) - - self.currentController = nil - } - - if let _selectedIndex = self._selectedIndex, _selectedIndex < self.controllers.count { - self.currentController = self.controllers[_selectedIndex] - } - - if let currentController = self.currentController { - currentController.willMove(toParent: self) - self.tabBarControllerNode.currentControllerNode = currentController.displayNode - self.addChild(currentController) - currentController.didMove(toParent: self) - - currentController.displayNode.recursivelyEnsureDisplaySynchronously(true) - self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle - } else { - } - - if let layout = self.validLayout { - self.containerLayoutUpdated(layout, transition: .immediate) - } - } - - public func updateLayout(transition: ContainedViewLayoutTransition = .immediate) { - if let layout = self.validLayout { - self.containerLayoutUpdated(layout, transition: transition) - } - } - - override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { - super.containerLayoutUpdated(layout, transition: transition) - - self.validLayout = layout - - self.tabBarControllerNode.containerLayoutUpdated(layout, toolbar: self.currentController?.toolbar, transition: transition) - - if let currentController = self.currentController { - currentController.view.frame = CGRect(origin: CGPoint(), size: layout.size) - - var updatedLayout = layout - - var tabBarHeight: CGFloat - var options: ContainerViewLayoutInsetOptions = [] - if updatedLayout.metrics.widthClass == .regular { - options.insert(.input) - } - let bottomInset: CGFloat = updatedLayout.insets(options: options).bottom - if !updatedLayout.safeInsets.left.isZero { - tabBarHeight = 34.0 + bottomInset - } else { - tabBarHeight = 49.0 + bottomInset - } - updatedLayout.intrinsicInsets.bottom = tabBarHeight - - currentController.containerLayoutUpdated(updatedLayout, transition: transition) - } - } - - override open func navigationStackConfigurationUpdated(next: [ViewController]) { - super.navigationStackConfigurationUpdated(next: next) - for controller in self.controllers { - controller.navigationStackConfigurationUpdated(next: next) - } - } - - override open func viewWillDisappear(_ animated: Bool) { - if let currentController = self.currentController { - currentController.viewWillDisappear(animated) - } - } - - override open func viewWillAppear(_ animated: Bool) { - if let currentController = self.currentController { - currentController.viewWillAppear(animated) - } - } - - override open func viewDidAppear(_ animated: Bool) { - if let currentController = self.currentController { - currentController.viewDidAppear(animated) - } - } - - override open func viewDidDisappear(_ animated: Bool) { - if let currentController = self.currentController { - currentController.viewDidDisappear(animated) - } - } - - public func setControllers(_ controllers: [ViewController], selectedIndex: Int?) { - var updatedSelectedIndex: Int? = selectedIndex - if updatedSelectedIndex == nil, let selectedIndex = self._selectedIndex, selectedIndex < self.controllers.count { - if let index = controllers.firstIndex(where: { $0 === self.controllers[selectedIndex] }) { - updatedSelectedIndex = index - } else { - updatedSelectedIndex = 0 - } - } - self.controllers = controllers - self.tabBarControllerNode.tabBarNode.tabBarItems = self.controllers.map({ TabBarNodeItem(item: $0.tabBarItem, contextActionType: $0.tabBarItemContextActionType) }) - - let signals = combineLatest(self.controllers.map({ $0.tabBarItem }).map { tabBarItem -> Signal in - if let tabBarItem = tabBarItem, tabBarItem.image == nil { - return Signal { [weak tabBarItem] subscriber in - let index = tabBarItem?.addSetImageListener({ image in - if image != nil { - subscriber.putNext(true) - subscriber.putCompletion() - } - }) - return ActionDisposable { - Queue.mainQueue().async { - if let index = index { - tabBarItem?.removeSetImageListener(index) - } - } - } - } - |> runOn(.mainQueue()) - } else { - return .single(true) - } - }) - |> map { items -> Bool in - for item in items { - if !item { - return false - } - } - return true - } - |> filter { $0 } - |> take(1) - - let allReady = signals - |> deliverOnMainQueue - |> mapToSignal { _ -> Signal in - // wait for tab bar items to be applied - return .single(true) - |> delay(0.0, queue: Queue.mainQueue()) - } - - self._ready.set(allReady) - - if let updatedSelectedIndex = updatedSelectedIndex { - self.selectedIndex = updatedSelectedIndex - self.updateSelectedIndex() - } - } + func frameForControllerTab(controller: ViewController) -> CGRect? + func isPointInsideContentArea(point: CGPoint) -> Bool + func sourceNodesForController(at index: Int) -> [ASDisplayNode]? + + func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) + func updateIsTabBarHidden(_ value: Bool, transition: ContainedViewLayoutTransition) + func updateLayout(transition: ContainedViewLayoutTransition) } diff --git a/submodules/Display/Source/TabBarTapRecognizer.swift b/submodules/Display/Source/TabBarTapRecognizer.swift deleted file mode 100644 index 616ced31e5..0000000000 --- a/submodules/Display/Source/TabBarTapRecognizer.swift +++ /dev/null @@ -1,83 +0,0 @@ -import Foundation -import UIKit -import SwiftSignalKit - -final class TabBarTapRecognizer: UIGestureRecognizer { - private let tap: (CGPoint) -> Void - private let longTap: (CGPoint) -> Void - - private var initialLocation: CGPoint? - private var longTapTimer: SwiftSignalKit.Timer? - - init(tap: @escaping (CGPoint) -> Void, longTap: @escaping (CGPoint) -> Void) { - self.tap = tap - self.longTap = longTap - - super.init(target: nil, action: nil) - } - - override func reset() { - super.reset() - - self.initialLocation = nil - self.longTapTimer?.invalidate() - self.longTapTimer = nil - } - - override func touchesBegan(_ touches: Set, with event: UIEvent) { - super.touchesBegan(touches, with: event) - - if self.initialLocation == nil { - self.initialLocation = touches.first?.location(in: self.view) - let longTapTimer = SwiftSignalKit.Timer(timeout: 0.4, repeat: false, completion: { [weak self] in - guard let strongSelf = self else { - return - } - if let initialLocation = strongSelf.initialLocation { - strongSelf.initialLocation = nil - strongSelf.longTap(initialLocation) - strongSelf.state = .ended - } - }, queue: Queue.mainQueue()) - self.longTapTimer?.invalidate() - self.longTapTimer = longTapTimer - longTapTimer.start() - } - } - - override func touchesEnded(_ touches: Set, with event: UIEvent) { - super.touchesEnded(touches, with: event) - - if let initialLocation = self.initialLocation { - self.initialLocation = nil - self.longTapTimer?.invalidate() - self.longTapTimer = nil - self.tap(initialLocation) - self.state = .ended - } - } - - override func touchesMoved(_ touches: Set, with event: UIEvent) { - super.touchesMoved(touches, with: event) - - if let initialLocation = self.initialLocation, let location = touches.first?.location(in: self.view) { - let deltaX = initialLocation.x - location.x - let deltaY = initialLocation.y - location.y - if deltaX * deltaX + deltaY * deltaY > 4.0 { - self.longTapTimer?.invalidate() - self.longTapTimer = nil - self.initialLocation = nil - self.state = .failed - } - } - } - - override func touchesCancelled(_ touches: Set, with event: UIEvent) { - super.touchesCancelled(touches, with: event) - - self.initialLocation = nil - self.longTapTimer?.invalidate() - self.longTapTimer = nil - self.state = .failed - } -} diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 34b564e1c9..1f28523c04 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -1059,9 +1059,17 @@ public class TextNode: ASDisplayNode { let tokenString = "\u{2026}" let truncatedTokenString = NSAttributedString(string: tokenString, attributes: truncationTokenAttributes) let truncationToken = CTLineCreateWithAttributedString(truncatedTokenString) - + coreTextLine = CTLineCreateTruncatedLine(originalLine, Double(lineConstrainedSize.width), truncationType, truncationToken) ?? truncationToken - brokenLineRange.length = CTLineGetGlyphCount(coreTextLine) - 1 + let runs = (CTLineGetGlyphRuns(coreTextLine) as [AnyObject]) as! [CTRun] + for run in runs { + let runAttributes: NSDictionary = CTRunGetAttributes(run) + if let _ = runAttributes["CTForegroundColorFromContext"] { + brokenLineRange.length = CTRunGetStringRange(run).location + break + } + } +// brokenLineRange.length = CTLineGetGlyphCount(coreTextLine) - 1 if brokenLineRange.location + brokenLineRange.length > attributedString.length { brokenLineRange.length = attributedString.length - brokenLineRange.location } @@ -1326,7 +1334,9 @@ public class TextNode: ASDisplayNode { context.saveGState() var clipRects: [CGRect] = [] for spoiler in line.spoilerWords { - clipRects.append(spoiler.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)) + var spoilerClipRect = spoiler.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY) + spoilerClipRect.size.height += 1.0 + UIScreenPixel + clipRects.append(spoilerClipRect) } context.clip(to: clipRects) } @@ -1361,7 +1371,9 @@ public class TextNode: ASDisplayNode { context.restoreGState() } else { for spoiler in line.spoilerWords { - clearRects.append(spoiler.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)) + var spoilerClearRect = spoiler.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY) + spoilerClearRect.size.height += 1.0 + UIScreenPixel + clearRects.append(spoilerClearRect) } } } diff --git a/submodules/Display/Source/ToolbarNode.swift b/submodules/Display/Source/ToolbarNode.swift index d9ae99e453..d1fb9e5fed 100644 --- a/submodules/Display/Source/ToolbarNode.swift +++ b/submodules/Display/Source/ToolbarNode.swift @@ -2,8 +2,28 @@ import Foundation import UIKit import AsyncDisplayKit +public enum ToolbarActionOption { + case left + case right + case middle +} + +public final class ToolbarTheme { + public let barBackgroundColor: UIColor + public let barSeparatorColor: UIColor + public let barTextColor: UIColor + public let barSelectedTextColor: UIColor + + public init(barBackgroundColor: UIColor, barSeparatorColor: UIColor, barTextColor: UIColor, barSelectedTextColor: UIColor) { + self.barBackgroundColor = barBackgroundColor + self.barSeparatorColor = barSeparatorColor + self.barTextColor = barTextColor + self.barSelectedTextColor = barSelectedTextColor + } +} + public final class ToolbarNode: ASDisplayNode { - private var theme: TabBarControllerTheme + private var theme: ToolbarTheme private let displaySeparator: Bool public var left: () -> Void public var right: () -> Void @@ -18,14 +38,14 @@ public final class ToolbarNode: ASDisplayNode { private let middleTitle: ImmediateTextNode private let middleButton: HighlightTrackingButtonNode - public init(theme: TabBarControllerTheme, displaySeparator: Bool = false, left: @escaping () -> Void = {}, right: @escaping () -> Void = {}, middle: @escaping () -> Void = {}) { + public init(theme: ToolbarTheme, displaySeparator: Bool = false, left: @escaping () -> Void = {}, right: @escaping () -> Void = {}, middle: @escaping () -> Void = {}) { self.theme = theme self.displaySeparator = displaySeparator self.left = left self.right = right self.middle = middle - self.backgroundNode = NavigationBackgroundNode(color: theme.tabBarBackgroundColor) + self.backgroundNode = NavigationBackgroundNode(color: theme.barBackgroundColor) self.separatorNode = ASDisplayNode() self.separatorNode.isLayerBacked = true @@ -100,9 +120,9 @@ public final class ToolbarNode: ASDisplayNode { self.middleButton.accessibilityTraits = .button } - public func updateTheme(_ theme: TabBarControllerTheme) { - self.separatorNode.backgroundColor = theme.tabBarSeparatorColor - self.backgroundNode.updateColor(color: theme.tabBarBackgroundColor, transition: .immediate) + public func updateTheme(_ theme: ToolbarTheme) { + self.separatorNode.backgroundColor = theme.barSeparatorColor + self.backgroundNode.updateColor(color: theme.barBackgroundColor, transition: .immediate) } public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, bottomInset: CGFloat, toolbar: Toolbar, transition: ContainedViewLayoutTransition) { @@ -112,10 +132,10 @@ public final class ToolbarNode: ASDisplayNode { var sideInset: CGFloat = 16.0 - self.leftTitle.attributedText = NSAttributedString(string: toolbar.leftAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.leftAction?.isEnabled ?? false) ? self.theme.tabBarSelectedTextColor : self.theme.tabBarTextColor) + self.leftTitle.attributedText = NSAttributedString(string: toolbar.leftAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.leftAction?.isEnabled ?? false) ? self.theme.barSelectedTextColor : self.theme.barTextColor) self.leftButton.accessibilityLabel = toolbar.leftAction?.title - self.rightTitle.attributedText = NSAttributedString(string: toolbar.rightAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.rightAction?.isEnabled ?? false) ? self.theme.tabBarSelectedTextColor : self.theme.tabBarTextColor) + self.rightTitle.attributedText = NSAttributedString(string: toolbar.rightAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.rightAction?.isEnabled ?? false) ? self.theme.barSelectedTextColor : self.theme.barTextColor) self.rightButton.accessibilityLabel = toolbar.rightAction?.title let middleColor: UIColor @@ -123,15 +143,15 @@ public final class ToolbarNode: ASDisplayNode { if middleAction.isEnabled { switch middleAction.color { case .accent: - middleColor = self.theme.tabBarSelectedTextColor + middleColor = self.theme.barSelectedTextColor case let .custom(color): middleColor = color } } else { - middleColor = self.theme.tabBarTextColor + middleColor = self.theme.barTextColor } } else { - middleColor = self.theme.tabBarTextColor + middleColor = self.theme.barTextColor } self.middleTitle.attributedText = NSAttributedString(string: toolbar.middleAction?.title ?? "", font: Font.regular(17.0), textColor: middleColor) self.middleButton.accessibilityLabel = toolbar.middleAction?.title diff --git a/submodules/Display/Source/ViewController.swift b/submodules/Display/Source/ViewController.swift index 3eb5c4492d..f53077c4c8 100644 --- a/submodules/Display/Source/ViewController.swift +++ b/submodules/Display/Source/ViewController.swift @@ -191,7 +191,7 @@ public enum TabBarItemContextActionType { public let statusBar: StatusBar public let navigationBar: NavigationBar? - private(set) var toolbar: Toolbar? + public private(set) var toolbar: Toolbar? public var displayNavigationBar = true open var navigationBarRequiresEntireLayoutUpdate: Bool { diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index ddf544ac87..5e22f4f52e 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -501,7 +501,7 @@ open class ItemListControllerNode: ASDisplayNode { transition.updateFrame(node: toolbarNode, frame: toolbarFrame) toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: transition) } else if let theme = self.theme { - let toolbarNode = ToolbarNode(theme: TabBarControllerTheme(rootControllerTheme: theme), displaySeparator: true) + let toolbarNode = ToolbarNode(theme: ToolbarTheme(rootControllerTheme: theme), displaySeparator: true) toolbarNode.frame = toolbarFrame toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: .immediate) self.addSubnode(toolbarNode) diff --git a/submodules/Postbox/Sources/MediaBox.swift b/submodules/Postbox/Sources/MediaBox.swift index ca4b2a57de..9d98b5b0f3 100644 --- a/submodules/Postbox/Sources/MediaBox.swift +++ b/submodules/Postbox/Sources/MediaBox.swift @@ -1244,7 +1244,7 @@ public final class MediaBox { } } - public func removeOtherCachedResources(paths: [String]) -> Signal { + public func removeOtherCachedResources(paths: [String]) -> Signal { return Signal { subscriber in self.dataQueue.async { var keepPrefixes: [String] = [] @@ -1254,14 +1254,31 @@ public final class MediaBox { keepPrefixes.append(resourcePaths.complete) } + var count: Int = 0 + let totalCount = paths.count + if totalCount == 0 { + subscriber.putNext(1.0) + subscriber.putCompletion() + return + } + + let reportProgress: (Int) -> Void = { count in + Queue.mainQueue().async { + subscriber.putNext(min(1.0, Float(count) / Float(totalCount))) + } + } + outer: for path in paths { for prefix in keepPrefixes { if path.starts(with: prefix) { + count += 1 continue outer } } + count += 1 unlink(self.basePath + "/" + path) + reportProgress(count) } subscriber.putCompletion() } @@ -1269,27 +1286,10 @@ public final class MediaBox { } } - public func removeCachedResources(_ ids: Set, force: Bool = false) -> Signal { + public func removeCachedResources(_ ids: Set, force: Bool = false) -> Signal { return Signal { subscriber in self.dataQueue.async { - for id in ids { - if !force { - if self.fileContexts[id] != nil { - continue - } - if self.keepResourceContexts[id] != nil { - continue - } - } - let paths = self.storePathsForId(id) - unlink(paths.complete) - unlink(paths.partial) - unlink(paths.partial + ".meta") - self.fileContexts.removeValue(forKey: id) - } - let uniqueIds = Set(ids.map { $0.stringRepresentation }) - var pathsToDelete: [String] = [] for cacheType in ["cache", "short-cache"] { @@ -1309,8 +1309,46 @@ public final class MediaBox { } } + var count: Int = 0 + let totalCount = ids.count * 3 + pathsToDelete.count + if totalCount == 0 { + subscriber.putNext(1.0) + subscriber.putCompletion() + return + } + + let reportProgress: (Int) -> Void = { count in + Queue.mainQueue().async { + subscriber.putNext(min(1.0, Float(count) / Float(totalCount))) + } + } + + for id in ids { + if !force { + if self.fileContexts[id] != nil { + count += 3 + reportProgress(count) + continue + } + if self.keepResourceContexts[id] != nil { + count += 3 + reportProgress(count) + continue + } + } + let paths = self.storePathsForId(id) + unlink(paths.complete) + unlink(paths.partial) + unlink(paths.partial + ".meta") + self.fileContexts.removeValue(forKey: id) + count += 3 + reportProgress(count) + } + for path in pathsToDelete { unlink(path) + count += 1 + reportProgress(count) } subscriber.putCompletion() diff --git a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift index 893a90de24..ba29ef4915 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift @@ -1,6 +1,7 @@ import Foundation import UIKit import Display +import AsyncDisplayKit import SwiftSignalKit import Postbox import TelegramCore @@ -14,6 +15,8 @@ import AccountContext import ItemListPeerItem import DeleteChatPeerActionSheetItem import UndoUI +import AnimatedStickerNode +import TelegramAnimatedStickerNode private func totalDiskSpace() -> Int64 { do { @@ -275,15 +278,15 @@ private enum StorageUsageEntry: ItemListNodeEntry { } } -private struct StoragUsageState: Equatable { +private struct StorageUsageState: Equatable { let peerIdWithRevealedOptions: PeerId? - func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> StoragUsageState { - return StoragUsageState(peerIdWithRevealedOptions: peerIdWithRevealedOptions) + func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> StorageUsageState { + return StorageUsageState(peerIdWithRevealedOptions: peerIdWithRevealedOptions) } } -private func storageUsageControllerEntries(presentationData: PresentationData, cacheSettings: CacheStorageSettings, cacheStats: CacheUsageStatsResult?, state: StoragUsageState) -> [StorageUsageEntry] { +private func storageUsageControllerEntries(presentationData: PresentationData, cacheSettings: CacheStorageSettings, cacheStats: CacheUsageStatsResult?, state: StorageUsageState) -> [StorageUsageEntry] { var entries: [StorageUsageEntry] = [] entries.append(.keepMediaHeader(presentationData.theme, presentationData.strings.Cache_KeepMedia.uppercased())) @@ -398,9 +401,9 @@ func cacheUsageStats(context: AccountContext) -> Signal? = nil, isModal: Bool = false) -> ViewController { - let statePromise = ValuePromise(StoragUsageState(peerIdWithRevealedOptions: nil)) - let stateValue = Atomic(value: StoragUsageState(peerIdWithRevealedOptions: nil)) - let updateState: ((StoragUsageState) -> StoragUsageState) -> Void = { f in + let statePromise = ValuePromise(StorageUsageState(peerIdWithRevealedOptions: nil)) + let stateValue = Atomic(value: StorageUsageState(peerIdWithRevealedOptions: nil)) + let updateState: ((StorageUsageState) -> StorageUsageState) -> Void = { f in statePromise.set(stateValue.modify { f($0) }) } @@ -546,7 +549,9 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P selectedSize = totalSize if !items.isEmpty { - items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))").string, action: { + var cancelImpl: (() -> Void)? + + items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))").string, action: { [weak controller] in if let statsPromise = statsPromise { let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 }) @@ -582,20 +587,37 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P var updatedTempPaths = stats.tempPaths var updatedTempSize = stats.tempSize - var signal: Signal = context.engine.resources.clearCachedMediaResources(mediaResourceIds: clearResourceIds) + var signal: Signal = context.engine.resources.clearCachedMediaResources(mediaResourceIds: clearResourceIds) if otherSize.0 { - let removeTempFiles: Signal = Signal { subscriber in + let removeTempFiles: Signal = Signal { subscriber in let fileManager = FileManager.default + var count: Int = 0 + let totalCount = stats.tempPaths.count + + let reportProgress: (Int) -> Void = { count in + Queue.mainQueue().async { + subscriber.putNext(min(1.0, Float(count) / Float(totalCount))) + } + } + + if totalCount == 0 { + subscriber.putNext(1.0) + subscriber.putCompletion() + return EmptyDisposable + } + for path in stats.tempPaths { let _ = try? fileManager.removeItem(atPath: path) + count += 1 + reportProgress(count) } subscriber.putCompletion() return EmptyDisposable } |> runOn(Queue.concurrentDefaultQueue()) - signal = signal - |> then(context.account.postbox.mediaBox.removeOtherCachedResources(paths: stats.otherPaths)) - |> then(removeTempFiles) + signal = (signal |> map { $0 * 0.7 }) + |> then(context.account.postbox.mediaBox.removeOtherCachedResources(paths: stats.otherPaths) |> map { 0.7 + 0.2 * $0 }) + |> then(removeTempFiles |> map { 0.9 + 0.1 * $0 }) } if otherSize.0 { @@ -606,49 +628,39 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P updatedTempSize = 0 } + let progressPromise = ValuePromise(0.0) + let overlayNode = StorageUsageClearProgressOverlayNode(presentationData: presentationData) + overlayNode.setProgressSignal(progressPromise.get()) + controller?.setItemGroupOverlayNode(groupIndex: 0, node: overlayNode) + let resultStats = CacheUsageStats(media: media, mediaResourceIds: stats.mediaResourceIds, peers: stats.peers, otherSize: updatedOtherSize, otherPaths: updatedOtherPaths, cacheSize: updatedCacheSize, tempPaths: updatedTempPaths, tempSize: updatedTempSize, immutableSize: stats.immutableSize) - var cancelImpl: (() -> Void)? - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let progressSignal = Signal { subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.15, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() - - signal = signal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } cancelImpl = { clearDisposable.set(nil) resetStats() } + statsPromise.set(.single(.result(resultStats))) clearDisposable.set((signal - |> deliverOnMainQueue).start(completed: { + |> deliverOnMainQueue).start(next: { progress in + progressPromise.set(progress) + }, completed: { statsPromise.set(.single(.result(resultStats))) - presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))", stringForDeviceType()).string), elevatedLayout: false, action: { _ in return false }), .current, nil) + progressPromise.set(1.0) + Queue.mainQueue().after(1.0) { + dismissAction() + presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))", stringForDeviceType()).string), elevatedLayout: false, action: { _ in return false }), .current, nil) + } })) } - - dismissAction() })) controller.setItemGroups([ ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) - ]) + ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { + cancelImpl?() + dismissAction() + })]) + ]) presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } } @@ -743,7 +755,9 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P selectedSize = totalSize if !items.isEmpty { - items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))").string, action: { + var cancelImpl: (() -> Void)? + + items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))").string, action: { [weak controller] in if let statsPromise = statsPromise { let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 }) var clearMediaIds = Set() @@ -785,50 +799,39 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P } } - var signal = context.engine.resources.clearCachedMediaResources(mediaResourceIds: clearResourceIds) + let signal = context.engine.resources.clearCachedMediaResources(mediaResourceIds: clearResourceIds) + + let progressPromise = ValuePromise(0.0) + let overlayNode = StorageUsageClearProgressOverlayNode(presentationData: presentationData) + overlayNode.setProgressSignal(progressPromise.get()) + controller?.setItemGroupOverlayNode(groupIndex: 0, node: overlayNode) let resultStats = CacheUsageStats(media: media, mediaResourceIds: stats.mediaResourceIds, peers: stats.peers, otherSize: stats.otherSize, otherPaths: stats.otherPaths, cacheSize: stats.cacheSize, tempPaths: stats.tempPaths, tempSize: stats.tempSize, immutableSize: stats.immutableSize) - var cancelImpl: (() -> Void)? - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let progressSignal = Signal { subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.15, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() - - signal = signal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } cancelImpl = { clearDisposable.set(nil) resetStats() } clearDisposable.set((signal - |> deliverOnMainQueue).start(completed: { + |> deliverOnMainQueue).start(next: { progress in + progressPromise.set(progress) + }, completed: { statsPromise.set(.single(.result(resultStats))) - presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))", stringForDeviceType()).string), elevatedLayout: false, action: { _ in return false }), .current, nil) + progressPromise.set(1.0) + Queue.mainQueue().after(1.0) { + dismissAction() + presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))", stringForDeviceType()).string), elevatedLayout: false, action: { _ in return false }), .current, nil) + } })) } - - dismissAction() })) controller.setItemGroups([ ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) + ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { + cancelImpl?() + dismissAction() + })]) ]) presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } @@ -995,3 +998,105 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P } return controller } + +private class StorageUsageClearProgressOverlayNode: ASDisplayNode, ActionSheetGroupOverlayNode { + private let presentationData: PresentationData + + private let animationNode: AnimatedStickerNode + private let progressTextNode: ImmediateTextNode + private let descriptionTextNode: ImmediateTextNode + private let progressBackgroundNode: ASDisplayNode + private let progressForegroundNode: ASDisplayNode + + private let progressDisposable = MetaDisposable() + + private var validLayout: CGSize? + + init(presentationData: PresentationData) { + self.presentationData = presentationData + + self.animationNode = AnimatedStickerNode() + self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "ClearCache"), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) + self.animationNode.visibility = true + + self.progressTextNode = ImmediateTextNode() + self.progressTextNode.textAlignment = .center + + self.descriptionTextNode = ImmediateTextNode() + self.descriptionTextNode.textAlignment = .center + self.descriptionTextNode.maximumNumberOfLines = 0 + + self.progressBackgroundNode = ASDisplayNode() + self.progressBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.controlAccentColor.withMultipliedAlpha(0.2) + self.progressBackgroundNode.cornerRadius = 3.0 + + self.progressForegroundNode = ASDisplayNode() + self.progressForegroundNode.backgroundColor = self.presentationData.theme.actionSheet.controlAccentColor + self.progressForegroundNode.cornerRadius = 3.0 + + super.init() + + self.addSubnode(self.animationNode) + self.addSubnode(self.progressTextNode) + self.addSubnode(self.descriptionTextNode) + self.addSubnode(self.progressBackgroundNode) + self.addSubnode(self.progressForegroundNode) + } + + deinit { + self.progressDisposable.dispose() + } + + func setProgressSignal(_ signal: Signal) { + self.progressDisposable.set((signal + |> deliverOnMainQueue).start(next: { [weak self] progress in + if let strongSelf = self { + strongSelf.setProgress(progress) + } + })) + } + + private var progress: Float = 0.0 + private func setProgress(_ progress: Float) { + self.progress = progress + + if let size = self.validLayout { + self.updateLayout(size: size, transition: .animated(duration: 0.2, curve: .easeInOut)) + } + } + + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { + self.validLayout = size + + let inset: CGFloat = 24.0 + let progressHeight: CGFloat = 6.0 + let spacing: CGFloat = 16.0 + + let progressFrame = CGRect(x: inset, y: size.height - inset - progressHeight, width: size.width - inset * 2.0, height: progressHeight) + self.progressBackgroundNode.frame = progressFrame + let progressForegroundFrame = CGRect(x: inset, y: size.height - inset - progressHeight, width: floorToScreenPixels(progressFrame.width * CGFloat(self.progress)), height: progressHeight) + if !self.progressForegroundNode.frame.width.isZero { + transition.updateFrame(node: self.progressForegroundNode, frame: progressForegroundFrame) + } else { + self.progressForegroundNode.frame = progressForegroundFrame + } + + self.descriptionTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.ClearCache_KeepOpenedDescription, font: Font.regular(15.0), textColor: self.presentationData.theme.actionSheet.secondaryTextColor) + let descriptionTextSize = self.descriptionTextNode.updateLayout(CGSize(width: size.width - inset * 3.0, height: size.height)) + let descriptionTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - descriptionTextSize.width) / 2.0), y: progressFrame.minY - spacing - 9.0 - descriptionTextSize.height), size: descriptionTextSize) + self.descriptionTextNode.frame = descriptionTextFrame + + self.progressTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.ClearCache_Progress(Int(progress * 100.0)).string, font: Font.with(size: 17.0, design: .regular, weight: .bold, traits: [.monospacedNumbers]), textColor: self.presentationData.theme.actionSheet.primaryTextColor) + let progressTextSize = self.progressTextNode.updateLayout(size) + let progressTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - progressTextSize.width) / 2.0), y: descriptionTextFrame.minY - spacing - progressTextSize.height), size: progressTextSize) + self.progressTextNode.frame = progressTextFrame + + let availableHeight = progressTextFrame.minY + let imageSide = min(160.0, availableHeight - 30.0) + let imageSize = CGSize(width: imageSide, height: imageSide) + + let animationFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floorToScreenPixels((progressTextFrame.minY - imageSize.height) / 2.0)), size: imageSize) + self.animationNode.frame = animationFrame + self.animationNode.updateLayout(size: imageSize) + } +} diff --git a/submodules/SlotMachineAnimationNode/Sources/SlotMachineAnimationNode.swift b/submodules/SlotMachineAnimationNode/Sources/SlotMachineAnimationNode.swift index 41e3e0db1f..2f9bc789b7 100644 --- a/submodules/SlotMachineAnimationNode/Sources/SlotMachineAnimationNode.swift +++ b/submodules/SlotMachineAnimationNode/Sources/SlotMachineAnimationNode.swift @@ -246,7 +246,7 @@ public final class SlotMachineAnimationNode: ASDisplayNode { } } - public func setOverlayColor(_ color: UIColor?, animated: Bool) { + public func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) { } } @@ -352,6 +352,6 @@ class DiceAnimatedStickerNode: ASDisplayNode { self.animationNode.frame = self.bounds } - public func setOverlayColor(_ color: UIColor?, animated: Bool) { + public func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) { } } diff --git a/submodules/TabBarUI/BUILD b/submodules/TabBarUI/BUILD new file mode 100644 index 0000000000..1abbce2193 --- /dev/null +++ b/submodules/TabBarUI/BUILD @@ -0,0 +1,23 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "TabBarUI", + module_name = "TabBarUI", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", + "//submodules/AnimatedStickerNode:AnimatedStickerNode", + "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", + "//submodules/TelegramPresentationData:TelegramPresentationData", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/Display/Source/TabBarContollerNode.swift b/submodules/TabBarUI/Sources/TabBarContollerNode.swift similarity index 91% rename from submodules/Display/Source/TabBarContollerNode.swift rename to submodules/TabBarUI/Sources/TabBarContollerNode.swift index 93bf14dac1..39a56e395a 100644 --- a/submodules/Display/Source/TabBarContollerNode.swift +++ b/submodules/TabBarUI/Sources/TabBarContollerNode.swift @@ -1,11 +1,12 @@ import Foundation import UIKit import AsyncDisplayKit +import Display -public enum ToolbarActionOption { - case left - case right - case middle +private extension ToolbarTheme { + convenience init(tabBarTheme theme: TabBarControllerTheme) { + self.init(barBackgroundColor: theme.tabBarBackgroundColor, barSeparatorColor: theme.tabBarSeparatorColor, barTextColor: theme.tabBarTextColor, barSelectedTextColor: theme.tabBarSelectedTextColor) + } } final class TabBarControllerNode: ASDisplayNode { @@ -65,7 +66,7 @@ final class TabBarControllerNode: ASDisplayNode { self.tabBarNode.updateTheme(theme) self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5) - self.toolbarNode?.updateTheme(theme) + self.toolbarNode?.updateTheme(ToolbarTheme(tabBarTheme: theme)) } func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) { @@ -99,7 +100,7 @@ final class TabBarControllerNode: ASDisplayNode { transition.updateFrame(node: toolbarNode, frame: tabBarFrame) toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition) } else { - let toolbarNode = ToolbarNode(theme: self.theme, left: { [weak self] in + let toolbarNode = ToolbarNode(theme: ToolbarTheme(tabBarTheme: self.theme), left: { [weak self] in self?.toolbarActionSelected(.left) }, right: { [weak self] in self?.toolbarActionSelected(.right) diff --git a/submodules/TabBarUI/Sources/TabBarController.swift b/submodules/TabBarUI/Sources/TabBarController.swift new file mode 100644 index 0000000000..c91d0e70b2 --- /dev/null +++ b/submodules/TabBarUI/Sources/TabBarController.swift @@ -0,0 +1,451 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import SwiftSignalKit +import Display +import TelegramPresentationData + +public final class TabBarControllerTheme { + public let backgroundColor: UIColor + public let tabBarBackgroundColor: UIColor + public let tabBarSeparatorColor: UIColor + public let tabBarIconColor: UIColor + public let tabBarSelectedIconColor: UIColor + public let tabBarTextColor: UIColor + public let tabBarSelectedTextColor: UIColor + public let tabBarBadgeBackgroundColor: UIColor + public let tabBarBadgeStrokeColor: UIColor + public let tabBarBadgeTextColor: UIColor + public let tabBarExtractedIconColor: UIColor + public let tabBarExtractedTextColor: UIColor + + public init(backgroundColor: UIColor, tabBarBackgroundColor: UIColor, tabBarSeparatorColor: UIColor, tabBarIconColor: UIColor, tabBarSelectedIconColor: UIColor, tabBarTextColor: UIColor, tabBarSelectedTextColor: UIColor, tabBarBadgeBackgroundColor: UIColor, tabBarBadgeStrokeColor: UIColor, tabBarBadgeTextColor: UIColor, tabBarExtractedIconColor: UIColor, tabBarExtractedTextColor: UIColor) { + self.backgroundColor = backgroundColor + self.tabBarBackgroundColor = tabBarBackgroundColor + self.tabBarSeparatorColor = tabBarSeparatorColor + self.tabBarIconColor = tabBarIconColor + self.tabBarSelectedIconColor = tabBarSelectedIconColor + self.tabBarTextColor = tabBarTextColor + self.tabBarSelectedTextColor = tabBarSelectedTextColor + self.tabBarBadgeBackgroundColor = tabBarBadgeBackgroundColor + self.tabBarBadgeStrokeColor = tabBarBadgeStrokeColor + self.tabBarBadgeTextColor = tabBarBadgeTextColor + self.tabBarExtractedIconColor = tabBarExtractedIconColor + self.tabBarExtractedTextColor = tabBarExtractedTextColor + } + + public convenience init(rootControllerTheme: PresentationTheme) { + let theme = rootControllerTheme.rootController.tabBar + self.init(backgroundColor: rootControllerTheme.list.plainBackgroundColor, tabBarBackgroundColor: theme.backgroundColor, tabBarSeparatorColor: theme.separatorColor, tabBarIconColor: theme.iconColor, tabBarSelectedIconColor: theme.selectedIconColor, tabBarTextColor: theme.textColor, tabBarSelectedTextColor: theme.selectedTextColor, tabBarBadgeBackgroundColor: theme.badgeBackgroundColor, tabBarBadgeStrokeColor: theme.badgeStrokeColor, tabBarBadgeTextColor: theme.badgeTextColor, tabBarExtractedIconColor: rootControllerTheme.contextMenu.extractedContentTintColor, tabBarExtractedTextColor: rootControllerTheme.contextMenu.extractedContentTintColor) + } +} + +public final class TabBarItemInfo: NSObject { + public let previewing: Bool + + public init(previewing: Bool) { + self.previewing = previewing + + super.init() + } + + override public func isEqual(_ object: Any?) -> Bool { + if let object = object as? TabBarItemInfo { + if self.previewing != object.previewing { + return false + } + return true + } else { + return false + } + } + + public static func ==(lhs: TabBarItemInfo, rhs: TabBarItemInfo) -> Bool { + if lhs.previewing != rhs.previewing { + return false + } + return true + } +} + +public enum TabBarContainedControllerPresentationUpdate { + case dismiss + case present + case progress(CGFloat) +} + +public protocol TabBarContainedController { + func presentTabBarPreviewingController(sourceNodes: [ASDisplayNode]) + func updateTabBarPreviewingControllerPresentation(_ update: TabBarContainedControllerPresentationUpdate) +} + +open class TabBarControllerImpl: ViewController, TabBarController { + private var validLayout: ContainerViewLayout? + + private var tabBarControllerNode: TabBarControllerNode { + get { + return super.displayNode as! TabBarControllerNode + } + } + + open override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { + for controller in self.controllers { + controller.updateNavigationCustomData(data, progress: progress, transition: transition) + } + } + + public private(set) var controllers: [ViewController] = [] + + private let _ready = Promise() + override open var ready: Promise { + return self._ready + } + + private var _selectedIndex: Int? + public var selectedIndex: Int { + get { + if let _selectedIndex = self._selectedIndex { + return _selectedIndex + } else { + return 0 + } + } set(value) { + let index = max(0, min(self.controllers.count - 1, value)) + if _selectedIndex != index { + _selectedIndex = index + + self.updateSelectedIndex() + } + } + } + + public var currentController: ViewController? + + private let pendingControllerDisposable = MetaDisposable() + + private var theme: TabBarControllerTheme + + public init(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) { + self.theme = theme + + super.init(navigationBarPresentationData: nil) + + self.scrollToTop = { [weak self] in + guard let strongSelf = self else { + return + } + if let controller = strongSelf.currentController { + controller.scrollToTop?() + } + } + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.pendingControllerDisposable.dispose() + } + + public func updateTheme(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) { + if self.theme !== theme { + self.theme = theme + if self.isNodeLoaded { + self.tabBarControllerNode.updateTheme(theme) + } + } + } + + private var debugTapCounter: (Double, Int) = (0.0, 0) + + public func sourceNodesForController(at index: Int) -> [ASDisplayNode]? { + return self.tabBarControllerNode.tabBarNode.sourceNodesForController(at: index) + } + + public func frameForControllerTab(controller: ViewController) -> CGRect? { + if let index = self.controllers.firstIndex(of: controller) { + return self.tabBarControllerNode.tabBarNode.frameForControllerTab(at: index).flatMap { self.tabBarControllerNode.tabBarNode.view.convert($0, to: self.view) } + } else { + return nil + } + } + + public func isPointInsideContentArea(point: CGPoint) -> Bool { + if point.y < self.tabBarControllerNode.tabBarNode.frame.minY { + return true + } + return false + } + + public func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) { + self.tabBarControllerNode.updateIsTabBarEnabled(value, transition: transition) + } + + public func updateIsTabBarHidden(_ value: Bool, transition: ContainedViewLayoutTransition) { + self.tabBarControllerNode.tabBarHidden = value + if let layout = self.validLayout { + self.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .slide)) + } + } + + override open func loadDisplayNode() { + self.displayNode = TabBarControllerNode(theme: self.theme, itemSelected: { [weak self] index, longTap, itemNodes in + if let strongSelf = self { + if longTap, let controller = strongSelf.controllers[index] as? TabBarContainedController { + controller.presentTabBarPreviewingController(sourceNodes: itemNodes) + return + } + + if strongSelf.selectedIndex == index { + let timestamp = CACurrentMediaTime() + if strongSelf.debugTapCounter.0 < timestamp - 0.4 { + strongSelf.debugTapCounter.0 = timestamp + strongSelf.debugTapCounter.1 = 0 + } + + if strongSelf.debugTapCounter.0 >= timestamp - 0.4 { + strongSelf.debugTapCounter.0 = timestamp + strongSelf.debugTapCounter.1 += 1 + } + + if strongSelf.debugTapCounter.1 >= 10 { + strongSelf.debugTapCounter.1 = 0 + + strongSelf.controllers[index].tabBarItemDebugTapAction?() + } + } + if let validLayout = strongSelf.validLayout { + var updatedLayout = validLayout + + var tabBarHeight: CGFloat + var options: ContainerViewLayoutInsetOptions = [] + if validLayout.metrics.widthClass == .regular { + options.insert(.input) + } + let bottomInset: CGFloat = validLayout.insets(options: options).bottom + if !validLayout.safeInsets.left.isZero { + tabBarHeight = 34.0 + bottomInset + } else { + tabBarHeight = 49.0 + bottomInset + } + updatedLayout.intrinsicInsets.bottom = tabBarHeight + + strongSelf.controllers[index].containerLayoutUpdated(updatedLayout, transition: .immediate) + } + let startTime = CFAbsoluteTimeGetCurrent() + strongSelf.pendingControllerDisposable.set((strongSelf.controllers[index].ready.get() + |> deliverOnMainQueue).start(next: { _ in + if let strongSelf = self { + let readyTime = CFAbsoluteTimeGetCurrent() - startTime + if readyTime > 0.5 { + print("TabBarController: controller took \(readyTime) to become ready") + } + + if strongSelf.selectedIndex == index { + if let controller = strongSelf.currentController { + if longTap { + controller.longTapWithTabBar?() + } else { + controller.scrollToTopWithTabBar?() + } + } + } else { + strongSelf.selectedIndex = index + } + } + })) + } + }, contextAction: { [weak self] index, node, gesture in + guard let strongSelf = self else { + return + } + if index >= 0 && index < strongSelf.controllers.count { + strongSelf.controllers[index].tabBarItemContextAction(sourceNode: node, gesture: gesture) + } + }, swipeAction: { [weak self] index, direction in + guard let strongSelf = self else { + return + } + if index >= 0 && index < strongSelf.controllers.count { + strongSelf.controllers[index].tabBarItemSwipeAction(direction: direction) + } + }, toolbarActionSelected: { [weak self] action in + self?.currentController?.toolbarActionSelected(action: action) + }, disabledPressed: { [weak self] in + self?.currentController?.tabBarDisabledAction() + }) + + self.updateSelectedIndex() + self.displayNodeDidLoad() + } + + public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { + let alpha = max(0.0, min(1.0, alpha)) + transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.backgroundNode, alpha: alpha, delay: 0.15) + transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.separatorNode, alpha: alpha, delay: 0.15) + } + + private func updateSelectedIndex() { + if !self.isNodeLoaded { + return + } + + self.tabBarControllerNode.tabBarNode.selectedIndex = self.selectedIndex + + if let currentController = self.currentController { + currentController.willMove(toParent: nil) + self.tabBarControllerNode.currentControllerNode = nil + currentController.removeFromParent() + currentController.didMove(toParent: nil) + + self.currentController = nil + } + + if let _selectedIndex = self._selectedIndex, _selectedIndex < self.controllers.count { + self.currentController = self.controllers[_selectedIndex] + } + + if let currentController = self.currentController { + currentController.willMove(toParent: self) + self.tabBarControllerNode.currentControllerNode = currentController.displayNode + self.addChild(currentController) + currentController.didMove(toParent: self) + + currentController.displayNode.recursivelyEnsureDisplaySynchronously(true) + self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle + } else { + } + + if let layout = self.validLayout { + self.containerLayoutUpdated(layout, transition: .immediate) + } + } + + public func updateLayout(transition: ContainedViewLayoutTransition = .immediate) { + if let layout = self.validLayout { + self.containerLayoutUpdated(layout, transition: transition) + } + } + + override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + self.validLayout = layout + + self.tabBarControllerNode.containerLayoutUpdated(layout, toolbar: self.currentController?.toolbar, transition: transition) + + if let currentController = self.currentController { + currentController.view.frame = CGRect(origin: CGPoint(), size: layout.size) + + var updatedLayout = layout + + var tabBarHeight: CGFloat + var options: ContainerViewLayoutInsetOptions = [] + if updatedLayout.metrics.widthClass == .regular { + options.insert(.input) + } + let bottomInset: CGFloat = updatedLayout.insets(options: options).bottom + if !updatedLayout.safeInsets.left.isZero { + tabBarHeight = 34.0 + bottomInset + } else { + tabBarHeight = 49.0 + bottomInset + } + updatedLayout.intrinsicInsets.bottom = tabBarHeight + + currentController.containerLayoutUpdated(updatedLayout, transition: transition) + } + } + + override open func navigationStackConfigurationUpdated(next: [ViewController]) { + super.navigationStackConfigurationUpdated(next: next) + for controller in self.controllers { + controller.navigationStackConfigurationUpdated(next: next) + } + } + + override open func viewWillDisappear(_ animated: Bool) { + if let currentController = self.currentController { + currentController.viewWillDisappear(animated) + } + } + + override open func viewWillAppear(_ animated: Bool) { + if let currentController = self.currentController { + currentController.viewWillAppear(animated) + } + } + + override open func viewDidAppear(_ animated: Bool) { + if let currentController = self.currentController { + currentController.viewDidAppear(animated) + } + } + + override open func viewDidDisappear(_ animated: Bool) { + if let currentController = self.currentController { + currentController.viewDidDisappear(animated) + } + } + + public func setControllers(_ controllers: [ViewController], selectedIndex: Int?) { + var updatedSelectedIndex: Int? = selectedIndex + if updatedSelectedIndex == nil, let selectedIndex = self._selectedIndex, selectedIndex < self.controllers.count { + if let index = controllers.firstIndex(where: { $0 === self.controllers[selectedIndex] }) { + updatedSelectedIndex = index + } else { + updatedSelectedIndex = 0 + } + } + self.controllers = controllers + self.tabBarControllerNode.tabBarNode.tabBarItems = self.controllers.map({ TabBarNodeItem(item: $0.tabBarItem, contextActionType: $0.tabBarItemContextActionType) }) + + let signals = combineLatest(self.controllers.map({ $0.tabBarItem }).map { tabBarItem -> Signal in + if let tabBarItem = tabBarItem, tabBarItem.image == nil { + return Signal { [weak tabBarItem] subscriber in + let index = tabBarItem?.addSetImageListener({ image in + if image != nil { + subscriber.putNext(true) + subscriber.putCompletion() + } + }) + return ActionDisposable { + Queue.mainQueue().async { + if let index = index { + tabBarItem?.removeSetImageListener(index) + } + } + } + } + |> runOn(.mainQueue()) + } else { + return .single(true) + } + }) + |> map { items -> Bool in + for item in items { + if !item { + return false + } + } + return true + } + |> filter { $0 } + |> take(1) + + let allReady = signals + |> deliverOnMainQueue + |> mapToSignal { _ -> Signal in + // wait for tab bar items to be applied + return .single(true) + |> delay(0.0, queue: Queue.mainQueue()) + } + + self._ready.set(allReady) + + if let updatedSelectedIndex = updatedSelectedIndex { + self.selectedIndex = updatedSelectedIndex + self.updateSelectedIndex() + } + } +} diff --git a/submodules/Display/Source/TabBarNode.swift b/submodules/TabBarUI/Sources/TabBarNode.swift similarity index 87% rename from submodules/Display/Source/TabBarNode.swift rename to submodules/TabBarUI/Sources/TabBarNode.swift index 190f22e9a1..432914be9f 100644 --- a/submodules/Display/Source/TabBarNode.swift +++ b/submodules/TabBarUI/Sources/TabBarNode.swift @@ -2,7 +2,10 @@ import Foundation import UIKit import AsyncDisplayKit import SwiftSignalKit +import Display import UIKitRuntimeUtils +import AnimatedStickerNode +import TelegramAnimatedStickerNode private let separatorHeight: CGFloat = 1.0 / UIScreen.main.scale private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor: UIColor, tintColor: UIColor, horizontal: Bool, imageMode: Bool, centered: Bool = false) -> (UIImage, CGFloat) { @@ -41,35 +44,24 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor: context.fill(CGRect(origin: CGPoint(), size: size)) if let image = image, imageMode { + let imageRect: CGRect if horizontal { - let imageRect = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - imageSize.height) / 2.0)), size: imageSize) - context.saveGState() - context.translateBy(x: imageRect.midX, y: imageRect.midY) - context.scaleBy(x: 1.0, y: -1.0) - context.translateBy(x: -imageRect.midX, y: -imageRect.midY) - if image.renderingMode == .alwaysOriginal { - context.draw(image.cgImage!, in: imageRect) - } else { - context.clip(to: imageRect, mask: image.cgImage!) - context.setFillColor(tintColor.cgColor) - context.fill(imageRect) - } - context.restoreGState() + imageRect = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - imageSize.height) / 2.0)), size: imageSize) } else { - let imageRect = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - imageSize.width) / 2.0), y: centered ? floor((size.height - imageSize.height) / 2.0) : 0.0), size: imageSize) - context.saveGState() - context.translateBy(x: imageRect.midX, y: imageRect.midY) - context.scaleBy(x: 1.0, y: -1.0) - context.translateBy(x: -imageRect.midX, y: -imageRect.midY) - if image.renderingMode == .alwaysOriginal { - context.draw(image.cgImage!, in: imageRect) - } else { - context.clip(to: imageRect, mask: image.cgImage!) - context.setFillColor(tintColor.cgColor) - context.fill(imageRect) - } - context.restoreGState() + imageRect = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - imageSize.width) / 2.0), y: centered ? floor((size.height - imageSize.height) / 2.0) : 0.0), size: imageSize) } + context.saveGState() + context.translateBy(x: imageRect.midX, y: imageRect.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageRect.midX, y: -imageRect.midY) + if image.renderingMode == .alwaysOriginal { + context.draw(image.cgImage!, in: imageRect) + } else { + context.clip(to: imageRect, mask: image.cgImage!) + context.setFillColor(tintColor.cgColor) + context.fill(imageRect) + } + context.restoreGState() } } @@ -89,15 +81,12 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor: private let badgeFont = Font.regular(13.0) -public enum TabBarItemSwipeDirection { - case left - case right -} - private final class TabBarItemNode: ASDisplayNode { let extractedContainerNode: ContextExtractedContentContainingNode let containerNode: ContextControllerSourceNode let imageNode: ASImageNode + let animationContainerNode: ASDisplayNode + let animationNode: AnimatedStickerNode let textImageNode: ASImageNode let contextImageNode: ASImageNode let contextTextImageNode: ASImageNode @@ -117,6 +106,12 @@ private final class TabBarItemNode: ASDisplayNode { self.imageNode.displayWithoutProcessing = true self.imageNode.displaysAsynchronously = false self.imageNode.isAccessibilityElement = false + + self.animationContainerNode = ASDisplayNode() + + self.animationNode = AnimatedStickerNode() + self.animationNode.automaticallyLoadFirstFrame = true + self.textImageNode = ASImageNode() self.textImageNode.isUserInteractionEnabled = false self.textImageNode.displayWithoutProcessing = true @@ -142,6 +137,8 @@ private final class TabBarItemNode: ASDisplayNode { self.extractedContainerNode.contentNode.addSubnode(self.textImageNode) self.extractedContainerNode.contentNode.addSubnode(self.imageNode) + self.extractedContainerNode.contentNode.addSubnode(self.animationContainerNode) + self.animationContainerNode.addSubnode(self.animationNode) self.extractedContainerNode.contentNode.addSubnode(self.contextTextImageNode) self.extractedContainerNode.contentNode.addSubnode(self.contextImageNode) self.containerNode.addSubnode(self.extractedContainerNode) @@ -451,7 +448,26 @@ class TabBarNode: ASDisplayNode { }) if let selectedIndex = self.selectedIndex, selectedIndex == i { let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) - let (image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) + let (image, imageContentWidth): (UIImage, CGFloat) + + if let _ = item.item.animationName { + (image, imageContentWidth) = (UIImage(), 0.0) + + node.animationNode.isHidden = false + let animationSize: Int = Int(51.0 * UIScreen.main.scale) + node.animationNode.visibility = true + if !node.isSelected { + node.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.item.animationName ?? ""), width: animationSize, height: animationSize, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) + } + node.animationNode.setOverlayColor(self.theme.tabBarSelectedIconColor, replace: true, animated: false) + node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0)) + } else { + (image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) + + node.animationNode.isHidden = true + node.animationNode.visibility = false + } + let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) node.textImageNode.image = textImage @@ -466,6 +482,10 @@ class TabBarNode: ASDisplayNode { let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) + + node.animationNode.isHidden = true + node.animationNode.visibility = false + node.textImageNode.image = textImage node.accessibilityLabel = item.item.title node.imageNode.image = image @@ -496,7 +516,25 @@ class TabBarNode: ASDisplayNode { let previousTextImageSize = node.textImageNode.image?.size ?? CGSize() if let selectedIndex = self.selectedIndex, selectedIndex == index { let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) - let (image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) + let (image, imageContentWidth): (UIImage, CGFloat) + if let _ = item.item.animationName { + (image, imageContentWidth) = (UIImage(), 0.0) + + node.animationNode.isHidden = false + let animationSize: Int = Int(51.0 * UIScreen.main.scale) + node.animationNode.visibility = true + if !node.isSelected { + node.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.item.animationName ?? ""), width: animationSize, height: animationSize, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) + } + node.animationNode.setOverlayColor(self.theme.tabBarSelectedIconColor, replace: true, animated: false) + node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0)) + } else { + (image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) + + node.animationNode.isHidden = true + node.animationNode.visibility = false + } + let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) node.textImageNode.image = textImage @@ -511,6 +549,11 @@ class TabBarNode: ASDisplayNode { let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) + + node.animationNode.stop() + node.animationNode.isHidden = true + node.animationNode.visibility = false + node.textImageNode.image = textImage node.accessibilityLabel = item.item.title node.imageNode.image = image @@ -614,6 +657,14 @@ class TabBarNode: ASDisplayNode { node.contextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.contextTextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) + let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0 + node.animationContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0) + if horizontal { + node.animationNode.frame = CGRect(origin: CGPoint(x: -10.0 - UIScreenPixel, y: -4.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0)) + } else { + node.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 51.0) / 2.0), y: -10.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0)) + } + if container.badgeValue != container.appliedBadgeValue { container.appliedBadgeValue = container.badgeValue if let badgeValue = container.badgeValue, !badgeValue.isEmpty { @@ -633,14 +684,14 @@ class TabBarNode: ASDisplayNode { let backgroundSize = CGSize(width: hasSingleLetterValue ? 18.0 : max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0) let backgroundFrame: CGRect if horizontal { - backgroundFrame = CGRect(origin: CGPoint(x: 15.0, y: 0.0), size: backgroundSize) + backgroundFrame = CGRect(origin: CGPoint(x: 13.0, y: 0.0), size: backgroundSize) } else { let contentWidth: CGFloat = 25.0 backgroundFrame = CGRect(origin: CGPoint(x: floor(node.frame.width / 2.0) + contentWidth - backgroundSize.width - 5.0, y: self.centered ? 6.0 : -1.0), size: backgroundSize) } transition.updateFrame(node: container.badgeContainerNode, frame: backgroundFrame) container.badgeBackgroundNode.frame = CGRect(origin: CGPoint(), size: backgroundFrame.size) - let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0 + container.badgeContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0) container.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundFrame.size.width - badgeSize.width) / 2.0), y: 1.0), size: badgeSize) @@ -673,7 +724,11 @@ class TabBarNode: ASDisplayNode { if let closestNode = closestNode { let container = self.tabBarNodeContainers[closestNode.0] + let previousSelectedIndex = self.selectedIndex self.itemSelected(closestNode.0, longTap, [container.imageNode.imageNode, container.imageNode.textImageNode, container.badgeContainerNode]) + if previousSelectedIndex != closestNode.0 { + container.imageNode.animationNode.play() + } } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift index fcd2900a7f..cc1e9fe5fc 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift @@ -290,6 +290,6 @@ func _internal_collectCacheUsageStats(account: Account, peerId: PeerId? = nil, a } } -func _internal_clearCachedMediaResources(account: Account, mediaResourceIds: Set) -> Signal { +func _internal_clearCachedMediaResources(account: Account, mediaResourceIds: Set) -> Signal { return account.postbox.mediaBox.removeCachedResources(mediaResourceIds) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift index bc2765aa26..da3d77fbd5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift @@ -142,7 +142,7 @@ public extension TelegramEngine { return _internal_collectCacheUsageStats(account: self.account, peerId: peerId, additionalCachePaths: additionalCachePaths, logFilesPath: logFilesPath) } - public func clearCachedMediaResources(mediaResourceIds: Set) -> Signal { + public func clearCachedMediaResources(mediaResourceIds: Set) -> Signal { return _internal_clearCachedMediaResources(account: self.account, mediaResourceIds: mediaResourceIds) } diff --git a/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift b/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift index 4df9b65770..f9f68374ff 100644 --- a/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift +++ b/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift @@ -79,7 +79,7 @@ func mediaBubbleCornerImage(incoming: Bool, radius: CGFloat, inset: CGFloat) -> return formContext.generateImage()! } -public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloat, incoming: Bool, fillColor: UIColor, strokeColor: UIColor, neighbors: MessageBubbleImageNeighbors, theme: PresentationThemeChat, wallpaper: TelegramWallpaper, knockout knockoutValue: Bool, mask: Bool = false, extendedEdges: Bool = false, onlyOutline: Bool = false, onlyShadow: Bool = false) -> UIImage { +public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloat, incoming: Bool, fillColor: UIColor, strokeColor: UIColor, neighbors: MessageBubbleImageNeighbors, theme: PresentationThemeChat, wallpaper: TelegramWallpaper, knockout knockoutValue: Bool, mask: Bool = false, extendedEdges: Bool = false, onlyOutline: Bool = false, onlyShadow: Bool = false, alwaysFillColor: Bool = false) -> UIImage { let topLeftRadius: CGFloat let topRightRadius: CGFloat let bottomLeftRadius: CGFloat @@ -346,6 +346,12 @@ public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloa if !onlyOutline { context.clip(to: CGRect(origin: CGPoint(), size: rawSize), mask: formImage.cgImage!) context.fill(CGRect(origin: CGPoint(), size: rawSize)) + + if alwaysFillColor && drawWithClearColor { + context.setBlendMode(.normal) + context.setFillColor(fillColor.cgColor) + context.fill(CGRect(origin: CGPoint(), size: rawSize)) + } } else { context.setFillColor(strokeColor.cgColor) context.clip(to: CGRect(origin: CGPoint(), size: rawSize), mask: outlineImage.cgImage!) diff --git a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift index ccc7a13c8c..c9aa13d065 100644 --- a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift +++ b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift @@ -38,10 +38,10 @@ public extension PresentationFontSize { } } -public extension TabBarControllerTheme { +public extension ToolbarTheme { convenience init(rootControllerTheme: PresentationTheme) { let theme = rootControllerTheme.rootController.tabBar - self.init(backgroundColor: rootControllerTheme.list.plainBackgroundColor, tabBarBackgroundColor: theme.backgroundColor, tabBarSeparatorColor: theme.separatorColor, tabBarIconColor: theme.iconColor, tabBarSelectedIconColor: theme.selectedIconColor, tabBarTextColor: theme.textColor, tabBarSelectedTextColor: theme.selectedTextColor, tabBarBadgeBackgroundColor: theme.badgeBackgroundColor, tabBarBadgeStrokeColor: theme.badgeStrokeColor, tabBarBadgeTextColor: theme.badgeTextColor, tabBarExtractedIconColor: rootControllerTheme.contextMenu.extractedContentTintColor, tabBarExtractedTextColor: rootControllerTheme.contextMenu.extractedContentTintColor) + self.init(barBackgroundColor: theme.backgroundColor, barSeparatorColor: theme.separatorColor, barTextColor: theme.textColor, barSelectedTextColor: theme.selectedTextColor) } } diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index 1e68394109..80e4279ae4 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -163,7 +163,7 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti outgoingBubbleStrokeColor = .clear } - outgoingBubbleHighlightedFill = outgoingBubbleFillColors?.first?.withMultiplied(hue: 1.054, saturation: 1.589, brightness: 0.96) + outgoingBubbleHighlightedFill = outgoingBubbleFillColors?.first?.withMultiplied(hue: 1.00, saturation: 1.589, brightness: 0.96) let lightnessColor = UIColor.average(of: bubbleColors.map(UIColor.init(rgb:))) if lightnessColor.lightness > 0.705 { diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift index 2f64a23ff3..6ce6f8634b 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift @@ -250,6 +250,13 @@ public final class PrincipalThemeEssentialGraphics { let incomingKnockout = self.incomingBubbleGradientImage != nil let outgoingKnockout = self.outgoingBubbleGradientImage != nil + let highlightKnockout: Bool + if case .color = wallpaper { + highlightKnockout = true + } else { + highlightKnockout = false + } + let serviceColor = serviceMessageColorComponents(chatTheme: theme, wallpaper: wallpaper) let maxCornerRadius = bubbleCorners.mainRadius @@ -376,27 +383,27 @@ public final class PrincipalThemeEssentialGraphics { self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundIncomingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundIncomingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundIncomingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundIncomingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) @@ -405,27 +412,27 @@ public final class PrincipalThemeEssentialGraphics { self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundOutgoingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.chatMessageBackgroundIncomingMergedSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) @@ -435,8 +442,8 @@ public final class PrincipalThemeEssentialGraphics { self.chatMessageBackgroundOutgoingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) - self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) + self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true) self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)! self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)! diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 3a3ff87bed..6892a89902 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -252,6 +252,7 @@ swift_library( "//submodules/QrCodeUI:QrCodeUI", "//submodules/Components/ReactionListContextMenuContent:ReactionListContextMenuContent", "//submodules/Translate:Translate", + "//submodules/TabBarUI:TabBarUI", ] + select({ "@build_bazel_rules_apple//apple:ios_armv7": [], "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, diff --git a/submodules/TelegramUI/Resources/Animations/TabCalls.json b/submodules/TelegramUI/Resources/Animations/TabCalls.json new file mode 100644 index 0000000000..fcc4344b23 --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/TabCalls.json @@ -0,0 +1 @@ +{"v":"4.8.0","meta":{"g":"LottieFiles AE ","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":42,"w":512,"h":512,"nm":"Calls 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[266,248,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[80,80,100],"ix":6}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Wave 1","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":17,"s":[2.339,-2.355,0],"to":[0,0,0],"ti":[0,0,0]},{"t":38,"s":[45.839,-45.855,0]}],"ix":2},"a":{"a":0,"k":[43.339,-55.355,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":17,"s":[{"i":[[-5.533,0.024],[-0.032,-37.225]],"o":[[17.452,-0.075],[0.004,4.811]],"v":[[20.548,-80.425],[68.532,-30.775]],"c":false}]},{"t":38,"s":[{"i":[[-5.325,-0.175],[-0.909,-28.527]],"o":[[25.011,0.822],[0.148,4.631]],"v":[[19.364,-78.447],[66.534,-31.598]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":20,"op":37,"st":17,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Wave 2","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":11,"s":[12.195,-11.177,0],"to":[0,0,0],"ti":[0,0,0]},{"t":32,"s":[65.195,-66.177,0]}],"ix":2},"a":{"a":0,"k":[60.195,-73.177,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":11,"s":[{"i":[[0,0],[0.624,-27.851]],"o":[[30.962,0.129],[-0.035,1.579]],"v":[[34.538,-99.129],[86.483,-48.079]],"c":false}]},{"t":32,"s":[{"i":[[0,0],[1.283,-57.259]],"o":[[66.449,-5.733],[-0.073,3.246]],"v":[[6.784,-125.442],[113.578,-20.488]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":13,"op":33,"st":11,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Wave 3","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[13.436,-15.049,0],"to":[0,0,0],"ti":[0,0,0]},{"t":26,"s":[87.436,-87.049,0]}],"ix":2},"a":{"a":0,"k":[82.436,-94.049,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[{"i":[[0,0],[0.307,-25.5]],"o":[[26.737,-0.438],[0,0]],"v":[[60.763,-121.062],[108.693,-74]],"c":false}]},{"t":26,"s":[{"i":[[0,0],[7.65,-85.061]],"o":[[98.311,-12.031],[0,0]],"v":[[6.336,-167.822],[157.889,-19.014]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":7,"op":28,"st":5,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Phone","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.72],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":9,"s":[-11]},{"i":{"x":[0.72],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":17,"s":[10]},{"i":{"x":[0.72],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":24,"s":[-6]},{"i":{"x":[0.72],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":30,"s":[4]},{"i":{"x":[0.72],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":36,"s":[-2]},{"t":41,"s":[0]}],"ix":10},"p":{"a":0,"k":[256.05,256.05,0],"ix":2},"a":{"a":0,"k":[0.05,0.05,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[35.3,35.3],[0,36.3],[-12.2,11.8],[-8,0],[-4.4,-6.2],[0,0],[7.5,-9.9],[0,0],[0,-1.7],[-0.7,-1.4],[-11.9,-12.3],[0,0],[-7.5,-4],[-1.8,0],[-1.4,1.4],[0,0],[-10.6,-6.6],[0,0],[0,-6.1],[6.5,-6.8],[16.6,0]],"o":[[-35.4,-35.4],[0,-16.1],[7.4,-7.2],[6.9,0],[0,0],[6.5,10.5],[0,0],[-1.4,1.4],[0,1.5],[3.5,7.3],[0,0],[12.8,12.9],[1.4,0.6],[1.8,0],[0,0],[9.9,-7.6],[0,0],[6.2,4.3],[0,8.5],[-11.7,12.6],[-36.4,-0.1]],"v":[[-53.3,53],[-116.1,-62.4],[-98.6,-104.9],[-74.6,-116.1],[-57.1,-107.1],[-35.6,-72.3],[-37.2,-38.4],[-52.4,-18.3],[-54.3,-13.7],[-53,-9.1],[-27.6,22.8],[-25.3,25.1],[8.7,52.8],[13.5,54.1],[18.3,52.2],[38,37],[72.1,35.3],[107.7,57.4],[116.2,73.7],[104.9,97.9],[62.4,116.2]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[{"tm":0,"cm":"1","dr":0},{"tm":41,"cm":"2","dr":0}]} \ No newline at end of file diff --git a/submodules/TelegramUI/Resources/Animations/TabChats.json b/submodules/TelegramUI/Resources/Animations/TabChats.json new file mode 100644 index 0000000000..98ddded181 --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/TabChats.json @@ -0,0 +1 @@ +{"v":"4.8.0","meta":{"g":"LottieFiles AE ","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":37,"w":512,"h":512,"nm":"Chats 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 2","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[256,256,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.6,"y":0},"t":8,"s":[256,294,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":19,"s":[256,232,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":27,"s":[256,262,0],"to":[0,0,0],"ti":[0,0,0]},{"t":34,"s":[256,256,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":9,"s":[92,80,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":20,"s":[105,112,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":28,"s":[100,95,100]},{"t":36,"s":[100,100,100]}],"ix":6}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Bubble L","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":8,"s":[-14]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":19,"s":[8]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":27,"s":[-2]},{"t":34,"s":[0]}],"ix":10},"p":{"a":0,"k":[-113.45,90.543,0],"ix":2},"a":{"a":0,"k":[-113.45,90.543,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-60.8,0],[0,-55.2],[60.8,0],[8.6,1.9],[16.8,-9.4],[2.7,1.1],[-7.2,10.9],[3.2,2.3],[0,31.5]],"o":[[60.8,0],[0,55.2],[-9.3,0],[-3.6,-0.8],[-11.7,6.6],[-4.5,-1.9],[7.2,-10.9],[-25,-18.3],[-0.1,-55.3]],"v":[[-34.5,-125.7],[75.5,-25.7],[-34.5,74.3],[-61.5,71.3],[-86.6,89.2],[-117.3,94.1],[-105.7,76.3],[-104.7,50],[-144.4,-25.6]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Bubble R Piece","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":2,"s":[0]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":10,"s":[12]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":21,"s":[-8]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":29,"s":[2]},{"t":36,"s":[0]}],"ix":10},"p":{"a":0,"k":[112.4,121.165,0],"ix":2},"a":{"a":0,"k":[112.4,121.165,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.432,"y":0},"t":0,"s":[{"i":[[1.569,2.155],[0,-44],[25,-18.3],[-7.2,-10.9],[4.4,-1.9],[11.7,6.6],[3.6,-0.9],[9.3,0],[16.5,9],[-1.027,0.126],[0,59.411],[15.314,21.592]],"o":[[43.4,13.4],[0,31.5],[-3.2,2.3],[7.1,10.9],[-2.8,1.2],[-16.8,-9.4],[-8.6,2],[-20.6,0],[1.035,-0.103],[61.721,-7.57],[-0.088,-21.101],[-1.541,-2.172]],"v":[[70.9,-90.4],[145.5,4.3],[105.8,79.9],[106.8,106.2],[118.4,124],[87.7,119.1],[62.6,101.2],[35.6,104.2],[-20.7,90.1],[-17.607,89.756],[92.2,-25.8],[71.186,-89.842]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.61,"y":0},"t":8,"s":[{"i":[[-35.504,-11.483],[0,-44],[25,-18.3],[-7.2,-10.9],[4.4,-1.9],[11.7,6.6],[3.6,-0.9],[9.3,0],[16.5,9],[-0.689,0.37],[5.894,33.366],[12.273,11.199]],"o":[[43.4,13.4],[0,31.5],[-3.2,2.3],[7.1,10.9],[-2.8,1.2],[-16.8,-9.4],[-8.6,2],[-20.6,0],[0.755,-0.228],[40.821,-21.94],[-6.253,-33.631],[7.006,-2.798]],"v":[[70.9,-90.4],[145.5,4.3],[105.8,79.9],[106.8,106.2],[118.4,124],[87.7,119.1],[62.6,101.2],[35.6,104.2],[-24.815,87.368],[-22.573,86.644],[32.722,-21.117],[-1.515,-89.705]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-37.115,-12.076],[0,-44],[25,-18.3],[-7.2,-10.9],[4.4,-1.9],[11.7,6.6],[3.6,-0.9],[9.3,0],[16.5,9],[-0.728,0.26],[6.151,32.234],[12.343,10.945]],"o":[[43.4,13.4],[0,31.5],[-3.2,2.3],[7.1,10.9],[-2.8,1.2],[-16.8,-9.4],[-8.6,2],[-20.6,0],[0.742,-0.234],[43.722,-15.614],[-6.521,-34.176],[7.372,-2.822]],"v":[[70.9,-90.4],[145.5,4.3],[105.8,79.9],[106.8,106.2],[118.4,124],[87.7,119.1],[62.6,101.2],[35.6,104.2],[-20.804,89.611],[-18.599,88.87],[35.968,-21.004],[-2.605,-90.675]],"c":true}]},{"i":{"x":0.71,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[-19.247,-9.551],[-0.275,-42.138],[25,-18.3],[-7.2,-10.9],[4.4,-1.9],[11.7,6.6],[3.6,-0.9],[9.3,0],[13.209,5.038],[1.615,2.361],[-0.353,55.136],[11.83,14.082]],"o":[[31.739,16.231],[0.225,31.498],[-3.2,2.3],[7.1,10.9],[-2.8,1.2],[-16.8,-9.4],[-8.6,2],[-20.6,0],[-0.965,-0.989],[41.328,-7.153],[-0.441,-31.073],[6.153,-0.435]],"v":[[86.326,-83.268],[145.5,4.3],[105.8,79.9],[106.8,106.2],[118.4,124],[87.7,119.1],[62.6,101.2],[35.6,104.2],[-9.996,95.52],[-10.596,93.787],[70.779,-6.574],[40.716,-94.962]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.167,"y":0.167},"t":19,"s":[{"i":[[-3.52,0.283],[-0.696,-39.297],[25,-18.3],[-7.2,-10.9],[4.4,-1.9],[11.7,6.6],[3.6,-0.9],[9.3,0],[16.5,9],[5.191,5.567],[-16.412,43.028],[11.047,18.87]],"o":[[15.573,8.892],[0.568,31.494],[-3.2,2.3],[7.1,10.9],[-2.8,1.2],[-16.8,-9.4],[-8.6,2],[-20.6,0],[-3.571,-2.141],[76.865,17.922],[8.839,-26.337],[-0.13,-2.406]],"v":[[107.119,-68.866],[145.5,4.3],[105.8,79.9],[106.8,106.2],[118.4,124],[87.7,119.1],[62.6,101.2],[35.6,104.2],[-20.714,90.036],[-25.594,86.786],[115.287,17.773],[106.417,-68.37]],"c":true}]},{"i":{"x":0.19,"y":1},"o":{"x":0.3,"y":0},"t":21,"s":[{"i":[[1.569,2.155],[-0.801,-38.584],[25,-18.3],[-7.2,-10.9],[4.4,-1.9],[11.7,6.6],[3.6,-0.9],[9.3,0],[16.5,9],[6.087,6.371],[-19.829,44.663],[10.851,20.071]],"o":[[11.358,8.209],[0.654,31.493],[-3.2,2.3],[7.1,10.9],[-2.8,1.2],[-16.8,-9.4],[-8.6,2],[-20.6,0],[-4.225,-2.43],[61.906,22.357],[11.166,-25.149],[-1.267,-2.343]],"v":[[112.605,-65.604],[145.5,4.3],[105.8,79.9],[106.8,106.2],[118.4,124],[87.7,119.1],[62.6,101.2],[35.6,104.2],[-20.7,90.1],[-46.104,71.92],[109.444,16.764],[112.684,-63.641]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":29,"s":[{"i":[[1.569,2.155],[0,-44],[25,-18.3],[-7.2,-10.9],[4.4,-1.9],[11.7,6.6],[3.6,-0.9],[9.3,0],[16.5,9],[-1.027,0.126],[0,59.411],[10.648,17.024]],"o":[[51.204,13.806],[0,31.5],[-3.2,2.3],[7.1,10.9],[-2.8,1.2],[-16.8,-9.4],[-8.6,2],[-20.6,0],[1.035,-0.103],[61.721,-7.57],[-0.088,-21.101],[-1.412,-2.258]],"v":[[62.059,-92.843],[145.5,4.3],[105.8,79.9],[106.8,106.2],[118.4,124],[87.7,119.1],[62.6,101.2],[35.6,104.2],[-20.7,90.1],[-17.607,89.756],[86.16,-26.84],[66.533,-86.222]],"c":true}]},{"t":36,"s":[{"i":[[1.569,2.155],[0,-44],[25,-18.3],[-7.2,-10.9],[4.4,-1.9],[11.7,6.6],[3.6,-0.9],[9.3,0],[16.5,9],[-1.027,0.126],[0,59.411],[10.648,17.024]],"o":[[43.4,13.4],[0,31.5],[-3.2,2.3],[7.1,10.9],[-2.8,1.2],[-16.8,-9.4],[-8.6,2],[-20.6,0],[1.035,-0.103],[61.721,-7.57],[-0.088,-21.101],[-1.412,-2.258]],"v":[[70.9,-90.4],[145.5,4.3],[105.8,79.9],[106.8,106.2],[118.4,124],[87.7,119.1],[62.6,101.2],[35.6,104.2],[-20.7,90.1],[-17.607,89.756],[92.2,-25.8],[75.374,-83.779]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[{"tm":0,"cm":"1","dr":0},{"tm":88,"cm":"2","dr":0}]} \ No newline at end of file diff --git a/submodules/TelegramUI/Resources/Animations/TabContacts.json b/submodules/TelegramUI/Resources/Animations/TabContacts.json new file mode 100644 index 0000000000..726465ec1c --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/TabContacts.json @@ -0,0 +1 @@ +{"v":"4.8.0","meta":{"g":"LottieFiles AE ","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":60,"w":512,"h":512,"nm":"Contacts 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Main","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.7],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":20,"s":[97,97,100]},{"t":39,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.2,"y":0},"t":16,"s":[{"i":[[0.057,0],[0,-0.057],[-0.057,0],[0,0.057]],"o":[[-0.057,0],[0,0.057],[0.057,0],[0,-0.057]],"v":[[-13.08,19],[-13.183,19.103],[-13.08,19.205],[-12.978,19.103]],"c":true}]},{"t":39,"s":[{"i":[[21.242,0],[0,-21.242],[-21.242,0],[0,21.242]],"o":[[-21.242,0],[0,21.242],[21.242,0],[0,-21.242]],"v":[[0,-67.308],[-38.462,-28.846],[0,9.615],[38.462,-28.846]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.2,"y":0},"t":16,"s":[{"i":[[0.077,0],[0.043,-0.036],[0.005,-0.006],[-0.009,-0.01],[-0.002,-0.001],[-0.068,0],[-0.049,0.04],[-0.005,0.005],[0.007,0.01],[0.007,0.005]],"o":[[-0.077,0],[-0.004,0.003],[-0.009,0.009],[0,0],[0.049,0.04],[0.068,0],[0.003,-0.003],[0.009,-0.009],[0,0],[-0.043,-0.036]],"v":[[-13.08,19.282],[-13.26,19.337],[-13.273,19.35],[-13.274,19.384],[-13.26,19.397],[-13.08,19.462],[-12.901,19.397],[-12.888,19.386],[-12.885,19.352],[-12.901,19.337]],"c":true}]},{"t":39,"s":[{"i":[[28.941,0],[15.96,-13.616],[1.957,-2.123],[-3.375,-3.669],[-0.654,-0.54],[-25.552,0],[-18.285,15.109],[-1.809,1.726],[2.746,3.839],[2.536,2.04]],"o":[[-28.914,0],[-1.444,1.232],[-3.281,3.561],[0,0],[18.282,15.084],[25.573,0],[1.296,-1.071],[3.5,-3.341],[0,0],[-15.952,-13.638]],"v":[[0,38.462],[-67.306,58.884],[-72.407,63.917],[-72.687,76.606],[-67.269,81.625],[0,105.769],[67.311,81.585],[71.967,77.39],[73.156,64.848],[67.339,58.919]],"c":true}]}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.2,"y":0},"t":16,"s":[86.947,69.231],"to":[0,0],"ti":[0,0]},{"t":39,"s":[-0.053,19.231]}],"ix":2},"a":{"a":0,"k":[-0.053,19.231],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.7,"y":0},"t":0,"s":[{"i":[[21.242,0],[0,-21.242],[-21.242,0],[0,21.242]],"o":[[-21.242,0],[0,21.242],[21.242,0],[0,-21.242]],"v":[[0,-67.308],[-38.462,-28.846],[0,9.615],[38.462,-28.846]],"c":true}]},{"t":20,"s":[{"i":[[0.057,0],[0,-0.057],[-0.057,0],[0,0.057]],"o":[[-0.057,0],[0,0.057],[0.057,0],[0,-0.057]],"v":[[-13.08,19],[-13.183,19.103],[-13.08,19.205],[-12.978,19.103]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.7,"y":0},"t":0,"s":[{"i":[[28.941,0],[15.96,-13.616],[1.957,-2.123],[-3.375,-3.669],[-0.654,-0.54],[-25.552,0],[-18.285,15.109],[-1.809,1.726],[2.746,3.839],[2.536,2.04]],"o":[[-28.914,0],[-1.444,1.232],[-3.281,3.561],[0,0],[18.282,15.084],[25.573,0],[1.296,-1.071],[3.5,-3.341],[0,0],[-15.952,-13.638]],"v":[[0,38.462],[-67.306,58.884],[-72.407,63.917],[-72.687,76.606],[-67.269,81.625],[0,105.769],[67.311,81.585],[71.967,77.39],[73.156,64.848],[67.339,58.919]],"c":true}]},{"t":20,"s":[{"i":[[0.077,0],[0.043,-0.036],[0.005,-0.006],[-0.009,-0.01],[-0.002,-0.001],[-0.068,0],[-0.049,0.04],[-0.005,0.005],[0.007,0.01],[0.007,0.005]],"o":[[-0.077,0],[-0.004,0.003],[-0.009,0.009],[0,0],[0.049,0.04],[0.068,0],[0.003,-0.003],[0.009,-0.009],[0,0],[-0.043,-0.036]],"v":[[-13.08,19.282],[-13.26,19.337],[-13.273,19.35],[-13.274,19.384],[-13.26,19.397],[-13.08,19.462],[-12.901,19.397],[-12.888,19.386],[-12.885,19.352],[-12.901,19.337]],"c":true}]}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.7,"y":0},"t":0,"s":[-0.053,19.231],"to":[0,0],"ti":[0,0]},{"t":20,"s":[-60.053,69.231]}],"ix":2},"a":{"a":0,"k":[-0.053,19.231],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":0,"s":[{"i":[[-69.036,0],[0,-69.036],[69.036,0],[0,69.036]],"o":[[69.036,0],[0,69.036],[-69.036,0],[0,-69.036]],"v":[[0,-125],[125,0],[0,125],[-125,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":17,"s":[{"i":[[-60.751,0],[0,-60.751],[60.751,0],[0,60.751]],"o":[[60.751,0],[0,60.751],[-60.751,0],[0,-60.751]],"v":[[0,-110],[110,0],[0,110],[-110,0]],"c":true}]},{"t":36,"s":[{"i":[[-69.036,0],[0,-69.036],[69.036,0],[0,69.036]],"o":[[69.036,0],[0,69.036],[-69.036,0],[0,-69.036]],"v":[[0,-125],[125,0],[0,125],[-125,0]],"c":true}]}],"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/submodules/TelegramUI/Resources/Animations/TabSettings.json b/submodules/TelegramUI/Resources/Animations/TabSettings.json new file mode 100644 index 0000000000..0064b19364 --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/TabSettings.json @@ -0,0 +1 @@ +{"v":"4.8.0","meta":{"g":"LottieFiles AE ","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":44,"w":512,"h":512,"nm":"Settings 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL ALL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.32,0.32,0.32],"y":[3.958,3.958,-4.339]},"o":{"x":[0.199,0.199,0.027],"y":[0,0,0]},"t":12,"s":[110,110,100]},{"i":{"x":[0.292,0.292,0.609],"y":[1,1,1]},"o":{"x":[0.278,0.278,0.278],"y":[0.099,0.099,1.795]},"t":29,"s":[110,110,100]},{"t":43,"s":[100,100,100]}],"ix":6}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.667,-0.667,0],"ix":2},"a":{"a":0,"k":[0.667,-0.667,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.199,"y":0},"t":2,"s":[{"i":[[0,-55.136],[55.136,0],[0,55.136],[-55.136,0]],"o":[[0,55.136],[-55.136,0],[0,-55.136],[55.136,0]],"v":[[100.5,-0.667],[0.667,99.167],[-99.167,-0.667],[0.667,-100.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.199,"y":0},"t":12,"s":[{"i":[[0,-32.651],[32.651,0],[0,32.651],[-32.651,0]],"o":[[0,32.651],[-32.651,0],[0,-32.651],[32.651,0]],"v":[[58.476,-1.024],[-0.643,58.095],[-59.762,-1.024],[-0.643,-60.143]],"c":true}]},{"i":{"x":0.685,"y":1},"o":{"x":0.452,"y":0.444},"t":16,"s":[{"i":[[0,-32.15],[32.15,0],[0,32.15],[-32.15,0]],"o":[[0,32.15],[-32.15,0],[0,-32.15],[32.15,0]],"v":[[56.887,-0.472],[-1.326,57.742],[-59.54,-0.472],[-1.326,-58.685]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":1,"y":0},"t":28,"s":[{"i":[[0,-58.226],[58.226,0],[0,58.226],[-58.226,0]],"o":[[0,58.226],[-58.226,0],[0,-58.226],[58.226,0]],"v":[[105.646,-0.623],[0.219,104.804],[-105.208,-0.623],[0.219,-106.05]],"c":true}]},{"t":43,"s":[{"i":[[0,-55.136],[55.136,0],[0,55.136],[-55.136,0]],"o":[[0,55.136],[-55.136,0],[0,-55.136],[55.136,0]],"v":[[100.5,-0.667],[0.667,99.167],[-99.167,-0.667],[0.667,-100.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.199],"y":[0]},"t":2,"s":[23]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.199],"y":[0]},"t":12,"s":[11]},{"i":{"x":[0.662],"y":[1.231]},"o":{"x":[0.331],"y":[0]},"t":16,"s":[7]},{"i":{"x":[0.469],"y":[0.527]},"o":{"x":[0.218],"y":[0.166]},"t":18,"s":[5]},{"i":{"x":[0.71],"y":[1]},"o":{"x":[0.237],"y":[0.316]},"t":28,"s":[14]},{"t":43,"s":[23]}],"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Gears","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.431],"y":[1]},"o":{"x":[0.284],"y":[0]},"t":0,"s":[0]},{"t":43,"s":[-120]}],"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.199,0.199,0.199],"y":[0,0,0]},"t":2,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1.003,1.003,1.006]},"o":{"x":[0.199,0.199,0.199],"y":[0,0,0]},"t":12,"s":[50,50,100]},{"i":{"x":[0.685,0.685,0.685],"y":[1,1,1]},"o":{"x":[0.452,0.452,0.452],"y":[-0.008,-0.008,-0.104]},"t":14,"s":[52,52,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":28,"s":[101,101,100]},{"t":43,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.674,0],[6.348,1.134],[0,0],[0,0],[0,-3.927],[0,0],[0,0],[-5.128,0],[0,5.523],[0,0],[0,0],[-3.744,1.182],[0,0]],"o":[[-6.674,0],[0,0],[0,0],[3.515,1.584],[0,0],[0,0],[0.578,4.973],[5.523,0],[0,0],[0,0],[0.452,-3.829],[0,0],[-6.348,1.134]],"v":[[0,110.5],[-19.559,108.774],[-16.919,109.482],[-15.829,109.897],[-10,118.922],[-10,126.667],[-9.933,127.833],[0,136.667],[10,126.667],[10,118.922],[10.068,117.758],[16.919,109.482],[19.559,108.774]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[9.97,8.477],[0,0],[2.16,-3.741],[0,0],[-4.783,-2.761],[-2.761,4.783],[0,0],[-4.217,-0.933],[0,0]],"o":[[0,0],[2.917,3.186],[0,0],[-2.761,4.783],[4.783,2.761],[0,0],[2.16,-3.741],[0,0],[-12.55,-4.53]],"v":[[-71.533,84.215],[-69.393,86.355],[-68.121,97.99],[-71.994,104.697],[-68.333,118.357],[-54.673,114.697],[-50.801,107.99],[-40.089,103.273],[-37.52,103.962]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.53,12.55],[0,0],[3.741,-2.16],[0,0],[-2.761,-4.783],[-4.783,2.761],[0,0],[-3.186,-2.917],[0,0]],"o":[[0,0],[0.933,4.217],[0,0],[-4.783,2.761],[2.761,4.783],[0,0],[3.741,-2.16],[0,0],[-8.477,-9.97]],"v":[[-103.962,37.52],[-103.273,40.089],[-107.99,50.801],[-114.697,54.673],[-118.357,68.333],[-104.697,71.994],[-97.99,68.121],[-86.355,69.393],[-84.215,71.533]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.917,3.186],[0,0],[12.55,-4.53],[0,0],[-2.16,-3.741],[0,0],[-4.783,2.761],[2.761,4.783]],"o":[[-2.16,-3.741],[0,0],[-9.97,8.477],[0,0],[4.217,-0.933],[0,0],[2.761,4.783],[4.783,-2.761],[0,0]],"v":[[68.121,97.99],[69.393,86.355],[71.533,84.215],[37.52,103.962],[40.089,103.273],[50.801,107.99],[54.673,114.697],[68.333,118.357],[71.994,104.697]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.523,0],[0,0],[1.301,4.119],[0,0],[0,-6.674],[1.134,-6.348],[0,0],[-4.319,0],[0,0],[0,5.523]],"o":[[0,0],[-4.319,0],[0,0],[1.134,6.348],[0,6.674],[0,0],[1.301,-4.119],[0,0],[5.523,0],[0,-5.523]],"v":[[126.667,-10],[118.922,-10],[109.482,-16.919],[108.774,-19.559],[110.5,0],[108.774,19.559],[109.482,16.919],[118.922,10],[126.667,10],[136.667,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.783,2.761],[0,0],[-0.933,4.217],[0,0],[8.477,-9.97],[0,0],[-3.74,-2.16],[0,0],[-2.761,4.783]],"o":[[0,0],[-3.74,-2.16],[0,0],[-4.53,12.55],[0,0],[3.186,-2.917],[0,0],[4.783,2.761],[2.761,-4.783]],"v":[[114.697,54.673],[107.99,50.801],[103.273,40.089],[103.962,37.52],[84.215,71.533],[86.355,69.393],[97.99,68.121],[104.697,71.994],[118.357,68.333]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.674,0],[-6.348,-1.134],[0,0],[0,4.319],[0,0],[5.523,0],[0,-5.523],[0,0],[4.119,-1.301],[0,0]],"o":[[6.674,0],[0,0],[-4.119,-1.301],[0,0],[0,-5.523],[-5.523,0],[0,0],[0,4.319],[0,0],[6.348,-1.134]],"v":[[0,-110.5],[19.559,-108.774],[16.919,-109.482],[10,-118.922],[10,-126.667],[0,-136.667],[-10,-126.667],[-10,-118.922],[-16.919,-109.482],[-19.559,-108.774]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-9.97,-8.477],[0,0],[-2.16,3.741],[0,0],[4.783,2.761],[2.761,-4.783],[0,0],[4.217,0.933],[0,0]],"o":[[0,0],[-2.917,-3.186],[0,0],[2.761,-4.783],[-4.783,-2.761],[0,0],[-2.16,3.741],[0,0],[12.55,4.53]],"v":[[71.533,-84.215],[69.393,-86.355],[68.121,-97.99],[71.994,-104.697],[68.333,-118.357],[54.673,-114.697],[50.801,-107.99],[40.089,-103.273],[37.52,-103.962]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.53,-12.55],[0,0],[-3.74,2.16],[0,0],[2.761,4.783],[4.783,-2.761],[0,0],[3.186,2.917],[0,0]],"o":[[0,0],[-0.933,-4.217],[0,0],[4.783,-2.761],[-2.761,-4.783],[0,0],[-3.74,2.16],[0,0],[8.477,9.97]],"v":[[103.962,-37.52],[103.273,-40.089],[107.99,-50.801],[114.697,-54.673],[118.357,-68.333],[104.697,-71.994],[97.99,-68.121],[86.355,-69.393],[84.215,-71.533]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.783,-2.761],[0,0],[0.933,-4.217],[0,0],[-8.477,9.97],[0,0],[3.741,2.16],[0,0],[2.761,-4.783]],"o":[[0,0],[3.741,2.16],[0,0],[4.53,-12.55],[0,0],[-3.186,2.917],[0,0],[-4.783,-2.761],[-2.761,4.783]],"v":[[-114.697,-54.673],[-107.99,-50.801],[-103.273,-40.089],[-103.962,-37.52],[-84.215,-71.533],[-86.355,-69.393],[-97.99,-68.121],[-104.697,-71.994],[-118.357,-68.333]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.301,-4.119],[0,0],[0,6.674],[-1.134,6.348],[0,0],[4.319,0],[0,0],[0,-5.523],[-5.523,0],[0,0]],"o":[[0,0],[-1.134,-6.348],[0,-6.674],[0,0],[-1.301,4.119],[0,0],[-5.523,0],[0,5.523],[0,0],[4.319,0]],"v":[[-109.482,16.919],[-108.774,19.559],[-110.5,0],[-108.774,-19.559],[-109.482,-16.919],[-118.922,-10],[-126.667,-10],[-136.667,0],[-126.667,10],[-118.922,10]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[2.917,-3.186],[0,0],[-12.55,4.53],[0,0],[2.16,3.741],[0,0],[4.783,-2.761],[-2.761,-4.783]],"o":[[2.16,3.741],[0,0],[9.97,-8.477],[0,0],[-4.217,0.933],[0,0],[-2.761,-4.783],[-4.783,2.761],[0,0]],"v":[[-68.121,-97.99],[-69.393,-86.355],[-71.533,-84.215],[-37.52,-103.962],[-40.089,-103.273],[-50.801,-107.99],[-54.673,-114.697],[-68.333,-118.357],[-71.994,-104.697]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Center","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.615],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1.002]},"o":{"x":[0.335],"y":[0]},"t":10,"s":[-50]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.057],"y":[0]},"t":11,"s":[-175]},{"t":38,"s":[0]}],"ix":10},"p":{"a":0,"k":[-1.289,0,0],"ix":2},"a":{"a":0,"k":[-1.289,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.15,0.15,0.15],"y":[0,0,0]},"t":12,"s":[70,70,100]},{"t":38,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.986,0],[-9.71,-29.167],[0,0],[5.141,-1.512],[0,0],[1.868,2.699],[0,0],[-3.849,2.664],[0,0]],"o":[[32.43,0],[0,0],[1.512,5.141],[-0.889,0.262],[-3.282,0],[0,0],[-2.663,-3.849],[0.509,-0.352],[6.401,-1.823]],"v":[[0,-73.4],[69.658,-23.201],[70.425,-20.752],[63.855,-8.705],[14.587,-8.305],[6.363,-12.614],[-24.792,-57.628],[-22.646,-69.419],[-20.145,-70.601]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.199,"y":0},"t":0,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.32,"y":0.617},"o":{"x":0.199,"y":0},"t":12,"s":[-3.475,5.769],"to":[0,0],"ti":[0,0]},{"i":{"x":0.292,"y":1},"o":{"x":0.278,"y":0.099},"t":29,"s":[5.001,-11.271],"to":[0,0],"ti":[0,0]},{"t":43,"s":[0,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.066,-3.716],[0,0],[2.371,-3.425],[0,0],[4.39,3.039],[0,0],[0,19.231],[-10.767,12.772],[0,0]],"o":[[0.552,0.505],[2.371,3.425],[0,0],[-3.039,4.39],[0,0],[-12.103,-13.087],[0,-18.013],[0,0],[3.716,-4.066]],"v":[[-40.109,-50.132],[-9.036,-5.692],[-9.036,5.692],[-38.855,48.762],[-52.307,51.207],[-53.901,49.822],[-73.4,0],[-56.144,-47.282],[-54.2,-49.497]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.199,"y":0},"t":0,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.32,"y":0.625},"o":{"x":0.199,"y":0},"t":12,"s":[5.858,-0.402],"to":[0,0],"ti":[0,0]},{"i":{"x":0.292,"y":1},"o":{"x":0.278,"y":0.099},"t":29,"s":[-10.124,0.87],"to":[0,0],"ti":[0,0]},{"t":43,"s":[0,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-5.523],[0,0],[0,0],[33.102,0],[4.923,1.041],[0,0],[-1.317,4.95],[0,0],[-3.282,0]],"o":[[5.523,0.001],[0,0.64],[0,0],[-9.153,30.11],[-5.233,0],[0,0],[-4.95,-1.317],[0.275,-1.035],[1.868,-2.699],[0,0]],"v":[[60.722,8.3],[70.721,18.301],[70.238,21.378],[70.238,21.378],[0,73.4],[-15.262,71.811],[-18.919,70.939],[-25.498,59.591],[6.353,12.605],[14.577,8.296]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.199,"y":0},"t":0,"s":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.32,"y":0.621},"o":{"x":0.199,"y":0},"t":12,"s":[-2.549,-4.624],"to":[0,0],"ti":[0,0]},{"i":{"x":0.292,"y":1},"o":{"x":0.278,"y":0.099},"t":29,"s":[6.634,6.311],"to":[0,0],"ti":[0,0]},{"t":43,"s":[0,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 5ec03202f2..c013b96e56 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -2035,7 +2035,7 @@ private func extractAccountManagerState(records: AccountRecordsView = 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?) in + 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 notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed) let phoneNumberWarning = suggestions.contains(.validatePhoneNumber) let passwordWarning = suggestions.contains(.validatePassword) @@ -7179,14 +7179,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) + return (presentationData.strings.Settings_Title, accountTabBarAvatar?.0 ?? icon, accountTabBarAvatar?.1 ?? icon, notificationsWarning || phoneNumberWarning || passwordWarning ? "!" : otherAccountsBadge, accountTabBarAvatar != nil) } - self.tabBarItemDisposable = (tabBarItem |> deliverOnMainQueue).start(next: { [weak self] title, image, selectedImage, badgeValue in + self.tabBarItemDisposable = (tabBarItem |> deliverOnMainQueue).start(next: { [weak self] title, image, selectedImage, badgeValue, isAvatar 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.badgeValue = badgeValue } }) diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index ad07e31c0d..78c73bde49 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -14,6 +14,7 @@ import SettingsUI import AppBundle import DatePickerNode import DebugSettingsUI +import TabBarUI public final class TelegramRootController: NavigationController { private let context: AccountContext @@ -64,7 +65,7 @@ public final class TelegramRootController: NavigationController { let previousTheme = strongSelf.presentationData.theme strongSelf.presentationData = presentationData if previousTheme !== presentationData.theme { - strongSelf.rootTabController?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme)) + (strongSelf.rootTabController as? TabBarControllerImpl)?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme)) strongSelf.rootTabController?.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style } } @@ -81,7 +82,7 @@ public final class TelegramRootController: NavigationController { } public func addRootControllers(showCallsTab: Bool) { - let tabBarController = TabBarController(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme)) + let tabBarController = TabBarControllerImpl(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme)) tabBarController.navigationPresentation = .master let chatListController = self.context.sharedContext.makeChatListController(context: self.context, groupId: .root, controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild) if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl { @@ -131,7 +132,7 @@ public final class TelegramRootController: NavigationController { } public func updateRootControllers(showCallsTab: Bool) { - guard let rootTabController = self.rootTabController else { + guard let rootTabController = self.rootTabController as? TabBarControllerImpl else { return } var controllers: [ViewController] = [] diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index 2c1bd90901..5969580f71 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -728,44 +728,68 @@ public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttribu var pre = match.range(at: 3) if pre.location != NSNotFound { - let text = string.substring(with: pre) - - stringOffset -= match.range(at: 2).length + match.range(at: 4).length - - let substring = string.substring(with: match.range(at: 1)) + text + string.substring(with: match.range(at: 5)) - result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.monospace: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 1).length, text.count), 6)) + var intersectsWithEntities = false + text.enumerateAttributes(in: pre, options: [], using: { attributes, _, _ in + for (key, _) in attributes { + if key.rawValue.hasPrefix("Attribute__") { + intersectsWithEntities = true + } + } + }) + if intersectsWithEntities { + result.append(text.attributedSubstring(from: match.range(at: 0))) + } else { + let text = string.substring(with: pre) + + stringOffset -= match.range(at: 2).length + match.range(at: 4).length + + let substring = string.substring(with: match.range(at: 1)) + text + string.substring(with: match.range(at: 5)) + result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.monospace: true as NSNumber])) + offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 1).length, text.count), 6)) + } } pre = match.range(at: 8) if pre.location != NSNotFound { - let text = string.substring(with: pre) - - let entity = string.substring(with: match.range(at: 7)) - let substring = string.substring(with: match.range(at: 6)) + text + string.substring(with: match.range(at: 9)) - - let textInputAttribute: NSAttributedString.Key? - switch entity { - case "`": - textInputAttribute = ChatTextInputAttributes.monospace - case "**": - textInputAttribute = ChatTextInputAttributes.bold - case "__": - textInputAttribute = ChatTextInputAttributes.italic - case "~~": - textInputAttribute = ChatTextInputAttributes.strikethrough - case "||": - textInputAttribute = ChatTextInputAttributes.spoiler - default: - textInputAttribute = nil + var intersectsWithEntities = false + text.enumerateAttributes(in: pre, options: [], using: { attributes, _, _ in + for (key, _) in attributes { + if key.rawValue.hasPrefix("Attribute__") { + intersectsWithEntities = true + } + } + }) + if intersectsWithEntities { + result.append(text.attributedSubstring(from: match.range(at: 0))) + } else { + let text = string.substring(with: pre) + + let entity = string.substring(with: match.range(at: 7)) + let substring = string.substring(with: match.range(at: 6)) + text + string.substring(with: match.range(at: 9)) + + let textInputAttribute: NSAttributedString.Key? + switch entity { + case "`": + textInputAttribute = ChatTextInputAttributes.monospace + case "**": + textInputAttribute = ChatTextInputAttributes.bold + case "__": + textInputAttribute = ChatTextInputAttributes.italic + case "~~": + textInputAttribute = ChatTextInputAttributes.strikethrough + case "||": + textInputAttribute = ChatTextInputAttributes.spoiler + default: + textInputAttribute = nil + } + + if let textInputAttribute = textInputAttribute { + result.append(NSAttributedString(string: substring, attributes: [textInputAttribute: true as NSNumber])) + offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) + } + + stringOffset -= match.range(at: 7).length * 2 } - - if let textInputAttribute = textInputAttribute { - result.append(NSAttributedString(string: substring, attributes: [textInputAttribute: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) - } - - stringOffset -= match.range(at: 7).length * 2 } string = string.substring(from: match.range.location + match.range(at: 0).length) as NSString diff --git a/submodules/Translate/Sources/Translate.swift b/submodules/Translate/Sources/Translate.swift index 57b0b2fc15..83a88a5fcc 100644 --- a/submodules/Translate/Sources/Translate.swift +++ b/submodules/Translate/Sources/Translate.swift @@ -59,7 +59,7 @@ public func translateText(context: AccountContext, text: String) { return } if #available(iOS 15.0, *) { - let text = text.unicodeScalars.filter { !$0.properties.isEmojiPresentation}.reduce("") { $0 + String($1) } + let text = text.unicodeScalars.filter { !$0.properties.isEmojiPresentation }.reduce("") { $0 + String($1) } let textView = UITextView() textView.text = text diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.h b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.h index f378a98aa2..366137ba47 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.h +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.h @@ -48,7 +48,6 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem * _Nonnull item, UITabBa - (NSInteger)addSetSelectedImageListener:(UINavigationItemSetImageListener _Nonnull)listener; - (void)removeSetSelectedImageListener:(NSInteger)key; -- (NSObject * _Nullable)userInfo; -- (void)setUserInfo:(NSObject * _Nullable)userInfo; +@property (nonatomic, strong) NSString * _Nullable animationName; @end diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m index 22ea4a793e..99f6b435aa 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m @@ -16,7 +16,7 @@ static const void *setMultipleRightBarButtonItemsListenerKey = &setMultipleRight static const void *setBackBarButtonItemListenerBagKey = &setBackBarButtonItemListenerBagKey; static const void *setBadgeListenerBagKey = &setBadgeListenerBagKey; static const void *badgeKey = &badgeKey; -static const void *userInfoKey = &userInfoKey; +static const void *animationNameKey = &animationNameKey; @implementation UINavigationItem (Proxy) @@ -402,12 +402,16 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem *item, UITabBarItemSetBa [(NSBag *)[self associatedObjectForKey:setSelectedImageListenerBagKey] removeItem:key]; } -- (NSObject * _Nullable)userInfo { - return [self associatedObjectForKey:userInfoKey]; +- (void)setAnimationName:(NSString *)animationName { + [self setAssociatedObject:animationName forKey:animationNameKey]; + +// [(NSBag *)[self associatedObjectForKey:setBadgeListenerBagKey] enumerateItems:^(UITabBarItemSetBadgeListener listener) { +// listener(badge); +// }]; } -- (void)setUserInfo:(NSObject * _Nullable)userInfo { - [self setAssociatedObject:userInfo forKey:userInfoKey]; +- (NSString *)animationName { + return [self associatedObjectForKey:animationNameKey]; } @end diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index d133ee81ac..a38a49149d 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -432,9 +432,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode var isReady: Signal { return self._isReady.get() } - - private var newYearNode: WallpaperNewYearNode? - + init(context: AccountContext, useSharedAnimationPhase: Bool) { self.context = context self.useSharedAnimationPhase = useSharedAnimationPhase @@ -800,16 +798,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode if isFirstLayout && !self.frame.isEmpty { self.updateScale() - - if self.context.sharedContext.immediateExperimentalUISettings.snow, self.newYearNode == nil { - let newYearNode = WallpaperNewYearNode() - self.addSubnode(newYearNode) - self.newYearNode = newYearNode - } } - - self.newYearNode?.frame = CGRect(origin: CGPoint(), size: size) - self.newYearNode?.updateLayout(size: size) } func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) { @@ -1720,47 +1709,3 @@ public func createWallpaperBackgroundNode(context: AccountContext, forChatDispla return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase) } - -private class WallpaperNewYearNode: ASDisplayNode { - private var emitterLayer: CAEmitterLayer? - - func updateLayout(size: CGSize) { - if self.emitterLayer == nil { - let particlesLayer = CAEmitterLayer() - self.emitterLayer = particlesLayer - - self.layer.addSublayer(particlesLayer) - self.layer.masksToBounds = true - - particlesLayer.backgroundColor = UIColor.clear.cgColor - particlesLayer.emitterShape = .circle - particlesLayer.emitterMode = .surface - particlesLayer.renderMode = .oldestLast - - let cell1 = CAEmitterCell() - cell1.contents = UIImage(bundleImageName: "Components/Snowflake")?.cgImage - cell1.name = "snow" - cell1.birthRate = 252.0 - cell1.lifetime = 20.0 - cell1.velocity = 19.0 - cell1.velocityRange = -5.0 - cell1.xAcceleration = 2.5 - cell1.yAcceleration = 10.0 - cell1.emissionRange = .pi - cell1.spin = -28.6 * (.pi / 180.0) - cell1.spinRange = 57.2 * (.pi / 180.0) - cell1.scale = 0.04 - cell1.scaleRange = 0.15 - cell1.color = UIColor.white.withAlphaComponent(0.58).cgColor -// cell1.alphaRange = -0.2 - - particlesLayer.emitterCells = [cell1] - } - - if let emitterLayer = self.emitterLayer { - emitterLayer.emitterPosition = CGPoint(x: size.width / 2.0, y: -size.height / 2.0) - emitterLayer.emitterSize = CGSize(width: size.width * 2.5, height: size.height) - emitterLayer.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height) - } - } -}