From 6af3837f857e1a88145fa84aed0a8ff21b9af892 Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Wed, 8 Mar 2023 12:02:50 +0400 Subject: [PATCH 01/16] bug fixes --- submodules/TelegramCore/Sources/State/MessageReactions.swift | 3 +-- submodules/TelegramCore/Sources/Utils/MessageUtils.swift | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index a4a54d9b3c..34ddd0a942 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -508,9 +508,8 @@ public final class EngineMessageReactionListContext { for reaction in reactions { switch reaction { case let .messagePeerReaction(_, peer, date, reaction): - let _ = date if let peer = transaction.getPeer(peer.peerId), let reaction = MessageReaction.Reaction(apiReaction: reaction) { - items.append(EngineMessageReactionListContext.Item(peer: EnginePeer(peer), reaction: reaction, timestamp: nil)) + items.append(EngineMessageReactionListContext.Item(peer: EnginePeer(peer), reaction: reaction, timestamp: date)) } } } diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 8f6a87d262..7532230103 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -225,7 +225,9 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer], associ public extension Message { func effectivelyIncoming(_ accountPeerId: PeerId) -> Bool { - if self.id.peerId == accountPeerId { + if self.author?.id == accountPeerId { + return false + } else if self.id.peerId == accountPeerId { if self.forwardInfo != nil { return true } else { From 06666285d4b071cc79b3662124bb4655f7efd5d9 Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Wed, 8 Mar 2023 20:36:30 +0400 Subject: [PATCH 02/16] bug fixes --- .../Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift index 847f856cca..cf1d2e3c8b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift @@ -117,6 +117,8 @@ func _internal_updateChannelOwnership(account: Account, accountStateManager: Acc return .invalidPassword } else if error.errorDescription == "PASSWORD_MISSING" { return .twoStepAuthMissing + } else if error.errorDescription == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH" { + return .userPublicChannelsTooMuch } else if error.errorDescription.hasPrefix("PASSWORD_TOO_FRESH_") { let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "PASSWORD_TOO_FRESH_".count)...]) if let value = Int32(timeout) { From 589d9f71558c78e6c06848f64b3aef7ed13a6043 Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Thu, 9 Mar 2023 17:00:57 +0400 Subject: [PATCH 03/16] syntax fix --- ...tworkFrameworkTcpConnectionInterface.swift | 77 ++++++++++--------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/submodules/TelegramCore/Sources/Network/NetworkFrameworkTcpConnectionInterface.swift b/submodules/TelegramCore/Sources/Network/NetworkFrameworkTcpConnectionInterface.swift index 8ff07267bc..0919732bdd 100644 --- a/submodules/TelegramCore/Sources/Network/NetworkFrameworkTcpConnectionInterface.swift +++ b/submodules/TelegramCore/Sources/Network/NetworkFrameworkTcpConnectionInterface.swift @@ -231,45 +231,52 @@ final class NetworkFrameworkTcpConnectionInterface: NSObject, MTTcpConnectionInt self.processReadRequests() } else { connection.receive(minimumIncompleteLength: requestChunkLength, maximumLength: requestChunkLength, completion: { [weak self] data, context, isComplete, error in - guard let self = self, let currentReadRequest = self.currentReadRequest else { - return - } - if let data = data { - self.networkUsageManager?.addIncomingBytes(UInt(data.count), interface: self.currentInterfaceIsWifi ? MTNetworkUsageManagerInterfaceOther : MTNetworkUsageManagerInterfaceWWAN) - - if data.count != 0 && data.count <= currentReadRequest.request.length - currentReadRequest.readyLength { - currentReadRequest.data.withUnsafeMutableBytes { currentBuffer in - guard let currentBytes = currentBuffer.assumingMemoryBound(to: UInt8.self).baseAddress else { - return - } - data.copyBytes(to: currentBytes.advanced(by: currentReadRequest.readyLength), count: data.count) - } - currentReadRequest.readyLength += data.count - - let tag = currentReadRequest.request.tag - let readCount = data.count - weak var delegate = self.delegate - self.delegateQueue.async { - if let delegate = delegate { - delegate.connectionInterfaceDidReadPartialData(ofLength: UInt(readCount), tag: tag) - } - } - - self.processCurrentRead() - } else { - self.cancelWithError(error: error) - } - - if isComplete && data.count == 0 { - self.cancelWithError(error: nil) - } - } else { - self.cancelWithError(error: error) - } + self?.readWithBytes(data: data, isComplete: isComplete, error: error) }) } } + private func readWithBytes(data: Data?, isComplete: Bool, error: Error?) { + guard let currentReadRequest = self.currentReadRequest else { + return + } + if let data = data { + self.networkUsageManager?.addIncomingBytes(UInt(data.count), interface: self.currentInterfaceIsWifi ? MTNetworkUsageManagerInterfaceOther : MTNetworkUsageManagerInterfaceWWAN) + + if data.count != 0 && data.count <= currentReadRequest.request.length - currentReadRequest.readyLength { + + + currentReadRequest.data.withUnsafeMutableBytes { currentBuffer in + guard let currentBytes = currentBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } + data.copyBytes(to: currentBytes.advanced(by: currentReadRequest.readyLength), count: data.count) + } + currentReadRequest.readyLength += data.count + + let tag = currentReadRequest.request.tag + let readCount = data.count + weak var delegate = self.delegate + self.delegateQueue.async { + if let delegate = delegate { + delegate.connectionInterfaceDidReadPartialData(ofLength: UInt(readCount), tag: tag) + } + } + + self.processCurrentRead() + } else { + self.cancelWithError(error: error) + } + + if isComplete && data.count == 0 { + self.cancelWithError(error: nil) + } + } else { + self.cancelWithError(error: error) + } + + } + private func cancelWithError(error: Error?) { if let connectTimeoutTimer = self.connectTimeoutTimer { self.connectTimeoutTimer = nil From 7034b710ddd502efd0a19a18e5a8c90ac495a2dd Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Fri, 17 Mar 2023 09:24:31 +0400 Subject: [PATCH 04/16] bug fixes --- submodules/TelegramCore/Sources/Utils/MessageUtils.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 7532230103..774cdf4f87 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -225,7 +225,7 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer], associ public extension Message { func effectivelyIncoming(_ accountPeerId: PeerId) -> Bool { - if self.author?.id == accountPeerId { + if self.author?.id == accountPeerId, self.id.peerId != accountPeerId { return false } else if self.id.peerId == accountPeerId { if self.forwardInfo != nil { From 32ff62bcb5b27ba988b6f4f7790ce9780869043a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 22 Mar 2023 01:07:24 +0400 Subject: [PATCH 05/16] Various improvements --- .../Resources/ForumAvatarMask.tgs | Bin 0 -> 53841 bytes .../Telegram-iOS/en.lproj/Localizable.strings | 2 + ...rizationSequencePhoneEntryController.swift | 3 + ...tionSequencePhoneEntryControllerNode.swift | 11 + .../Sources/ChatListController.swift | 39 ++-- .../Navigation/NavigationContainer.swift | 8 +- .../Navigation/NavigationController.swift | 6 + .../MediaPickerUI/Sources/FetchAssets.swift | 25 ++- .../Sources/MediaPickerGridItem.swift | 84 ++----- .../Sources/MediaPickerScreen.swift | 76 ++++++- .../Sources/SparseItemGrid.swift | 6 + .../Sources/SparseItemGridScrollingArea.swift | 3 + .../ApiUtils/TelegramMediaAction.swift | 2 +- .../Panes/PeerInfoVisualMediaPaneNode.swift | 209 +++++++++++++++++- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 42 ++-- .../Sources/SharedMediaPlayer.swift | 3 + .../Sources/WebSearchController.swift | 24 +- 17 files changed, 422 insertions(+), 121 deletions(-) create mode 100644 Telegram/Telegram-iOS/Resources/ForumAvatarMask.tgs diff --git a/Telegram/Telegram-iOS/Resources/ForumAvatarMask.tgs b/Telegram/Telegram-iOS/Resources/ForumAvatarMask.tgs new file mode 100644 index 0000000000000000000000000000000000000000..232e96540a7d79ceb2ca0d074cefdc3d86e7836b GIT binary patch literal 53841 zcmV*0KzY9(iwFpmsu5%W19Ne8VRB_IYIARH0PMY6uQbbXCH7Z{^JD`Zjmmqb-fVb0 zuLj2Oi?A`EDasN=iJU`fJcEJ$yK}9Gtgg)3{q;FB><#R8Kal6^OI2Q~cdU$v6)XPR zPyhL+zxgR$-F9_9U4HuepMUzByj_0!$3Ok_H~l|2H$rm!JN@{`)Wg z+^_l%|LI?U`}6OA^Y8x4pZ@ssU;4#=_jiBCtA6|EKmYzOfBxxj{`SB9^pE|WpMLkp zfBw^d+-ZmWBuW`|MmAj>(|E}{_$V>f8FJ$zx>yq{wCUWzx&6ZfA@#q|Ho}} zIsfK=`aggE?eBj7cmH$0&7XhfSO2Ns;lKU#NBrM!d5?eUZ*REQf9d~l|91K5=jpTY z$<{yl)AY&Ty4Olp{oDE5+hyCoytr${DE(!+`hJW4I=lV`KhAi~S=dW{yWiq>KmE;Le*V|r|C??8 zzuAxA|M=}+{^2+Hq5ku~^jH7O|MtKByWjj@zyIA|{`m86{sVUJH}ac*|MMUJ^-ui^ z|Ijz=?|=J;KlfYMe*XO*e&T&k5AWjanZ$p)-1`M3{Q>nI|G)p4A96bX`#=8S51&ux z-~asEKmEf${;p35-}!Vtf5G$p(`h~*wGDe10BNpF+#HYsnD@_kH7dWuO& z!=&V4QqlvHlJA<7_+*n(HrSK4weM+A`X9avrT>LHk3WA2;p$5_j#u9zULzRq)rR2n z4gaj3f7jPq_(^d1W_10V73+hMs-fQ^@q+&S?055{!(mk3BPk|3 zH+>@GvJ~I;?bYmCe2RUG!@i|q-{J%NR_@xj@C8SvZiij%f9~#^+Bjdkd4N&da;5>f zHoS_SuUt2I_yhJEk37L$L*nZi)WY<`4`1_u6s{ozS{Xer)vHVV``>uE^Vi}74?nnh z-?pvat-lF_7R%M~ytcVOYv)5ZWBrECtX*zJha4Z=45zI@#v-@|8H?Z=#u9V|SU}{2 zi-&>FYDwooWto!96P9H;vM$$dCpuTNzq*RUC+=pD+s2=hg#I4|oW2CS5wA5Bf=3Gd zFxiik3D&g+eI2iX->31#AvYNA@Oa4;#0I@)xZk(9<5&6=d~|P{|7tcrJjLdRVe|L=V0d8j>s_1gzrs!KHosj{ z{;ua*->!u=kblM503ePd+_f~itf5si3v_}v?-#4)R!5pPC02Jcf?U1*zsE7LUE(#m zuza*w-kV*%*?7!Z#-zLL?S4F7w**VqO`@)WCv(ntOnC9yPr$POzx-f+vaU-OtOFA2 z{F=X6^xCiLKbCH>x2!RPjlZ70=aCjYlzu;&^yFxv<27wG>8bAVf<%+vLOZ_Ey@Y00 zgoM3{4ma!;*&`?s_2D#r7_ONP3tq;yzr=SpzkgY9tJwBiSu7*p#?s?`BqD8Z>;7`q z-9N>;`(fR~u}+w;Lja{S2R?IsxhU#yFSvD~K868Dl&8>vVP8j*YKM$B(fJ{KChl z(y!)xZoiOW^$+L%-Rd|04&=O?*>_Jd`)-(hKg_;+VD{a1*Y0mmk5pf`{>SazwZdQi zo_0T*Rn*vq3?r#$VpujFnY)2=&5pbI2bRCv+Cs};qZ?;xe8|9V=*FQR6{R9TVKJ7_ z50>3Z<22r1dFI~eLF{4l;SpRle$q`6f(eLK>~>$*AY%~@L7YZgw1uPL_f;K=e6#z= zqG@mfcb^B9WlAznSl0E(yj;13=4}nw7hU>naIo6(&GQQ_ z^Az7v0k3o4lVC=k8_|#GNnyZtrehL4pjPz?H|EiJ$NsHsjs^xCFZ;JsIeeR;o9Yku zL#STo7!eg3cE9SZf`1?PU(W7tPqF*ku={S<{q2$6zgs|(+85aU5HJ0t*TT28`{^2J z_1$ds_3Q@wTJeYPo3}%4`hnH|JV@>JtRIrUML@cROsT@8ISsQTwBaXjuJ2Sko4@UdX2!*Z%OL5c9{l}e@fwQp3VM=nCtQ^)L>-uEf zfM4bSAfkgHeK*&dWQh8c>yAfdS~hu&q1Qe%w|#RO_3zkFcan%xmth-Nz)R9K-h2d& zx~ClN9somfREU%n`Hz0;_u0@9bHIb!oHfLJi_zlx zF+VUKR9zfVj;-F0_x}JO`s)=Sz&~l%t*xnm>fh)7X@^h_^5C6EA~u*<7-#KjuM`y_bnX1(JxddW5fC0!=zW@Df7*~qd-*P@q7u# zkv@p%U&HOS^PJ=Dji8erH{h~rtG5vhM(S-6w|8{nZy0$o7^zQzkx~DZM*UZMRR87f z7J#Mld>&*st2KPrd~xbVGV`3SM?rVX5h6zJ>0c;Cg{R~k4q@_hSgF!HR{r@T=X!t; zkbF%+Ay?A!TAk zi_y;ai2iu2!hDX3H^A^wD9Frs(JQ%S8SvU0zMt5qzlnmy`ka7prJek8b%#o8g+c>Q5~PVTgaE5UC1{)jC@7x z`kSM}ux?HxP;nfH6PPpIW@^LX7r?$kpj)ra2mOcN^qWhXa#q*WlZI?C=U=&1rf%dBqMaxm#b2*R%t0{4)0= zbQG=rKs_aY_2Pk1AC}BHtVnKaC84*_*2hq71=Ftm&?Dg{W5Fw?WD5v6M8VX!2mW+H z81v+xrj8+qLV*}dm~mU_8{L`cDA_IZTLbhy(( zuEy&-lxTk4d=%uc%_G6<{bSs(6nDGd&Hu0wZ*Pn_3f2+0cwNUEh+Yhco>KjpM)hYJ z)t~86^{2aA{+XU#{yBiC^k2CD=17z@$J_0mp3+k_FTf`a6Dd;lH>#$%H}8HO5LMjc z5b7$-F4OX5BP{uB$&hqyK3vK*X5;%LGBh_=AA?UNwdW4_=rTL_MD@U&GoQ(|Nbo5- z)u`mCUl;$Hrzy)S7^%VM;x~%YW4Ees zLY~`Bm?h#kEA!YPr~6#dAHPz@Nn%4fq~HxLg$Uk2CZq$#r0%WM0}_spae2TmZCrkX zlrh2qWZYhT(k~kj>wSHr%&VcyQwoC9CRjG1RC%Od2kJ~=I3*6sNWV~%l0UF8X5 zvf12tja9Sy%@Qx&o8gVKv&;+NNZp!dAQRw=Od!)CFy$l2AlO$Y&!&w;*9OOo%m6@4 zW)drnprK(5wnS?I^A*AF{c~JRQm$Tp1C+M^&~}I!lj21?q`q_ZuLm(tsmV^GCOeIq z?DVK6+ube7PR}mNMzRID((j&Z(ID0iFWHZ1R3M4Bl<~%VlOp(~yU2&1Ce{KojNr$vY~-pctZ`{~d7-q^O8+6i?m#{OicdBIJ#1$YwQT z*UDgF&$e^TcGHwaiPB!(wIK2w65t z){T^9qtEE$J`1WfOVp%m(%S$NFW98Wa9C!>1kM*(0nRzT?qH=DXBiVrHyqP6ZZDK{ z8z3QOT%M6?VT3gE=fw0(cbJG_8*~h|O~K~)fvlIwbW=HvDW_=(CLXxbKK1lY<-8p5 zJf$!^jl%FW3d7T*!f8{VFqEWEb{c$p;2A9MkqQ_e}Zva%b32;!) zQjdVs5hEx<5eW)Q2fKG=fwjPm>P}4A@})a9gDKFiUQuNENY+bbzO$?Y%V}!jn5$BF z&Fx*d_;R50lzR0v>ebVzS5J@X)!p5K_4E}5>p5Qn4yNVX0uO3$`q{tDxML?r8cUWf zI>VhdN(9a08Fd$GW4iT=hpLQBX%HC>1B#_-;&U<~5q{i{zC$d^^a_Dlpb^6YDh0~% zqi%_nxdw(X0Y$|^{MA`4A$+lDP;o<=5S@BBgHOi9xShBo1xu2%xL>n6P9D;&NG1(f zn`3#?EM%FZtZR_N`ea?ZEPI2MP%HpYU#*OK*ruF3TzH~d&>3gCt^>BOvR`lxHrk_J~wyM zMI1Vai9iB|b5pTCva=dwS>fIg_mWCx@o@dXyP4-D^Q;jW%N=3h&p$gQI5Q ztVYfltiHZ>q*rHT!&r}*7_}qzTsksq9;@~_1ZYbPOq@Snua@=pa+-jg=GZS%RCV#q z9C$etdP=2!+6D@ZN`1WZV-&husUM$TsecJH8Q&CzZqYTTHi21-H0V-3lRvD<1Dqx| zjpW<|p`kop6;&9-(v%JCF8Qrm44H{ID7RzP1?+sWj82EX>QSr_=o`QX>BoEAKO_@+ zEy__iLr*?HPozxN-N>3#?Ac;e83IC2(7>8bIF&iFmhWTFJTW=US=RN)VXd-kgRI*m z({z!?HgeAox{ZtZ`3e(nFITboqBI~n>O;5Y-EV5ZV3VQV+;i zsCy!5LF<~>g90af&C!QzL0cWzL4_oG7mY+F(5xUuf|5aZLm_%79q=;+YD<_spi$)M z(gZ{9K395&q0pMTK$=hT)JAA#)=fro2cq;D5OMHjYI2;mtZR~K#j@^)Ec+$ve#)}n zvObOzKDkfm)O9m_a&VY9E=|9O3L3qlOt5lVY1(HDv^we#YZxyL5;fl#EIB}eqDCS! zCDVr~#%rJ~vA=NP7zx*cxNgC>`Q`FunQtZQNOGEsoTl3^$~!uz{C1IE4zZphGZ2T& zKpZjy@sZ4czbi8kpDifE*E-{;68l4sT$6&tE%tIU#GDIBCDK5sUIozUPQVLtpwBovXf2foNkDLkUFZ3uxY%M!j@%Ux`gtI0^`?1TQ53P_DAl z#2YgKuAD~16u&V~<#A^+R_dk~!1JTOi_;y#a0Fp~h!Cl%W+zN=d?l;np#tI$-LiCC?X?J(>Eiy)T=i`_W4?My5b14fNtpy3o z0clbTds4&17A1DGd6pw93|5$hJd7Hi?r&{QIo|}i z*!qm)1@|J=!~<2>tXX~4(;LeLvffbU@#Higndd85PB0k8HE)1=F@SoC+Cdy@2XUw! z#7Ale{;t|V{DS`Hp`Xag_vI{TSH)BepqN6k6a-QdRZ~cNt6*(`xcMEPT(El1gG7X$dfz1 zk&uDED`XJAK*%7v{#X5d(c_VOvzdD{`ja)&3_G|tan9ycBUx8HbT*DAbSFm=_o zA<`!DB`(nrq{9idQ5B4iFP1OLdLvm!lG9vdo@?LG!M4a~S@Z^|R|BZ02p+^Cco2u+ zL3|{5;O`0^#4iv$i2fTZ?PL{_$%;QaK3UBLl33-)HgKbHn%|Gy`mZ_o7PWs_(ELFl z!whP}aik=|P!7}T&Y-$#df3+X(+D_4^*>s-&ZI#QnKEp>1DCRq$xQSh!&M+GCDmr&HdI<*(!rQDisj=6fwy@wGR;)xRmi$XnfE|WdnD^F z%Dl;+M%{wO%-Gg!MWnXTn(uUG_*UqBqdp(mWuAqzib&=xMU!zW^g8N9gG1s*Q$l_l z2_w2wC7D|TM=PQ5D_`J7vQpVmhaCLyHe`3^HY_x|P&-&o`HdY2QzYNnm`Z*(0zzX)LVgWc_b_1jZ z{FM?j-b7QV43G7hiO;$29{R? zmZvBY#BBh}P$IxPKL(b&N(A8xln5edJKsL+WtP&SUZc=3Z1qTig7>k4OSak87PY3E%Vz&_5}T=u5ZU zo7(v>5Wp(-+cm{@H_~O*H8mvlLHx%p(iKM&Y35@_r0aI)c}!WRB**E>x)wRCQ;!DXJCK+2mF ze>Y@%IhYAg(H{sye*ox0meBWhs}il}Ph`1HPB)fyXqjim&j)n6x9rU8q0Cb%{==yF52NBg zJgWE)cPswG7gYR5XbFFFkjW|?HR53!?1z*A43{f|qwF)oV}3idm|ntTivo?=C~edd zhKvj-SILAS8%Spt4F_4q-g|x?t@N*-u1&NP6NPGtOXkj?!VyY1RyLUt%C~Bj#9~No zQhXy4t8E@cE=a-M_iS`ixF<3;B|#wuo{TE9?AWAvZg?;GkfJ#gnS=Qt$JxnY;<7GE zmPN~9KV;o2SvOOb&6fF5$nv1<5vu$_{G?>F6|?B@r#L`i#sfbU-Ga?tHrDntL6_+7^a#_F!)Te-5aI^^jf=jqH^)^j67?@Fs%h(Typf56Dg!D4^LHs!gBC za;+@4mUU=ZrY7^m`}dQ%Qjt#V8|7XM<(?uo5Qf-57-9qAk=Q`ED>e|mKx`m&YxV8J zaragFH3fUN$ z@)qg~j!lnquoOq?rW@VIOg4Ipa909P>poDJYzU9t0LIM#S0JG9s{k&5U`K#U6Tn$K z(P=0VR z)f=)kb;8g`y8m#3;Tk{c%ZWR%1&B5rfz27fC<=}S5+Gc|Jw%Uj7Kdi11+beb^7Qdj z$1gSXb!K z);s6{Y4%SUaoMxvB~`8WZzgh5)J{hfS+c`%wjSAF;x;guKgfK8{u>eq*)+NwX#$6g zQZ%cb+TDcGrw&e{1P!};PVixR2rZ`tK0YjaqzpMXm~C58=J4RfO%s-7L9#BGcZ6RF z{m{Kj_q2t!y9y!slO$6A#TbTkuf!ZDx+0X$xH&D+Su^gY-_@KUY_(#FOSimI-@A|w zpP~;NQDs9;(bM*8Kcd`CJrnElNR z0y9{20=<`_;1EF+F8z+yhBUj@O|35$hRfwbS!O~=@H9LU zOF)TY->&Ku`Z6M4dod?m4;IjmXM{g7q9 zgle;06`xxQ3+vqaS zP3DQqyegSiEQhU-b(>_~MOiml)~6urW3ne`yo*oLF>bTYI8gOd>R4eOEuzFV-cT%F zD_MLqo4UFO-zJav4k`JAYQU2mX!F`#>jqydC){x}d% zfo^*Qbay2U{1-?X^kaI-?wbQ$YWWCsG=tHZg?H9V83HRz=P1h;M5|-nkQ;KrkOPj# zY)(*_OlvRcsWV3bdatOL5TmM=JI@qqhPLI9S##a%Xx2BbFt1*a58$gB$ajvxBy=JZ zD{UJI46k{Yy()EBDyVneFD1%Hd^6H;*ht-;nA^czF;j6%Z5d{sfGqQqW%@ENPL`$1 zvNN)5oSb%6maUid(b!Y->eGIjC9*P{7%ZYkkLLne0+lQ^WXrp|wkhmc^PW1QgS8EC zgWsbSV)`juqpZ&>D`NoMF-GokvXq*(r;b>0MzhgE>L5HbBnRVg)AkqZH)Xk#oX3)N zIx^3epAVGB+TVEgT6pH4qMhJ}b^>6Gc;+8zC&atj3H}ST6OtA^ycC`(iwkih#j}xB z3LCj?u32y_`b9}$8?`ww!~lb=)V2yBgjn+EZ5@?0tKFyJ$uy*4VzQ)E-7JuZ#C?TH zXFRU59H$?-%U}?6!*WTZ9tQWLeoh$5I@JwAfGq7o7WTjpn0*(1+tf;nzGP6&0@Y$*&ok^-Kv!` z2T`Z~-K5$}zFEAm-6TxeG9=wTMTbO!Re?W}n1^fpLcb1O?gG}Yab7B7MayhefUzR< z?R{AI{j@LwR>kP!&`t`WaG zv^zIr(ayD~xg59_IHb?1EpP;^@o4bj))b(jyD5?|B_;ESJS*junT9|O4Q_3gR$(gP zT1Ll&)DOjwGYHy;yX1DsmO+OZr%(JDV~H{1ddT$7OAzOK6;HVp>WJ(ooyh11WpxdH;q3@NvSI3#&{NwKs- zTmn1JU?S8}O1!|uVHOR-2_2`Sw@|B`-dL}Y<&JV1QqI$odEWen#W>SDjrDS%_7sf< zKQtQr&}i_FG#cVvjRyY(8VwoZE#H+vXrN~0lzSE{7^s=lkjuj$Yd29PFw3xl)<6`- zakQLEGg%|TauoX*iY+)DLmY-Z$~rQ*3)KfcEGYy5n``pw{ezWLVYk_6D(W5hnv@>?381BR#52atvZIuOku+|Ngt9Jn%k->L zSw@-1mw9%w%w3kH$-Hn`_eJJylXX{R-F8_Xi$1WRa2EV$1omp4mRHt%C1NG~9{5=K z?@aLthwi2Qn<~i>_IRP&HCfRaMGFatbp4NpI8zIv%Bu5253V?ht}+A1ma3V~C)kCMNPsFH3>h|B>W>aiFj8{!heC9M5ZL>w+Fsird$mJ0x*MsxQh|h zsHw4G(ocj9#x-64MD3;qAK8=?cDY&d3p{4@)G11eXZELEqX`wU$Y_w%g2I!cQ1KTS zE)55w01Li?!W$|xs7Iuom})_5qi7UHa1FCJKpLGpd_U0V`@mOsCv9&yk>OU6#*wFz zu)(WTI(>HYK*#ZAnwc!qmU&S!uUHOyAiY-u`MRQjca}~rF(fq=c zNN~&Utp+e%C{`b^d^m5_#1k(}x?#?J`Oo#MK|XMI!eu9LEXBGC!FnFYD6euxyz(MArS1Wj|%zZ+#T> z!If2Vf~(yFtLm|+RCwxB)>8(;j?qGr3RfP!p2C#{D9eHTf$(LuPYcskePHd|rD{r0Pq1$T2n5fb|n9-J*`wg!iho$5XM; zZmwBB>W#LI<`gipm9aU`zO%ciTe&7m++}0NZ#k<0HYjSh5ad>2^`p~TtX%4)sC;%{ zXRBgWK#;`92RZGK&S2v%`oHDXV_6CdD5Y!iQ?96egn`%~RieEL> z)gID>*1dyb$u}j=RH4~~$ zV0AOu6+7Os!MM|4S%)EXK8*CK(4AsaIjLy)RoiGKMeWHuI9c1rF?va8gs-6*h(QCW z;G)zqS!d(4d7NbiGS5$z*~_vxnU^kyO_624WZ6zxc3ak`A?xGPC)pGX(`Py%Zdmp> z-~u_Ujx_D&6lN$(-oUJXRk^w@C~u|vXFIntp`hH`he~2Bh~R!`m*8NnJP2I{V=D88 zT-VgWzM^Mi9Ch|MHT2<}A$y-mQhAm2Gg+>c_1?#K5@t?6>%90-=$uL4b(m;}{5ShKYV| zd_RVnCm_rGWS+guOOtusvh0c+HcXbilx3@Bc@nZdDOsMRtWR2h1gtiJg6U^*RB!HK zQ1)(BR4jYwlrOaBh(qf&psB{I4KPQnQ{kpN;BCMu%G>F9ZCZl2>F+gqEAoIKzGtVf zLd43DW@&5#&Go{k?WYS!h>#})Pq8U+hq^$j4h2{`IQ25<-*!csJt-Il9Fyw)jnQ|=JpE5%wv7PvIs$d2|? zeVX1PooGlBE@*eF&Kv6Y_)%fpCu!#(QoV8Tt3S>~NTnLcAxiCA*dV|s((1@ifkEQ>W zm5@=;v8if`cH>DXC-~5~_4IoEzAX2X(}1#0N#+?7?Q|~^gQ{;ldj&kZ2Nr)dJR2G( zZfKmiM;a$7+{Lpm&^RgX8vTp$T$FOCC1bN=Xm#bW%xHFS4Nc?NS2smOlZy9ls&ESx zW)?#mA{B@_AvwbgBL>JY%g$7tu8s!=q9Qow+P|@Tt4ySm$GcNtdBz0XQfY((-J()GDF+PxgwJw-&t4G|SLL{!`(5tS70qTLsWs1)c7y{Pwg z!@FG>v1V-%P_zhY*u#VKW-+R3EJfV}^}ncPpG@Mnn+THVQ8J}1PSB_Q!nQ3M^XmRV zqOz6O{#&_Q;0ym>xr$(;pwVP9e=ecwEsjculnCN(U{2mE+Cq-M*kv=SMWFlTgFm=z zriJgLn_M;}X?2*iQcVyi^S@u`f{ntlhvNPLfWeLut z|2sx(R>lJM$7b#hKrDl8sxPV`T(%uoLthQd6$jwAsi)TH1!*ZjL-}`59tpu49R`qM zWA`0Su+OlD3%jst6x};_;Y_p&ars^>zF<-dOcYSoIWb6gRX{ z+|WjGkF-%zx{Fm`ppDY8s@3>SvFd?m)<(*1NQ#7}NR=j_I{0Nn-m4tlGO#xoO zm37Z$c_#X>_}n6E-Otd)l7wuf@*TJMl3m{ha&=EpnMoKbqlPdYa0@WH%lgAz(L%)j zDce=UqtPnHq>bXj_!$;}Wnr}-3kEWXBYo@8sDKZ4V^9$T#+`vOy|G>)>n-IpsGKJz z^UMXEe;HOSZ`$R{VcAoZQru8VaYHG^JyJ?Z`7V}yfl^9Ij6Qr%EW2AUu3!b~+)}wf zrv#M?ZGy8~<$@c6!-@x{L#Qpo&@5Sx;#aWSNbTpVpry}_h8OC?h5Afo^ci)~AcSg@ zB2D)wI#y`9E%pQT?M7hddy{_j!3wzB4m3GicB+>8iwni+ej$t(J}FADoS9E7JkMbs zXqke{6O?)OGObLOWy`uPvTU2on=0#`%lc5P=1w`Q<HrFEF|6-t{5XE_mjRI?K`04}R%YH*gI8f#+G z%ggoKvffTk1IjujnP;p}$F117_&1on7??dp{lpFR6F1aP+#~gqRPKV=7pR{wxBOj; z@a{NfZh^AVe5#u?RsPaStlOFX;F?+Ync(8lH)GeZVj&!aa&Vsx7)_%@jj1?~=FT6i zB5;+Iy-{cm4GI)*X~ZyeliJ_RN$c&l;^5<`SR<>;ARKJXY?3Cb{O+7NQ!IAr*N`NO zHYBp5_?vU4N}9R2**P<@K$gFia8;?}OsXOp=72-# zM%*@gCJ+OhO(GdOt`>;J7A+S)y|G>)r+dmWsGO%I^St>D^-W`V>zuqCm_0@3#0{Mj zH*`+iBb}2}?}FJE=$xSO{dWyAK5)#+ILW(mYK4FzFwWXS1{fX?xSlK=W*r<$UHk)<}UNfWL~zcyCSE3 zlXYih*?C!?jFmA)1+-6#->g4dN2+XM{J^bbO3?JqGSs2@!V%zHxj>ajwH%!mf%k(q zVZtiu`iC7Wv=@wLM^ogWI3w+e49`-nbbn0IdTOGoxuwZlD+cAc6e~45J8NNvx2Euh zTHv#|m-?n|>g#5#f_ZS6rzZ2fWnPplOO|; zmat{>-86Pb)I)bTb>#RNI)A4f_c|mh$=}?bFw&~PlMEG9`%tr4C|2OvAzgjZmUD!# z8oMh7rhOlbkHVSP>hyIV6+jIw6V<{*0x+Y7kJ~W4TE8pn&14--*6GN3#xkv_e?eDU z^SitD%YoxlR8QPcJ#j#^G~b@4_7tv1WO#UnU| zL*1_irL4he-c-%squon4x~5ziA_;m*n*ffaLnaO_yDrovE+&DJ*cru=Pz@?gKn|y` zQeljbg-KpZz7TrK3cFOHS9V39GzhB2tbzB|w2^2UUfF0A+P1Z&+=E)SYVF~Cvh3h3 zF$ZM;GEGXHhgl|o@MZtO_vz&wAlt={KIY&>GB2mGLYdbs^QOqUXIi7}bXQgIkA|zizJj86csnAfNNF5#}($;sdjt;ZGb zJin=0BI+SQa`NU6hHDw_pwlFop14TnPukCpI9`J`F-Oh(2U#mMHU^1tePc{m`=xTx z>l>NrI7pY+PujDFp21@+K0J*m=UK^d((w2U%@I?5gV<|<*!C2ml5Gf;0J{ic+asZp zd{?Ms`vReo>KStSo-lUT=^L57nr5%B!4<$?dzpdk;P;s_a&gQO<)GXV(zm;1RcT)| zG9#ZtOXfUl<3Sw>){+h~kTOo~P@D?vn%$}vYoT<^qvVWc6Szv1$Y@!_ZqJK`wz~h5 zsUT8zugfSLm6V%b<=i1zl<3^K)w%07Q8l~PVU`KVG&@T|5hFK+_i4V|Iwq^)) zd$gRfMe~zDbgT+#t!e~-zfWKqt1JSMHGnmPZWD_1%gg1bMSJDlD8vaE2XdK&w2 z{@|8HYj)aH`y%K|r{wlv8X`GBS0BMft0hi)5D(c%mt~`Vl$PCg8=ia_?G7@e9hD}U zucboXLtTUJ;()PVG;Nqrd$xJa?bTW?J6W*1e{oK`4Z~mDuk+L(W2kj<`RJ)xQ-r0_ zfpJS>|5hBMbH~}cV7OKe#7h`|n^A$7{@%p%xOh2i3r-p+zC&--y)!PfI51w`Mp!f% zWkrVyQF|YZ0z2(1o<=BldV>0F6-BP;)AG4$C}x^rggo@hI3iv%dFwzpbQ+>Gr>9@P z9T878Mn)1$Rgqg!mny-Nx1=6I!GL#dG|t-fi$^4yIY<^41Z&B$T?!gWXaIit1U^@v zF*GGDt=Vs!v0El}QZuU3H8QI2^trg89r)UmuNwl+(>jFT&Dv!4jR0mqnZJZDhJ;TM zG1-QQ$u>kxwnri+`L2k`_5~s)s4?BjH$}o5XVb!V^`PZkhu?N%Y_`qPd0|4rn_pMx z=Rk~CLt(1j9GL}l*6tZSIfFVM-rPQz2`CsF-;@3oEzJijyi;LqRNb_tP=hxFZAXnz zhD;1pZ!;Sr;zr&d9QJvhJ*`TQBpI(MJ|ENSQw^r?W2OaNJbyxzTYeqXhv% zpvaMG@HiOcWosi&kb^>2+(o)Fw7$&cL#tMGo;clx3j-XSW}Q+8f$l4%(igd9n@7lWl08Y>zZg z@?Fi7?F%$daQwr8{+mPFQjQP}+;Ul>mf7mW!vb!@P*(M%{57b=p*j)S%tv=(v>YKO zx)>_F+rL$_k?NU!Q%r5fANNI>h#7U!A4+C{AV|}^UV#8{G7XX|j@lhkhSV~|pwrdS zm24H7Bi7mn(O2ZN42)7Sw+iYRcLDE{B89RMkhZ;d-zZX0S1)+&G@j>irtz_`+(6?r z(nxNcw2=X?uG?b2YoyH!mT5C&-72(t4eT~>c46lmMKs43(H>>w7qdd2L7}tK0tJ+6<&Es(jZ(pmIN1m<=ww@2NucbH zitg!b!Kxh|YEo56$3M~!Epq^=$0ITMVG5RoogBCu^UL+y0LDh&87+S_Fw#g-Ds%&2%s#3v#d(DVwRodp> z$Szi`c(Beg=~RSXoL05f7H`f5+(~MIxMuobw2=K>|~z0EDMum-LmeB9QI7s{gh?DWqls9JTF-u zrz}sKzXL7D2+DI>pp?bz-E|CG-hnh-tub%ni{ir$q*@UGs8sy*2N-S8_%x7Jrb&NZ z%A^~DyL=|=#d5m6 zoMs^BS;}c8GOrYYRdd4;i+Mxpi$UsBq+PZl?XnGNm+g_XOTH`ZvVDQHOaD{J{@aIE zKPSyplOc0uv>bp>3Oq{9x^=Ihu<53=-sq9>u5OVG ze7a>)+M)uJcZIx1>#cpw+LdAS)k}2dYEl1&ivaz^Bw8w($kAU882>UTDvpf$4mheK z$-24=Lat5$8)3>mQ$MuFQZx*Sd6n$^vBorf3Wiu0wv9ThRa2fN&%-Peka>EtOkU=t z$+T=)_C)3#lXXL7*>G8&hpf*_)(0xf1DECZU_4>sqVrzslc4yNwvq3hUa#Mm^@g&JD5p8eY1%R`OXf8bipluFro6%U z^}zTk(lpzUrrCxx&Gtx|Cf}8&*}g!Urndeo|E|p%0#b4SV{4d`wapUfm>)_iDa!G& zo&XUXM%vV(E@j}W+GZ|@xw%0pCP90&Y8yBt@3KaGYYub(y!NKqR7C~EAM3#2fDjQ( z(+@T*iq@+(_F18v!Fi3&ZX0+%TUL4x2%9)I80vRR?@>CYQ-Wq^wXz7b=P7hJEpqH3 z{@U@&?H~)3(nu(y(pC=dEOH9K=tk3-)N?Q`wqyjI!~HI#vK~j1c`mZdROWTayhvHL zKyI5O%eKjBUuE5SSs#rok4}~+E63-4!_ST~MSWLIosN_!y0{Ye27qeO#Poe0;9}$( zNFJKPk){%Z_G|zbw^7(rzhB{6gRP6{@(kp;#XjS#ps)!Miqs*Rq%gqXR?uZS8qv7G zTXzE@kDtkUv8;EO)A(|lqRdN>Wr=S{d@)FTiptG4RBpDRasVDe`QFd*Rt7{P53e!?a)C06Ag(JF;E2UwfScmE@{;+2DH=NJ~s-J#a(?A;L}u| z63z??`ARjVoaB%~)XFlDULAZh?Sxbi8y#JSo{f9xxL$6xh(NU1uT|cSwu6G|TN+MX z1Yxt5(Ks-5Uls5chEsQ3wFD?@vuZPro`1OpZ8A6Fw}{vJJk;BA_tz8-bF+xT^o$;W zTQu700JrYfmK(MqFcXj1hOZx>t+-L17_1(!KnXxIoAASs2X*`GFl16mkhD|r z0WB86;9G*8=%$50zE9mlEOF?JuWzut-a!n{8e`y|JROXC!p#dv&38AijouLJTxD6$ z8^>M@$DSfcvkgI-Z3xnAj|6GTb{EIKK#-<6<%WDq9D5LFWtwRUvo`HI>+QrJmLO}c zs{C2YF?CrotHWJVmo8Oj@*wK+5jEmoc@Ys%BfW$wGyP_bS}E4x&yfU#&f;3@jxc2h zJ)aF7gTv`tg;W3xlrqZ}0COFMqa|BWxS+)wd$))o4GIAMm4Q^m!8X&e+XJcmjGonI z*7q>XJU&6IM$T^J5c|fsgM)lEX{@(I+M&`?m3-Zo zQFhT%@PqVR6CjeNw3w#RQ&4}SmimWiXj`b=5LL>%qB5g0SlP6#t>-ZyY?kBoJRiW_ z%X;oOy-`uvsaJexHnNd2oJS*Cj#+PG%6VFHp1aJ8({FI=GEa4HxF}o1@I-T{M=i7 z@q>X)6r?~Hh2MilG$K>l?=GNn!Wo$eQAO`93)Gialt`n47Zky1J`esuRq^d|$7k%; zu+fiFVYoeJsKH=(S)lMJ#kKpmRFqTsXi|MfN@*GgRG$qz*$@pXQ;z!#(VLZR1dU?b zzt({i+BB9zJT6@3U6I3n$+DHQ%LEviGiu2y8V&}Fm~NXEsSdGDMO-+pdc)~4@5iY< zd>XU5m}PM>aegXgs1LBtu)hPTr=kb0?$+{kRMk^w?{w@{k=vLdsm;8k_KN`G=vTaH zkds$~+-crMTgi>}jRES!45_y(o7DS&-Gd;xdpzvlf8pa|hK=$jF#L9^ZPrvr|7H3Z%eKG~vnVCjy+T3zhL@`a^PKvSTPoVaoTyb`#wq$j_+V4mV0h901M5&cfM59*Ecca*?sT*l8Plu6C8wWu>O~<>)QhJhO>u`+NIvM4T z=EH8@?SazIA+~G;k`y%XIuscQu|O#xEPeJqqU$b^w|)zR*f`2Zj^oHY3k0WQ;}nNh z9EvgX5PNbuSpM8eHQRiJ=}CF;|$#qPXIM$5BfvFiKUHc2R^048+0rW zS72c|uy}SfqC_R;ZAc0g#N(t`ywdz};_9Ww3Os&({}HsrPjezIBZ5Fp>nIyYrDI_*RO1kHPSN>0|`x5Oij zCo;$d2^jUcU>HJFe*=bCRl(>NpqMUdNVghq7EYl&El%;e7KFC$;%2f0$b4MckpQ7CGCZjvThG>EZF{4b8uaBy1j!3J8i!#PiMPm zedi~c$~M$&D#NW%aN{lDKSoQqI%wHdZ4|Te5Mdt*<>Xl3 zC&#}XbrcaO32CGR{G|fa$Mz!Giyl{s*2fRmE8*^S5B6|xIgc;rNy>2*Z=8BDoO*_~ zPBUqpX39G4k+4p=C#yk%LIEUT7vS7h0>>E_F3%k(5o+9j-bC>q8IfpJ^C$bA){MNn{N~iwK#Ln(65WH>WZP5eyikr#2`RfN#Y&stP^% zY%StnY@j4oMtBH*~_##Ij;N-aW4jO&(PRuCb82@VW&M3*eUk} zcDD8f0y_;gX5Te+_&kWSUVPTgf_lu^8aHg#HfL}`oR-feC2)d+nY3+#+pJRt{0{b9 z{U9N9(<)0Xu5)zQpfOW#Ws2THewjRoY%KxdTD1$@$uW(9Du(2&LzPRHV00kFsxHVV zSW)fV`-sge_Tmv7`dOIjNKY`bR>XF7Z#Idw~UgR!j;%^pJ?mp`xi%GanM?j+*usVPNtUWQ;WmicVYj z)$J8YC<~#~c)kwTaG9eV*6@a>7lWr~NZ>S6z-cCc(;n&HlzaL&Tl)h28_>QWxAmk@N~9Bt^jt6?$eX$oTAtP~ z?ldSjlyP6UPWpYs_?rj zz$)YG;;Y={%@5imYXr8zXHAM`+1$G2nGw3+x}2da4v)p4tEwm5_)pWAG33fbzkys( zR@L+!lyQ_AstP!6tKbuZgPE-1FOzrhe0ZYiSd+m6<&Ul^a;1J#01REDz}W)5OvBVu z-TlJKMDxb3y%?lDLrA8X zj!ZKdnf6FUrrcAJ+1eMV$ROk&>vxq&b);^Cgf^-Px9Ax1Fd&lw)tI8Nq+FrL~8Z`4kVQj-RywO6y ziHiU^E6GL(u64IYRlX}J{`gKkcBN=$8Z&XGTGhXctUq*?nNoOiQ#8|HZeph}%3J9n zV)wk|4p9=1F2(9rVr!1H7Zamhh6KcPpAemzJ{WlQn(?a^`E1>vLjq8{KlGuoQj3~# zQVq^=n0t2C=>Y|mc zfQEvzPO-hpkqzAnWnS^@0zLD3%Z $olL||9}t(=4jbFCmwHzfMa0EJ#vDYC7h-t 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ʡuDzLJwD{`7_K4DX64StR5c8Qy}sB` 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 literal 0 HcmV?d00001 diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 90e5b2b5ed..8ebea7479c 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9090,3 +9090,5 @@ Sorry for the inconvenience."; "PeerInfo.CancelSelectionAlertNo" = "No"; "StickerPacksSettings.SuggestAnimatedEmojiInfo" = "Each time you enter an emoji you can replace it with an animated emoji."; + +"DialogList.DeleteBotClearHistory" = "Clear Chat History"; diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryController.swift index fd51831d55..74fd3996ef 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryController.swift @@ -252,6 +252,9 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF } @objc func nextPressed() { + guard self.confirmationController == nil else { + return + } let (_, _, number) = self.controllerNode.codeAndNumber if !number.isEmpty { let logInNumber = cleanPhoneNumber(self.controllerNode.currentNumber, removePlus: true) diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift index 3aa3319f14..cd168e2cf3 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift @@ -759,6 +759,7 @@ final class PhoneConfirmationController: ViewController { private let codeTargetNode: ImmediateTextNode private let phoneTargetNode: ImmediateTextNode + private let measureTargetNode: ImmediateTextNode private let textNode: ImmediateTextNode private let textActivateAreaNode: AccessibilityAreaNode @@ -824,6 +825,10 @@ final class PhoneConfirmationController: ViewController { self.phoneTargetNode = ImmediateTextNode() self.phoneTargetNode.displaysAsynchronously = false + self.measureTargetNode = ImmediateTextNode() + self.measureTargetNode.displaysAsynchronously = false + self.measureTargetNode.maximumNumberOfLines = 1 + let targetString = NSMutableAttributedString(string: number, font: largeFont, textColor: theme.list.itemPrimaryTextColor) targetString.addAttribute(NSAttributedString.Key.kern, value: 1.6, range: NSRange(location: 0, length: sourceString.length)) self.phoneTargetNode.attributedText = targetString @@ -1015,6 +1020,12 @@ final class PhoneConfirmationController: ViewController { fontSize = 30.0 } + self.measureTargetNode.attributedText = NSAttributedString(string: self.code + " " + self.number, font: Font.with(size: fontSize, design: .regular, weight: .bold, traits: [.monospacedNumbers]), textColor: self.theme.list.itemPrimaryTextColor) + let measuredSize = self.measureTargetNode.updateLayout(CGSize(width: 1000.0, height: .greatestFiniteMagnitude)) + if measuredSize.width > maxWidth { + fontSize = floor(0.8 * fontSize) + } + let largeFont = Font.with(size: fontSize, design: .regular, weight: .bold, traits: [.monospacedNumbers]) self.codeTargetNode.attributedText = NSAttributedString(string: self.code, font: largeFont, textColor: self.theme.list.itemPrimaryTextColor) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 05e9622f3e..fa9cb9ab72 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -3385,7 +3385,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } else if case let .user(user) = chatPeer, user.botInfo != nil { canStop = !user.flags.contains(.isSupport) - canClear = user.botInfo == nil deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat } else if case .secretChat = chatPeer { canClear = true @@ -3450,6 +3449,22 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } else { items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + if canStop { + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_DeleteBotConversationConfirmation, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + if let strongSelf = self { + strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in + }, removed: { + guard let strongSelf = self else { + return + } + let _ = strongSelf.context.engine.privacy.requestUpdatePeerIsBlocked(peerId: peer.peerId, isBlocked: true).start() + }) + } + })) + } + if canClear { let beginClear: (InteractiveHistoryClearingType) -> Void = { type in guard let strongSelf = self else { @@ -3495,7 +3510,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }), in: .current) } - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_ClearHistoryConfirmation, color: .accent, action: { [weak actionSheet] in + items.append(ActionSheetButtonItem(title: canStop ? strongSelf.presentationData.strings.DialogList_DeleteBotClearHistory : strongSelf.presentationData.strings.DialogList_ClearHistoryConfirmation, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() guard let strongSelf = self else { @@ -3558,7 +3573,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController strongSelf.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: true, completion: { }) })) - } else { + } else if !canStop { items.append(ActionSheetButtonItem(title: deleteTitle, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() guard let strongSelf = self else { @@ -3630,23 +3645,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController })) } } - - if canStop { - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_DeleteBotConversationConfirmation, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - - if let strongSelf = self { - strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in - }, removed: { - guard let strongSelf = self else { - return - } - let _ = strongSelf.context.engine.privacy.requestUpdatePeerIsBlocked(peerId: peer.peerId, isBlocked: true).start() - }) - } - })) - } - + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in diff --git a/submodules/Display/Source/Navigation/NavigationContainer.swift b/submodules/Display/Source/Navigation/NavigationContainer.swift index 175ca8845b..97c2ed8050 100644 --- a/submodules/Display/Source/Navigation/NavigationContainer.swift +++ b/submodules/Display/Source/Navigation/NavigationContainer.swift @@ -82,6 +82,7 @@ public final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelega public private(set) var isReady: Bool = false public var isReadyUpdated: (() -> Void)? public var controllerRemoved: (ViewController) -> Void + public var requestFilterController: (ViewController) -> Void = { _ in } public var keyboardViewManager: KeyboardViewManager? { didSet { } @@ -118,6 +119,8 @@ public final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelega var statusBarStyle: StatusBarStyle = .Ignore var statusBarStyleUpdated: ((ContainedViewLayoutTransition) -> Void)? + + private var panRecognizer: InteractiveTransitionGestureRecognizer? public init(isFlat: Bool, controllerRemoved: @escaping (ViewController) -> Void) { @@ -211,7 +214,10 @@ public final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelega let topController = self.controllers[self.controllers.count - 1] let bottomController = self.controllers[self.controllers.count - 2] - if !topController.attemptNavigation({ + if !topController.attemptNavigation({ [weak self, weak topController] in + if let self, let topController { + self.requestFilterController(topController) + } }) { return } diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index 4668c529a8..0204774656 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -826,6 +826,9 @@ open class NavigationController: UINavigationController, ContainableController, let flatContainer = NavigationContainer(isFlat: self.isFlat, controllerRemoved: { [weak self] controller in self?.controllerRemoved(controller) }) + flatContainer.requestFilterController = { [weak self] controller in + self?.filterController(controller, animated: true) + } flatContainer.statusBarStyleUpdated = { [weak self] transition in guard let strongSelf = self else { return @@ -853,6 +856,9 @@ open class NavigationController: UINavigationController, ContainableController, let flatContainer = NavigationContainer(isFlat: self.isFlat, controllerRemoved: { [weak self] controller in self?.controllerRemoved(controller) }) + flatContainer.requestFilterController = { [weak self] controller in + self?.filterController(controller, animated: true) + } flatContainer.statusBarStyleUpdated = { [weak self] transition in guard let strongSelf = self else { return diff --git a/submodules/MediaPickerUI/Sources/FetchAssets.swift b/submodules/MediaPickerUI/Sources/FetchAssets.swift index ae336baedf..cacc356fcb 100644 --- a/submodules/MediaPickerUI/Sources/FetchAssets.swift +++ b/submodules/MediaPickerUI/Sources/FetchAssets.swift @@ -3,20 +3,28 @@ import UIKit import Photos import SwiftSignalKit -private let imageManager = PHCachingImageManager() +private let imageManager: PHCachingImageManager = { + let imageManager = PHCachingImageManager() + imageManager.allowsCachingHighQualityImages = false + return imageManager +}() + + private let assetsQueue = Queue() -func assetImage(fetchResult: PHFetchResult, index: Int, targetSize: CGSize, exact: Bool) -> Signal { +func assetImage(fetchResult: PHFetchResult, index: Int, targetSize: CGSize, exact: Bool, deliveryMode: PHImageRequestOptionsDeliveryMode = .opportunistic, synchronous: Bool = false) -> Signal { let asset = fetchResult[index] - return assetImage(asset: asset, targetSize: targetSize, exact: exact) + return assetImage(asset: asset, targetSize: targetSize, exact: exact, deliveryMode: deliveryMode, synchronous: synchronous) } -func assetImage(asset: PHAsset, targetSize: CGSize, exact: Bool) -> Signal { +func assetImage(asset: PHAsset, targetSize: CGSize, exact: Bool, deliveryMode: PHImageRequestOptionsDeliveryMode = .opportunistic, synchronous: Bool = false) -> Signal { return Signal { subscriber in let options = PHImageRequestOptions() + options.deliveryMode = deliveryMode if exact { options.resizeMode = .exact } + options.isSynchronous = synchronous let token = imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: options) { (image, info) in var degraded = false @@ -31,17 +39,15 @@ func assetImage(asset: PHAsset, targetSize: CGSize, exact: Bool) -> Signal runOn(assetsQueue) + } } func assetVideo(fetchResult: PHFetchResult, index: Int) -> Signal { @@ -49,7 +55,6 @@ func assetVideo(fetchResult: PHFetchResult, index: Int) -> Signal { subscriber in -// if let signal = editingContext.thumbnailImageSignal(forIdentifier: media.identifier) { -// let disposable = signal.start(next: { next in -// if let image = next as? UIImage { -// subscriber.putNext(image) -// } else { -// subscriber.putNext(nil) -// } -// }, error: { _ in -// }, completed: nil)! -// -// return ActionDisposable { -// disposable.dispose() -// } -// } else { -// return EmptyDisposable -// } -// } -// -// let originalImageSignal = Signal { subscriber in -// if let signal = asset?.thumbnailImageSignal?() -// } -// -// let scale = min(2.0, UIScreenScale) -// let targetSize = CGSize(width: 128.0 * scale, height: 128.0 * scale) -// let originalSignal: Signal = assetImage(fetchResult: fetchResult, index: index, targetSize: targetSize, exact: false) -// let imageSignal: Signal = editedSignal -// |> mapToSignal { result in -// if let result = result { -// return .single(result) -// } else { -// return originalSignal -// } -// } -// self.imageNode.setSignal(imageSignal) -// -// if case .video = media, let asset = media.asset as? TGCameraCapturedVideo { -// self.typeIconNode.image = UIImage(bundleImageName: "Media Editor/MediaVideo") -// -// if self.typeIconNode.supernode == nil { -// self.durationNode.attributedText = NSAttributedString(string: stringForDuration(Int32(asset.videoDuration)), font: Font.semibold(12.0), textColor: .white) -// -// self.addSubnode(self.gradientNode) -// self.addSubnode(self.typeIconNode) -// self.addSubnode(self.durationNode) -// self.setNeedsLayout() -// } -// } -// self.currentMediaState = (media.asset, index) self.setNeedsLayout() } @@ -319,10 +272,17 @@ final class MediaPickerGridItemNode: GridItemNode { return EmptyDisposable } } - + let scale = min(2.0, UIScreenScale) let targetSize = CGSize(width: 128.0 * scale, height: 128.0 * scale) - let originalSignal = assetImage(fetchResult: fetchResult, index: index, targetSize: targetSize, exact: false) + + let assetImageSignal = assetImage(fetchResult: fetchResult, index: index, targetSize: targetSize, exact: false, deliveryMode: .fastFormat, synchronous: true) + |> then( + assetImage(fetchResult: fetchResult, index: index, targetSize: targetSize, exact: false, deliveryMode: .highQualityFormat, synchronous: false) + |> delay(0.03, queue: Queue.concurrentDefaultQueue()) + ) + + let originalSignal = assetImageSignal //assetImage(fetchResult: fetchResult, index: index, targetSize: targetSize, exact: false, synchronous: true) let imageSignal: Signal = editedSignal |> mapToSignal { result in if let result = result { diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index fcc0125de6..41b97f65b3 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -196,6 +196,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { private var placeholderNode: MediaPickerPlaceholderNode? private var manageNode: MediaPickerManageNode? private var scrollingArea: SparseItemGridScrollingArea + private var isFastScrolling = false private var selectionNode: MediaPickerSelectedListNode? @@ -213,12 +214,21 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { private let hiddenMediaId = Promise(nil) + private var selectionGesture: MediaPickerGridSelectionGesture? + + private var fastScrollContentOffset = ValuePromise(ignoreRepeated: true) + private var fastScrollDisposable: Disposable? + private var didSetReady = false private let _ready = Promise() var ready: Promise { return self._ready } + fileprivate var isSuspended = false + private var hasGallery = false + private var isCameraPreviewVisible = true + private var validLayout: (ContainerViewLayout, CGFloat)? init(controller: MediaPickerScreen) { @@ -248,7 +258,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.backgroundNode) self.containerNode.addSubnode(self.gridNode) - //self.containerNode.addSubnode(self.scrollingArea) + self.containerNode.addSubnode(self.scrollingArea) let preloadPromise = self.preloadPromise let updatedState: Signal @@ -357,9 +367,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.hiddenMediaDisposable?.dispose() self.selectionChangedDisposable?.dispose() self.itemsDimensionsUpdatedDisposable?.dispose() + self.fastScrollDisposable?.dispose() } - private var selectionGesture: MediaPickerGridSelectionGesture? override func didLoad() { super.didLoad() @@ -402,16 +412,44 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { guard let strongSelf = self else { return nil } + strongSelf.controller?.requestAttachmentMenuExpansion() + strongSelf.isFastScrolling = true return strongSelf.gridNode.scrollView } + self.scrollingArea.finishedScrolling = { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.isFastScrolling = false + } self.scrollingArea.setContentOffset = { [weak self] offset in guard let strongSelf = self else { return } -// strongSelf.isFastScrolling = true - strongSelf.gridNode.scrollView.setContentOffset(offset, animated: false) -// strongSelf.isFastScrolling = false + Queue.concurrentDefaultQueue().async { + strongSelf.fastScrollContentOffset.set(offset) + } } + self.gridNode.visibleItemsUpdated = { [weak self] _ in + self?.updateScrollingArea() + + if let self, let cameraView = self.cameraView { + self.isCameraPreviewVisible = self.gridNode.scrollView.bounds.intersects(cameraView.frame) + self.updateIsCameraActive() + } + } + self.updateScrollingArea() + + let throttledContentOffsetSignal = self.fastScrollContentOffset.get() + |> mapToThrottled { next -> Signal in + return .single(next) |> then(.complete() |> delay(0.02, queue: Queue.concurrentDefaultQueue())) + } + self.fastScrollDisposable = (throttledContentOffsetSignal + |> deliverOnMainQueue).start(next: { [weak self] contentOffset in + if let self { + self.gridNode.scrollView.setContentOffset(contentOffset, animated: false) + } + }) if let controller = self.controller, case .assets(nil) = controller.subject { let enableAnimations = self.controller?.context.sharedContext.energyUsageSettings.fullTranslucency ?? true @@ -442,6 +480,15 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } } + func updateIsCameraActive() { + let isCameraActive = !self.isSuspended && !self.hasGallery && self.isCameraPreviewVisible + if isCameraActive { + self.cameraView?.resumePreview() + } else { + self.cameraView?.pausePreview() + } + } + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { if otherGestureRecognizer.view is UIScrollView || otherGestureRecognizer is UIPanGestureRecognizer { return true @@ -706,9 +753,11 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self?.controller?.present(c, in: .window(.root), with: a) }, finishedTransitionIn: { [weak self] in self?.openingMedia = false - self?.cameraView?.pausePreview() + self?.hasGallery = true + self?.updateIsCameraActive() }, willTransitionOut: { [weak self] in - self?.cameraView?.resumePreview() + self?.hasGallery = false + self?.updateIsCameraActive() }, dismissAll: { [weak self] in self?.controller?.dismissAll() }) @@ -742,9 +791,11 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self?.controller?.present(c, in: .window(.root), with: a, blockInteraction: true) }, finishedTransitionIn: { [weak self] in self?.openingMedia = false - self?.cameraView?.pausePreview() + self?.hasGallery = true + self?.updateIsCameraActive() }, willTransitionOut: { [weak self] in - self?.cameraView?.resumePreview() + self?.hasGallery = false + self?.updateIsCameraActive() }, dismissAll: { [weak self] in self?.controller?.dismissAll() }) @@ -1590,12 +1641,13 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } self.scrollToTop?() - self.controllerNode.cameraView?.pausePreview() + self.controllerNode.isSuspended = true + self.controllerNode.updateIsCameraActive() } public func prepareForReuse() { - self.controllerNode.cameraView?.resumePreview() - + self.controllerNode.isSuspended = false + self.controllerNode.updateIsCameraActive() self.controllerNode.updateNavigation(delayDisappear: true, transition: .immediate) } diff --git a/submodules/SparseItemGrid/Sources/SparseItemGrid.swift b/submodules/SparseItemGrid/Sources/SparseItemGrid.swift index 620fea0fcc..b407a91364 100644 --- a/submodules/SparseItemGrid/Sources/SparseItemGrid.swift +++ b/submodules/SparseItemGrid/Sources/SparseItemGrid.swift @@ -1350,6 +1350,12 @@ public final class SparseItemGrid: ASDisplayNode { self.currentViewport?.scrollView.isScrollEnabled = self.isScrollEnabled } } + + public func scrollWithDelta(_ delta: CGFloat) { + if let scrollView = self.currentViewport?.scrollView { + scrollView.setContentOffset(CGPoint(x: 0.0, y: scrollView.contentOffset.y + delta), animated: false) + } + } public init(theme: PresentationTheme, initialZoomLevel: ZoomLevel? = nil) { self.theme = theme diff --git a/submodules/SparseItemGrid/Sources/SparseItemGridScrollingArea.swift b/submodules/SparseItemGrid/Sources/SparseItemGridScrollingArea.swift index fa1099becf..9cb348c33d 100644 --- a/submodules/SparseItemGrid/Sources/SparseItemGridScrollingArea.swift +++ b/submodules/SparseItemGrid/Sources/SparseItemGridScrollingArea.swift @@ -948,6 +948,7 @@ public final class SparseItemGridScrollingArea: ASDisplayNode { private var activityTimer: SwiftSignalKit.Timer? public var beginScrolling: (() -> UIScrollView?)? + public var finishedScrolling: (() -> Void)? public var setContentOffset: ((CGPoint) -> Void)? public var openCurrentDate: (() -> Void)? @@ -1059,6 +1060,8 @@ public final class SparseItemGridScrollingArea: ASDisplayNode { strongSelf.updateLineIndicator(transition: transition) strongSelf.updateActivityTimer(isScrolling: false) + + strongSelf.finishedScrolling?() }, moved: { [weak self] relativeOffset in guard let strongSelf = self else { diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift index 40f921e862..5045fa2670 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift @@ -104,7 +104,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe components.append(.isHidden(hidden == .boolTrue)) } return TelegramMediaAction(action: .topicEdited(components: components)) - case let.messageActionSuggestProfilePhoto(photo): + case let .messageActionSuggestProfilePhoto(photo): return TelegramMediaAction(action: .suggestedProfilePhoto(image: telegramMediaImageFromApiPhoto(photo))) case let .messageActionRequestedPeer(buttonId, peer): return TelegramMediaAction(action: .requestedPeer(buttonId: buttonId, peerId: peer.peerId)) diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift index 7aaf2dd2ce..e18aeb60ea 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift @@ -2447,7 +2447,152 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro } private var gridSelectionGesture: MediaPickerGridSelectionGesture? - private var listSelectionGesture: MediaPickerGridSelectionGesture? + private var listSelectionGesture: MediaListSelectionRecognizer? + + override func didLoad() { + super.didLoad() + + let selectionRecognizer = MediaListSelectionRecognizer(target: self, action: #selector(self.selectionPanGesture(_:))) + selectionRecognizer.shouldBegin = { + return true + } + self.view.addGestureRecognizer(selectionRecognizer) + } + + private var selectionPanState: (selecting: Bool, initialMessageId: EngineMessage.Id, toggledMessageIds: [[EngineMessage.Id]])? + private var selectionScrollActivationTimer: SwiftSignalKit.Timer? + private var selectionScrollDisplayLink: ConstantDisplayLinkAnimator? + private var selectionScrollDelta: CGFloat? + private var selectionLastLocation: CGPoint? + + private func messageAtPoint(_ location: CGPoint) -> EngineMessage? { + if let itemView = self.itemGrid.item(at: location)?.view as? ItemView, let message = itemView.item?.message { + return EngineMessage(message) + } + return nil + } + + @objc private func selectionPanGesture(_ recognizer: UIGestureRecognizer) -> Void { + let location = recognizer.location(in: self.view) + switch recognizer.state { + case .began: + if let message = self.messageAtPoint(location) { + let selecting = !(self.chatControllerInteraction.selectionState?.selectedIds.contains(message.id) ?? false) + self.selectionPanState = (selecting, message.id, []) + self.chatControllerInteraction.toggleMessagesSelection([message.id], selecting) + } + case .changed: + self.handlePanSelection(location: location) + self.selectionLastLocation = location + case .ended, .failed, .cancelled: + self.selectionPanState = nil + self.selectionScrollDisplayLink = nil + self.selectionScrollActivationTimer?.invalidate() + self.selectionScrollActivationTimer = nil + self.selectionScrollDelta = nil + self.selectionLastLocation = nil + self.selectionScrollSkipUpdate = false + case .possible: + break + @unknown default: + fatalError() + } + } + + private func handlePanSelection(location: CGPoint) { + var location = location + if location.y < 0.0 { + location.y = 5.0 + } else if location.y > self.frame.height { + location.y = self.frame.height - 5.0 + } + + var hasState = false + if let state = self.selectionPanState { + hasState = true + if let message = self.messageAtPoint(location) { + if message.id == state.initialMessageId { + if !state.toggledMessageIds.isEmpty { + self.chatControllerInteraction.toggleMessagesSelection(state.toggledMessageIds.flatMap { $0.compactMap({ $0 }) }, !state.selecting) + self.selectionPanState = (state.selecting, state.initialMessageId, []) + } + } else if state.toggledMessageIds.last?.first != message.id { + var updatedToggledMessageIds: [[EngineMessage.Id]] = [] + var previouslyToggled = false + for i in (0 ..< state.toggledMessageIds.count) { + if let messageId = state.toggledMessageIds[i].first { + if messageId == message.id { + previouslyToggled = true + updatedToggledMessageIds = Array(state.toggledMessageIds.prefix(i + 1)) + + let messageIdsToToggle = Array(state.toggledMessageIds.suffix(state.toggledMessageIds.count - i - 1)).flatMap { $0 } + self.chatControllerInteraction.toggleMessagesSelection(messageIdsToToggle, !state.selecting) + break + } + } + } + + if !previouslyToggled { + updatedToggledMessageIds = state.toggledMessageIds + let isSelected = self.chatControllerInteraction.selectionState?.selectedIds.contains(message.id) ?? false + if state.selecting != isSelected { + updatedToggledMessageIds.append([message.id]) + self.chatControllerInteraction.toggleMessagesSelection([message.id], state.selecting) + } + } + + self.selectionPanState = (state.selecting, state.initialMessageId, updatedToggledMessageIds) + } + } + } + guard hasState else { + return + } + let scrollingAreaHeight: CGFloat = 50.0 + if location.y < scrollingAreaHeight || location.y > self.frame.height - scrollingAreaHeight { + if location.y < self.frame.height / 2.0 { + self.selectionScrollDelta = (scrollingAreaHeight - location.y) / scrollingAreaHeight + } else { + self.selectionScrollDelta = -(scrollingAreaHeight - min(scrollingAreaHeight, max(0.0, (self.frame.height - location.y)))) / scrollingAreaHeight + } + if let displayLink = self.selectionScrollDisplayLink { + displayLink.isPaused = false + } else { + if let _ = self.selectionScrollActivationTimer { + } else { + let timer = SwiftSignalKit.Timer(timeout: 0.45, repeat: false, completion: { [weak self] in + self?.setupSelectionScrolling() + }, queue: .mainQueue()) + timer.start() + self.selectionScrollActivationTimer = timer + } + } + } else { + self.selectionScrollDisplayLink?.isPaused = true + self.selectionScrollActivationTimer?.invalidate() + self.selectionScrollActivationTimer = nil + } + } + + private var selectionScrollSkipUpdate = false + private func setupSelectionScrolling() { + self.selectionScrollDisplayLink = ConstantDisplayLinkAnimator(update: { [weak self] in + self?.selectionScrollActivationTimer = nil + if let strongSelf = self, let delta = strongSelf.selectionScrollDelta { + let distance: CGFloat = 15.0 * min(1.0, 0.15 + abs(delta * delta)) + let direction: ListViewScrollDirection = delta > 0.0 ? .up : .down + let _ = strongSelf.itemGrid.scrollWithDelta(direction == .up ? -distance : distance) + + if let location = strongSelf.selectionLastLocation { + if !strongSelf.selectionScrollSkipUpdate { + strongSelf.handlePanSelection(location: location) + } + strongSelf.selectionScrollSkipUpdate = !strongSelf.selectionScrollSkipUpdate + } + } + }) + self.selectionScrollDisplayLink?.isPaused = false + } override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { let location = gestureRecognizer.location(in: gestureRecognizer.view) @@ -2717,3 +2862,65 @@ func updateVisualMediaStoredState(engine: TelegramEngine, peerId: PeerId, messag return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.visualMediaStoredState, id: key) } } + +private class MediaListSelectionRecognizer: UIPanGestureRecognizer { + private let selectionGestureActivationThreshold: CGFloat = 5.0 + + var recognized: Bool? = nil + var initialLocation: CGPoint = CGPoint() + + public var shouldBegin: (() -> Bool)? + + public override init(target: Any?, action: Selector?) { + super.init(target: target, action: action) + + self.minimumNumberOfTouches = 2 + self.maximumNumberOfTouches = 2 + } + + public override func reset() { + super.reset() + + self.recognized = nil + } + + public override func touchesBegan(_ touches: Set, with event: UIEvent) { + super.touchesBegan(touches, with: event) + + if let shouldBegin = self.shouldBegin, !shouldBegin() { + self.state = .failed + } else { + let touch = touches.first! + self.initialLocation = touch.location(in: self.view) + } + } + + public override func touchesMoved(_ touches: Set, with event: UIEvent) { + let location = touches.first!.location(in: self.view) + let translation = location.offsetBy(dx: -self.initialLocation.x, dy: -self.initialLocation.y) + + let touchesArray = Array(touches) + if self.recognized == nil, touchesArray.count == 2 { + if let firstTouch = touchesArray.first, let secondTouch = touchesArray.last { + let firstLocation = firstTouch.location(in: self.view) + let secondLocation = secondTouch.location(in: self.view) + + func distance(_ v1: CGPoint, _ v2: CGPoint) -> CGFloat { + let dx = v1.x - v2.x + let dy = v1.y - v2.y + return sqrt(dx * dx + dy * dy) + } + if distance(firstLocation, secondLocation) > 200.0 { + self.state = .failed + } + } + if self.state != .failed && (abs(translation.y) >= selectionGestureActivationThreshold) { + self.recognized = true + } + } + + if let recognized = self.recognized, recognized { + super.touchesMoved(touches, with: event) + } + } +} diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index ecd2286aeb..10bcfb2072 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -1125,8 +1125,9 @@ final class PeerInfoAvatarListNode: ASDisplayNode { } } - func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, isForum: Bool, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) { self.arguments = (peer, threadId, threadInfo, theme, avatarSize, isExpanded) + self.maskNode.isForum = isForum self.pinchSourceNode.update(size: size, transition: transition) self.pinchSourceNode.frame = CGRect(origin: CGPoint(), size: size) self.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: self.isSettings) @@ -2579,7 +2580,6 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.presentationData = presentationData let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) - let credibilityIcon: CredibilityIcon if let peer = peer { if peer.isFake { @@ -2599,6 +2599,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { credibilityIcon = .none } + var isForum = false + if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + isForum = true + } + if themeUpdated || self.currentCredibilityIcon != credibilityIcon { self.currentCredibilityIcon = credibilityIcon @@ -2742,7 +2747,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { transitionSourceAvatarFrame = avatarNavigationNode.avatarNode.view.convert(avatarNavigationNode.avatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view) } } else { - transitionSourceAvatarFrame = avatarFrame.offsetBy(dx: 0.0, dy: -avatarFrame.maxY).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) + if deviceMetrics.hasDynamicIsland { + transitionSourceAvatarFrame = CGRect(origin: CGPoint(x: avatarFrame.minX, y: -20.0), size: avatarFrame.size).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) + } else { + transitionSourceAvatarFrame = avatarFrame.offsetBy(dx: 0.0, dy: -avatarFrame.maxY).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) + } } transitionSourceTitleFrame = navigationTransition.sourceTitleFrame transitionSourceSubtitleFrame = navigationTransition.sourceSubtitleFrame @@ -3207,12 +3216,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { transition.updateAlpha(node: subtitleArrowNode, alpha: (1.0 - titleCollapseFraction)) } } - - var isForum = false - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { - isForum = true - } - + let avatarCornerRadius: CGFloat = isForum ? floor(avatarSize * 0.25) : avatarSize / 2.0 if self.isAvatarExpanded { @@ -3240,7 +3244,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { }) } - self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, threadId: self.forumTopicThreadId, threadInfo: threadData?.info, theme: presentationData.theme, transition: transition) + self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, isForum: isForum, threadId: self.forumTopicThreadId, threadInfo: threadData?.info, theme: presentationData.theme, transition: transition) self.editingContentNode.avatarNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) self.avatarOverlayNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) if additive { @@ -3305,7 +3309,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale) } - if deviceMetrics.hasDynamicIsland && !isForum && self.forumTopicThreadId == nil { + if deviceMetrics.hasDynamicIsland && self.forumTopicThreadId == nil { self.avatarListNode.maskNode.frame = CGRect(origin: CGPoint(x: -85.5, y: -self.avatarListNode.frame.minY + 48.0), size: CGSize(width: 171.0, height: 171.0)) self.avatarListNode.bottomCoverNode.frame = self.avatarListNode.maskNode.frame self.avatarListNode.topCoverNode.frame = self.avatarListNode.maskNode.frame @@ -3677,15 +3681,27 @@ final class PeerInfoHeaderNode: ASDisplayNode { private class DynamicIslandMaskNode: ManagedAnimationNode { var frameIndex: Int = 0 + var isForum = false { + didSet { + if self.isForum != oldValue { + self.update(frameIndex: self.frameIndex) + } + } + } + func update(_ value: CGFloat) { let lowerBound = 0 let upperBound = 180 let frameIndex = lowerBound + Int(value * CGFloat(upperBound - lowerBound)) if frameIndex != self.frameIndex { - self.frameIndex = frameIndex - self.trackTo(item: ManagedAnimationItem(source: .local("UserAvatarMask"), frames: .range(startFrame: frameIndex, endFrame: frameIndex), duration: 0.001)) + self.update(frameIndex: frameIndex) } } + + func update(frameIndex: Int) { + self.frameIndex = frameIndex + self.trackTo(item: ManagedAnimationItem(source: .local(self.isForum ? "ForumAvatarMask" : "UserAvatarMask"), frames: .range(startFrame: frameIndex, endFrame: frameIndex), duration: 0.001)) + } } private class DynamicIslandBlurNode: ASDisplayNode { diff --git a/submodules/TelegramUI/Sources/SharedMediaPlayer.swift b/submodules/TelegramUI/Sources/SharedMediaPlayer.swift index a522db9696..979f24b4b6 100644 --- a/submodules/TelegramUI/Sources/SharedMediaPlayer.swift +++ b/submodules/TelegramUI/Sources/SharedMediaPlayer.swift @@ -374,6 +374,9 @@ final class SharedMediaPlayer { strongSelf.forceAudioToSpeaker = forceAudioToSpeaker strongSelf.playbackItem?.setForceAudioToSpeaker(forceAudioToSpeaker) if !forceAudioToSpeaker { + if let playbackStateValue = strongSelf._playbackStateValue, case let .item(item) = playbackStateValue, item.status.timestamp < 1.5 { + strongSelf.control(.seek(0.0)) + } strongSelf.control(.playback(.play)) } else { strongSelf.control(.playback(.pause)) diff --git a/submodules/WebSearchUI/Sources/WebSearchController.swift b/submodules/WebSearchUI/Sources/WebSearchController.swift index 7d8bfe60a7..7c0bcfe3f0 100644 --- a/submodules/WebSearchUI/Sources/WebSearchController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchController.swift @@ -122,6 +122,9 @@ public final class WebSearchController: ViewController { public var attemptItemSelection: (ChatContextResult) -> Bool = { _ in return true } + private var searchQueryPromise = ValuePromise() + private var searchQueryDisposable: Disposable? + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?, configuration: EngineConfiguration.SearchBots, mode: WebSearchControllerMode, activateOnDisplay: Bool = true) { self.context = context self.mode = mode @@ -195,7 +198,7 @@ public final class WebSearchController: ViewController { self.navigationContentNode = navigationContentNode navigationContentNode.setQueryUpdated { [weak self] query in if let strongSelf = self, strongSelf.isNodeLoaded { - strongSelf.updateSearchQuery(query) + strongSelf.searchQueryPromise.set(query) strongSelf.searchingUpdated(!query.isEmpty) } } @@ -288,6 +291,24 @@ public final class WebSearchController: ViewController { } }) } + + let throttledSearchQuery = self.searchQueryPromise.get() + |> mapToSignal { query -> Signal in + if !query.isEmpty { + return (.complete() |> delay(0.6, queue: Queue.mainQueue())) + |> then(.single(query)) + } else { + return .single(query) + } + } + + self.searchQueryDisposable = (throttledSearchQuery + |> deliverOnMainQueue).start(next: { [weak self] query in + if let self { + self.updateSearchQuery(query) + } + }) + } required public init(coder aDecoder: NSCoder) { @@ -298,6 +319,7 @@ public final class WebSearchController: ViewController { self.disposable?.dispose() self.resultsDisposable.dispose() self.selectionDisposable?.dispose() + self.searchQueryDisposable?.dispose() } public func cancel() { From 272f53806034266ab33028358245c4d05133004e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 22 Mar 2023 16:11:37 +0400 Subject: [PATCH 06/16] Various improvements --- .../Resources/ForumAvatarMask.tgs | Bin 53841 -> 2115 bytes .../Telegram-iOS/Resources/UserAvatarMask.tgs | Bin 50117 -> 2495 bytes .../Sources/CallListControllerNode.swift | 9 +- .../Sources/CallListNodeEntries.swift | 2 +- .../Sources/LocationSearchContainerNode.swift | 2 +- .../Sources/TelegramBaseController.swift | 8 +- .../GroupCallNavigationAccessoryPanel.swift | 12 ++ .../Sources/PresentationGroupCall.swift | 12 +- .../TelegramUI/Sources/ChatController.swift | 158 +++++++++--------- .../ChatMessageInteractiveMediaNode.swift | 16 +- .../Sources/ChatMessageNotificationItem.swift | 17 +- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 2 +- .../Sources/WebSearchController.swift | 2 +- submodules/rlottie/LottieInstance.mm | 2 +- 14 files changed, 127 insertions(+), 115 deletions(-) diff --git a/Telegram/Telegram-iOS/Resources/ForumAvatarMask.tgs b/Telegram/Telegram-iOS/Resources/ForumAvatarMask.tgs index 232e96540a7d79ceb2ca0d074cefdc3d86e7836b..9872f65326618aa15dcfec68f89af25d0d937a33 100644 GIT binary patch literal 2115 zcmV-J2)y?niwFp^)f!{~14eIhb!|a*VRT_~O<{9uE^2dcZUF6C+in{-5d9TnpLZ9$ zEN_16H7L-BqAB`f7=>!fv0+>6pzLxN1257v8Z>iemn!gwKFzUPI{pv))sLRXCs6N|l z=G#qkdiJrofhWxcDEuKBR{wdqcysdlueY=Hyzq24`!HYAm}KT=0pGH1wjY|)fd~IO zzu3+eZ|>$N0S0f@QQ;QGeQcKaF^eN_;jusqe?vn)w9UHeOFaU5R#nfLbfT56H?24) zC79$Le1hhz))cMCN-`^LCguPI|7TUps%UT-^)}bD)tqLIGi~0iVP-d5TD}D>MeNvI zH`(27awN$SsNw_Y6usBFZ9Z@d{48^iw2phA1wG)znFyK;oSnrv!DlCX?Ikp+5ubK) z=m&G8W^$DLBsb>~?^)@0RH8wnkr%ZJ`(5b(5WFJ z7PRSYmIQxS4@>phQt3oy>=_SWyl76h>$mfV^N04?!I#1x7X3lCo#>6U`~cu+nyio0 z>f|Kg__&o>Tc@?eH;mWi1w3FIg=vBzt4K!-olJ_1nA&XM>0B7X$cA1BQ!#=?;Q$Ty zISDU+5;*v{!3dhX4BU)eV^2A(v{$<-QB(+z#SiIT#E-VKPvZuJq_v-k8?F5`FVKHy zU&0HcGB04TP!%x-M+5ith86Vs$f(YkHrjq7&Bx* z08Rz!LND~flu9w(gvQ);>!}A6Z&z~@Z6M2^>{TK0+S#mYED^U+f$a5hU6RGS=r2 zBAuEk4oTt|`Jo^L36S?)AE<5Mxk()YPt$@W6`Dr{o&;I`Nej*K6#C$s*~>I)q44M1q#=f&FR(b zZZnT;z#7%nT@y|3RLbyx72OPJpV%$c8LHI5@!`+=*csvccyo8RAI_`wY;}Ee0f10v zFP<$H(^flpiYf1w^-@vq(Q+Ou=*J3+_VFp=Ubwm4Hwf2dGuzi=Qeb{t&la1j<@&zC znC+>DE>^K%dBKHCUPUe$?peNR6wfCW-vJtRKaBC*DbnuLPR}T%6`$uZi|u({WVzz9zFIjNOj;ak*%g<5G4t z&b&B18=pV}*()j0MbWn=J0!*N@=>DIZM7e5ODCKrDQY znyPs&bd0VEv@@t)Y8wmYLs~$obVYLwD}oFfbfEBKDu#i6P>Fh_53H#&zwt`pd+!Y` zPHkA?Mb_g^sC~)r*4MOFOzX<)1QgSHbTOURWZosK$$l40!Rgf>2pAzxbRMmDj6FkV zItz!PrQ3kQZ4H=bwa+qeM|0g!LIUs>D^$+MW%VJNH1LW@x-&o;FuJvo$3=Upm`yo0 zRlAx1!MlY?D7~N%NN?DflHdp~7W8^WNoR4YzULB&d7)&Y!8>)}wBBjmTQMd|WTQm| z-bUcn^SFx;H9^_cL=?S(dm2_lWF9Ty-bDM98bE(!VH@a@g#{pVPreI#@ef6E7@3Dd zLU96#CKa>CwhWU{GDx;Gnr4h$0!cF*L5j&jba8yETMHaWCy%*U?nNR%kNbLRD(t3XNQ}xrP<6m=?vB>c! t+;iR0ev9-T$bi4|y2f(tu>1QjUPN9!2*qrDON)2@@DCPs6~IF#003lR|84*P literal 53841 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 $olL||9}t(=4jbFCmwHzfMa0EJ#vDYC7h-t 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ʡuDzLJwD{`7_K4DX64StR5c8Qy}sB` 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 diff --git a/Telegram/Telegram-iOS/Resources/UserAvatarMask.tgs b/Telegram/Telegram-iOS/Resources/UserAvatarMask.tgs index 4bd975f15c0af62042350b002289aa0d51a82c7c..25a907fe02d474f2811201454b5d7476f7cbbfb5 100644 GIT binary patch literal 2495 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 JVY&D+004m4)t>+W literal 50117 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%0A< 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 diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index 9579a547a3..da0a55f504 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -690,13 +690,13 @@ final class CallListControllerNode: ASDisplayNode { let alpha: CGFloat = isHidden ? 0.0 : 1.0 let previousAlpha = self.emptyTextNode.alpha self.emptyTextNode.alpha = alpha - self.emptyTextNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.2) + self.emptyTextNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.25) if previousAlpha.isZero && !alpha.isZero { self.emptyAnimationNode.visibility = true } self.emptyAnimationNode.alpha = alpha - self.emptyAnimationNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.2, completion: { [weak self] _ in + self.emptyAnimationNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.25, completion: { [weak self] _ in if let strongSelf = self { if !previousAlpha.isZero && strongSelf.emptyAnimationNode.alpha.isZero { strongSelf.emptyAnimationNode.visibility = false @@ -705,9 +705,9 @@ final class CallListControllerNode: ASDisplayNode { }) self.emptyButtonIconNode.alpha = alpha - self.emptyButtonIconNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.2) + self.emptyButtonIconNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.25) self.emptyButtonTextNode.alpha = alpha - self.emptyButtonTextNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.2) + self.emptyButtonTextNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.25) self.emptyButtonNode.isUserInteractionEnabled = !isHidden if !isHidden { @@ -733,7 +733,6 @@ final class CallListControllerNode: ASDisplayNode { } self.emptyTextNode.attributedText = NSAttributedString(string: emptyText, font: textFont, textColor: color, paragraphAlignment: .center) - self.emptyButtonTextNode.attributedText = NSAttributedString(string: buttonText, font: buttonFont, textColor: theme.list.itemAccentColor, paragraphAlignment: .center) if let layout = self.containerLayout { diff --git a/submodules/CallListUI/Sources/CallListNodeEntries.swift b/submodules/CallListUI/Sources/CallListNodeEntries.swift index 47f83e21e0..d4a917251e 100644 --- a/submodules/CallListUI/Sources/CallListNodeEntries.swift +++ b/submodules/CallListUI/Sources/CallListNodeEntries.swift @@ -230,7 +230,7 @@ func countMeaningfulCallListEntries(_ entries: [CallListNodeEntry]) -> Int { var count: Int = 0 for entry in entries { switch entry.stableId { - case .setting, .groupCall: + case .setting: break default: count += 1 diff --git a/submodules/LocationUI/Sources/LocationSearchContainerNode.swift b/submodules/LocationUI/Sources/LocationSearchContainerNode.swift index f6c79e6238..4438cb06a3 100644 --- a/submodules/LocationUI/Sources/LocationSearchContainerNode.swift +++ b/submodules/LocationUI/Sources/LocationSearchContainerNode.swift @@ -156,7 +156,7 @@ final class LocationSearchContainerNode: ASDisplayNode { let searchItems = self.searchQuery.get() |> mapToSignal { query -> Signal in if let query = query, !query.isEmpty { - return (.complete() |> delay(0.6, queue: Queue.mainQueue())) + return (.complete() |> delay(1.0, queue: Queue.mainQueue())) |> then(.single(query)) } else { return .single(query) diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 5d344a7a3c..c085b6ccec 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -303,7 +303,13 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { disposable.set((callContext.context.panelData |> deliverOnMainQueue).start(next: { panelData in callContext.keep() - subscriber.putNext(panelData) + var updatedPanelData = panelData + if let panelData { + var updatedInfo = panelData.info + updatedInfo.subscribedToScheduled = activeCall.subscribedToScheduled + updatedPanelData = panelData.withInfo(updatedInfo) + } + subscriber.putNext(updatedPanelData) })) } diff --git a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift index 94bd233639..3adde6596e 100644 --- a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift +++ b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift @@ -64,6 +64,18 @@ public final class GroupCallPanelData { self.activeSpeakers = activeSpeakers self.groupCall = groupCall } + + public func withInfo(_ info: GroupCallInfo) -> GroupCallPanelData { + return GroupCallPanelData( + peerId: self.peerId, + isChannel: self.isChannel, + info: info, + topParticipants: self.topParticipants, + participantCount: self.participantCount, + activeSpeakers: self.activeSpeakers, + groupCall: self.groupCall + ) + } } private final class FakeAudioLevelGenerator { diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index d327f0853d..9af33d1ca0 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -116,12 +116,12 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { activeSpeakers: Set(), groupCall: nil )))*/ - + let state = engine.calls.getGroupCallParticipants(callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: nil) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } self.disposable = (combineLatest(queue: .mainQueue(), state, @@ -139,7 +139,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { state: state, previousServiceState: nil ) - + strongSelf.participantsContext = context strongSelf.panelDataPromise.set(combineLatest(queue: .mainQueue(), context.state, diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 9726aa319d..c9cb7853bb 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3910,7 +3910,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { - legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, initialCaption: NSAttributedString(string: message.text), snapshots: [], transitionCompletion: nil, getCaptionPanelView: { [weak self] in + let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText + legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, initialCaption: inputText, snapshots: [], transitionCompletion: nil, getCaptionPanelView: { [weak self] in return self?.getCaptionPanelView() }, sendMessagesWithSignals: { [weak self] signals, _, _ in if let strongSelf = self { @@ -8139,16 +8140,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self, let peerId = strongSelf.chatLocation.peerId { let presentationData = strongSelf.presentationData - let forwardOptions: Signal - if peerId.namespace == Namespaces.Peer.SecretChat { - forwardOptions = .single(ChatControllerSubject.ForwardOptions(hideNames: true, hideCaptions: false)) - } else { - forwardOptions = strongSelf.presentationInterfaceStatePromise.get() - |> map { state -> ChatControllerSubject.ForwardOptions in - return ChatControllerSubject.ForwardOptions(hideNames: state.interfaceState.forwardOptionsState?.hideNames ?? false, hideCaptions: state.interfaceState.forwardOptionsState?.hideCaptions ?? false) + let forwardOptions = strongSelf.presentationInterfaceStatePromise.get() + |> map { state -> ChatControllerSubject.ForwardOptions in + var hideNames = state.interfaceState.forwardOptionsState?.hideNames ?? false + if peerId.namespace == Namespaces.Peer.SecretChat { + hideNames = true } - |> distinctUntilChanged + return ChatControllerSubject.ForwardOptions(hideNames: hideNames, hideCaptions: state.interfaceState.forwardOptionsState?.hideCaptions ?? false) } + |> distinctUntilChanged let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .forwardedMessages(peerIds: [peerId], ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], options: forwardOptions), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) @@ -8208,88 +8208,90 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - let canHideNames = hasNotOwnMessages && hasOther - + var canHideNames = hasNotOwnMessages && hasOther + if case let .peer(peerId) = strongSelf.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { + canHideNames = false + } let hideNames = forwardOptions.hideNames let hideCaptions = forwardOptions.hideCaptions - if case let .peer(peerId) = strongSelf.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { + if canHideNames { + items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_ShowSendersName : presentationData.strings.Conversation_ForwardOptions_ShowSendersNames, icon: { theme in + if hideNames { + return nil + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } + }, action: { [weak self] _, f in + self?.interfaceInteraction?.updateForwardOptionsState({ current in + var updated = current + updated.hideNames = false + updated.hideCaptions = false + updated.unhideNamesOnCaptionChange = false + return updated + }) + }))) - } else { - if canHideNames { - items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_ShowSendersName : presentationData.strings.Conversation_ForwardOptions_ShowSendersNames, icon: { theme in - if hideNames { - return nil - } else { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } - }, action: { [weak self] _, f in - self?.interfaceInteraction?.updateForwardOptionsState({ current in - var updated = current - updated.hideNames = false - updated.hideCaptions = false - updated.unhideNamesOnCaptionChange = false - return updated - }) - }))) - - items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_HideSendersName : presentationData.strings.Conversation_ForwardOptions_HideSendersNames, icon: { theme in - if hideNames { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } else { - return nil - } - }, action: { _, f in - self?.interfaceInteraction?.updateForwardOptionsState({ current in - var updated = current - updated.hideNames = true - updated.unhideNamesOnCaptionChange = false - return updated - }) - }))) - - items.append(.separator) - } + items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_HideSendersName : presentationData.strings.Conversation_ForwardOptions_HideSendersNames, icon: { theme in + if hideNames { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } else { + return nil + } + }, action: { _, f in + self?.interfaceInteraction?.updateForwardOptionsState({ current in + var updated = current + updated.hideNames = true + updated.unhideNamesOnCaptionChange = false + return updated + }) + }))) - if hasCaptions { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ShowCaption, icon: { theme in - if hideCaptions { - return nil - } else { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } - }, action: { [weak self] _, f in - self?.interfaceInteraction?.updateForwardOptionsState({ current in - var updated = current - updated.hideCaptions = false + items.append(.separator) + } + + if hasCaptions { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ShowCaption, icon: { theme in + if hideCaptions { + return nil + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } + }, action: { [weak self] _, f in + self?.interfaceInteraction?.updateForwardOptionsState({ current in + var updated = current + updated.hideCaptions = false + if canHideNames { if updated.unhideNamesOnCaptionChange { updated.unhideNamesOnCaptionChange = false updated.hideNames = false } - return updated - }) - }))) - - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_HideCaption, icon: { theme in - if hideCaptions { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } else { - return nil } - }, action: { _, f in - self?.interfaceInteraction?.updateForwardOptionsState({ current in - var updated = current - updated.hideCaptions = true + return updated + }) + }))) + + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_HideCaption, icon: { theme in + if hideCaptions { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } else { + return nil + } + }, action: { _, f in + self?.interfaceInteraction?.updateForwardOptionsState({ current in + var updated = current + updated.hideCaptions = true + if canHideNames { if !updated.hideNames { updated.hideNames = true updated.unhideNamesOnCaptionChange = true } - return updated - }) - }))) - - items.append(.separator) - } + } + return updated + }) + }))) + + items.append(.separator) } items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ChangeRecipient, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { c, f in @@ -11370,7 +11372,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } var layout = layout - if case .compact = layout.metrics.widthClass, let _ = self.attachmentController { + if case .compact = layout.metrics.widthClass, let attachmentController = self.attachmentController, attachmentController.window != nil { layout = layout.withUpdatedInputHeight(nil) } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index c241ca3da9..1e6ed41220 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -1687,8 +1687,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio state = .none badgeContent = nil } else if wideLayout { - if let size = file.size { - let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(size, forceDecimal: true, formatting: formatting))" + if let size = file.size, size > 0 && size != .max { + let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(size, forceDecimal: true, formatting: formatting))" if let duration = file.duration, !message.flags.contains(.Unsent) { let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition) if isMediaStreamable(message: message, media: file) { @@ -1721,8 +1721,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio state = automaticPlayback ? .none : state } } else { - if isMediaStreamable(message: message, media: file), let size = file.size { - let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(size, forceDecimal: true, formatting: formatting))" + if isMediaStreamable(message: message, media: file), let fileSize = file.size, fileSize > 0 && fileSize != .max { + let sizeString = "\(dataSizeString(Int64(Float(fileSize) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(fileSize, forceDecimal: true, formatting: formatting))" if message.flags.contains(.Unsent), let duration = file.duration { let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition) @@ -1749,8 +1749,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio if let duration = file.duration, !file.isAnimated { let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition) - if automaticPlayback, let size = file.size { - let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(size, forceDecimal: true, formatting: formatting))" + if automaticPlayback, let fileSize = file.size, fileSize > 0 && fileSize != .max { + let sizeString = "\(dataSizeString(Int64(Float(fileSize) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(fileSize, forceDecimal: true, formatting: formatting))" mediaDownloadState = .fetching(progress: progress) badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: active ? sizeString : nil, muted: muted, active: active) } else { @@ -1800,9 +1800,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio do { let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : (file.duration ?? 0), position: playerPosition) if wideLayout { - if isMediaStreamable(message: message, media: file) { + if isMediaStreamable(message: message, media: file), let fileSize = file.size, fileSize > 0 && fileSize != .max { state = automaticPlayback ? .none : .play(messageTheme.mediaOverlayControlColors.foregroundColor) - badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: dataSizeString(file.size ?? 0, formatting: formatting), muted: muted, active: true) + badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: dataSizeString(fileSize, formatting: formatting), muted: muted, active: true) mediaDownloadState = .remote } else { state = automaticPlayback ? .none : state diff --git a/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift b/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift index 662a37a751..311ab53b93 100644 --- a/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift @@ -263,19 +263,16 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { } else if item.messages[0].id.peerId.namespace == Namespaces.Peer.CloudGroup { isGroup = true } + title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) if isChannel { switch kind { case .image: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHANNEL_MESSAGE_PHOTOS_TEXT(Int32(item.messages.count)) case .video: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHANNEL_MESSAGE_VIDEOS_TEXT(Int32(item.messages.count)) case .file: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHANNEL_MESSAGE_DOCS_TEXT(Int32(item.messages.count)) default: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHANNEL_MESSAGES_TEXT(Int32(item.messages.count)) } } else if isGroup, var author = item.messages[0].author { @@ -284,31 +281,23 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { } switch kind { case .image: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHAT_MESSAGE_PHOTOS_TEXT(Int32(item.messages.count)).replacingOccurrences(of: "{author}", with: EnginePeer(author).compactDisplayTitle) case .video: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHAT_MESSAGE_VIDEOS_TEXT(Int32(item.messages.count)).replacingOccurrences(of: "{author}", with: EnginePeer(author).compactDisplayTitle) case .file: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHAT_MESSAGE_DOCS_TEXT(Int32(item.messages.count)).replacingOccurrences(of: "{author}", with: EnginePeer(author).compactDisplayTitle) default: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHAT_MESSAGES_TEXT(Int32(item.messages.count)).replacingOccurrences(of: "{author}", with: EnginePeer(author).compactDisplayTitle) } } else { switch kind { case .image: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_MESSAGE_PHOTOS_TEXT(Int32(item.messages.count)) case .video: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_MESSAGE_VIDEOS_TEXT(Int32(item.messages.count)) case .file: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_MESSAGE_FILES_TEXT(Int32(item.messages.count)) default: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_MESSAGES_TEXT(Int32(item.messages.count)) } } @@ -325,6 +314,10 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { title = "📅 \(currentTitle)" } + if let attribute = item.messages.first?.attributes.first(where: { $0 is NotificationInfoMessageAttribute }) as? NotificationInfoMessageAttribute, attribute.flags.contains(.muted), let currentTitle = title { + title = "\(currentTitle) 🔕" + } + let textFont = compact ? Font.regular(15.0) : Font.regular(16.0) let textColor = presentationData.theme.inAppNotification.primaryTextColor var attributedMessageText: NSAttributedString diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 10bcfb2072..00170656e8 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -3691,7 +3691,7 @@ private class DynamicIslandMaskNode: ManagedAnimationNode { func update(_ value: CGFloat) { let lowerBound = 0 - let upperBound = 180 + let upperBound = 540 let frameIndex = lowerBound + Int(value * CGFloat(upperBound - lowerBound)) if frameIndex != self.frameIndex { self.update(frameIndex: frameIndex) diff --git a/submodules/WebSearchUI/Sources/WebSearchController.swift b/submodules/WebSearchUI/Sources/WebSearchController.swift index 7c0bcfe3f0..18e494c496 100644 --- a/submodules/WebSearchUI/Sources/WebSearchController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchController.swift @@ -295,7 +295,7 @@ public final class WebSearchController: ViewController { let throttledSearchQuery = self.searchQueryPromise.get() |> mapToSignal { query -> Signal in if !query.isEmpty { - return (.complete() |> delay(0.6, queue: Queue.mainQueue())) + return (.complete() |> delay(1.0, queue: Queue.mainQueue())) |> then(.single(query)) } else { return .single(query) diff --git a/submodules/rlottie/LottieInstance.mm b/submodules/rlottie/LottieInstance.mm index 61ac431c8f..f9da05da3e 100755 --- a/submodules/rlottie/LottieInstance.mm +++ b/submodules/rlottie/LottieInstance.mm @@ -61,7 +61,7 @@ _dimensions = CGSizeMake(width, height); - if ((_frameRate > 60) || _animation->duration() > 7.0) { + if ((_frameRate > 60) || _animation->duration() > 9.0) { return nil; } } From 4018360cab4089fadecc0ee1e20cdc341016a88b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 22 Mar 2023 17:48:22 +0400 Subject: [PATCH 07/16] Various fixes --- submodules/AnimationUI/BUILD | 1 + .../AnimationUI/Sources/AnimationNode.swift | 15 +++++++- .../Sources/ViewControllerComponent.swift | 21 +++++++----- .../PremiumUI/Sources/PremiumGiftScreen.swift | 2 +- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 34 +++++++++++-------- 5 files changed, 49 insertions(+), 24 deletions(-) diff --git a/submodules/AnimationUI/BUILD b/submodules/AnimationUI/BUILD index c4701284a7..adf8470d17 100644 --- a/submodules/AnimationUI/BUILD +++ b/submodules/AnimationUI/BUILD @@ -13,6 +13,7 @@ swift_library( "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/rlottie:RLottieBinding", "//submodules/lottie-ios:Lottie", + "//submodules/GZip:GZip", "//submodules/AppBundle:AppBundle", "//submodules/Display:Display", ], diff --git a/submodules/AnimationUI/Sources/AnimationNode.swift b/submodules/AnimationUI/Sources/AnimationNode.swift index e15a0cdc5a..c7f9391e20 100644 --- a/submodules/AnimationUI/Sources/AnimationNode.swift +++ b/submodules/AnimationUI/Sources/AnimationNode.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import AsyncDisplayKit import Lottie +import GZip import AppBundle import Display @@ -32,7 +33,15 @@ public final class AnimationNode: ASDisplayNode { super.init() self.setViewBlock({ - if let animationName = animationName, let url = getAppBundle().url(forResource: animationName, withExtension: "json"), let animation = Animation.filepath(url.path) { + var animation: Animation? + if let animationName { + if let url = getAppBundle().url(forResource: animationName, withExtension: "json"), let maybeAnimation = Animation.filepath(url.path) { + animation = maybeAnimation + } else if let url = getAppBundle().url(forResource: animationName, withExtension: "tgs"), let data = try? Data(contentsOf: URL(fileURLWithPath: url.path)), let unpackedData = TGGUnzipData(data, 5 * 1024 * 1024) { + animation = try? Animation.from(data: unpackedData, strategy: .codable) + } + } + if let animation { let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable)) view.animationSpeed = self.speed view.backgroundColor = .clear @@ -104,6 +113,10 @@ public final class AnimationNode: ASDisplayNode { self.animationView()?.currentProgress = 1.0 } + public func setProgress(_ progress: CGFloat) { + self.animationView()?.currentProgress = progress + } + public func setAnimation(name: String, colors: [String: UIColor]? = nil) { self.currentParams = (name, colors) if let url = getAppBundle().url(forResource: name, withExtension: "json"), let animation = Animation.filepath(url.path) { diff --git a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift index ece9265938..0266f7142a 100644 --- a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift +++ b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift @@ -20,6 +20,11 @@ open class ViewControllerComponentContainer: ViewController { case `default` } + public enum PresentationMode { + case `default` + case modal + } + public final class Environment: Equatable { public let statusBarHeight: CGFloat public let navigationHeight: CGFloat @@ -139,11 +144,6 @@ open class ViewControllerComponentContainer: ViewController { func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: Transition) { self.currentLayout = (layout, navigationHeight) - var theme = self.theme ?? self.presentationData.theme - if theme.list.blocksBackgroundColor.rgb == theme.list.plainBackgroundColor.rgb { - theme = theme.withModalBlocksBackground() - } - let environment = ViewControllerComponentContainer.Environment( statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, @@ -152,7 +152,7 @@ open class ViewControllerComponentContainer: ViewController { metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, isVisible: self.currentIsVisible, - theme: theme, + theme: self.theme ?? self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, controller: { [weak self] in @@ -203,7 +203,7 @@ open class ViewControllerComponentContainer: ViewController { private var presentationDataDisposable: Disposable? public private(set) var validLayout: ContainerViewLayout? - public init(context: AccountContext, component: C, navigationBarAppearance: NavigationBarAppearance, statusBarStyle: StatusBarStyle = .default, theme: PresentationTheme? = nil) where C.EnvironmentType == ViewControllerComponentContainer.Environment { + public init(context: AccountContext, component: C, navigationBarAppearance: NavigationBarAppearance, statusBarStyle: StatusBarStyle = .default, presentationMode: PresentationMode = .default, theme: PresentationTheme? = nil) where C.EnvironmentType == ViewControllerComponentContainer.Environment { self.context = context self.component = AnyComponent(component) self.theme = theme @@ -224,7 +224,12 @@ open class ViewControllerComponentContainer: ViewController { self.presentationDataDisposable = (self.context.sharedContext.presentationData |> deliverOnMainQueue).start(next: { [weak self] presentationData in if let strongSelf = self { - strongSelf.node.presentationData = presentationData + var theme = presentationData.theme + if case .modal = presentationMode, theme.list.blocksBackgroundColor.rgb == theme.list.plainBackgroundColor.rgb { + theme = theme.withModalBlocksBackground() + } + + strongSelf.node.presentationData = presentationData.withUpdated(theme: theme) switch statusBarStyle { case .none: diff --git a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift index 777e0f9e2c..adf80c4f64 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift @@ -1032,7 +1032,7 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer, Attachme completion: { duration in completionImpl?(duration) } - ), navigationBarAppearance: .transparent) + ), navigationBarAppearance: .transparent, presentationMode: .modal) let presentationData = context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 00170656e8..aa21e6fffb 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -1044,7 +1044,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode { self.bottomCoverNode = ASDisplayNode() self.bottomCoverNode.backgroundColor = .black - self.maskNode = DynamicIslandMaskNode(size: CGSize(width: 512.0, height: 512.0)) + self.maskNode = DynamicIslandMaskNode() self.pinchSourceNode = PinchSourceContainerNode() self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context) @@ -3678,29 +3678,35 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } -private class DynamicIslandMaskNode: ManagedAnimationNode { - var frameIndex: Int = 0 +private class DynamicIslandMaskNode: ASDisplayNode { + private var animationNode: AnimationNode? var isForum = false { didSet { if self.isForum != oldValue { - self.update(frameIndex: self.frameIndex) + self.animationNode?.removeFromSupernode() + let animationNode = AnimationNode(animation: "ForumAvatarMask") + self.addSubnode(animationNode) + self.animationNode = animationNode } } } - func update(_ value: CGFloat) { - let lowerBound = 0 - let upperBound = 540 - let frameIndex = lowerBound + Int(value * CGFloat(upperBound - lowerBound)) - if frameIndex != self.frameIndex { - self.update(frameIndex: frameIndex) - } + override init() { + let animationNode = AnimationNode(animation: "UserAvatarMask") + self.animationNode = animationNode + + super.init() + + self.addSubnode(animationNode) } - func update(frameIndex: Int) { - self.frameIndex = frameIndex - self.trackTo(item: ManagedAnimationItem(source: .local(self.isForum ? "ForumAvatarMask" : "UserAvatarMask"), frames: .range(startFrame: frameIndex, endFrame: frameIndex), duration: 0.001)) + func update(_ value: CGFloat) { + self.animationNode?.setProgress(value) + } + + override func layout() { + self.animationNode?.frame = self.bounds } } From 935d43a77f01e051910edb21aafc1f602cfb2999 Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Thu, 23 Mar 2023 14:48:04 +0400 Subject: [PATCH 08/16] - bug fix --- submodules/TelegramCore/Sources/Utils/MessageUtils.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index c2ff906576..8f6a87d262 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -225,7 +225,7 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer], associ public extension Message { func effectivelyIncoming(_ accountPeerId: PeerId) -> Bool { - if self.author?.id == accountPeerId, self.id.peerId != accountPeerId { + if self.id.peerId == accountPeerId { if self.forwardInfo != nil { return true } else { From af36deaac62b358a08fe85b988a30eb460f4993b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Mar 2023 19:39:55 +0400 Subject: [PATCH 09/16] Add iPad 9th generation model --- submodules/MtProtoKit/Sources/MTApiEnvironment.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/submodules/MtProtoKit/Sources/MTApiEnvironment.m b/submodules/MtProtoKit/Sources/MTApiEnvironment.m index ffdcb74d6f..e39218c327 100644 --- a/submodules/MtProtoKit/Sources/MTApiEnvironment.m +++ b/submodules/MtProtoKit/Sources/MTApiEnvironment.m @@ -711,6 +711,10 @@ NSString *suffix = @""; [platform isEqualToString:@"iPad11,7"]) return @"iPad (8th gen)"; + if ([platform isEqualToString:@"iPad12,1"] || + [platform isEqualToString:@"iPad12,2"]) + return @"iPad (9th gen)"; + if ([platform isEqualToString:@"iPad13,1"] || [platform isEqualToString:@"iPad13,2"]) return @"iPad Air (4th gen)"; From 3eb2ccb1a5fd6c3ba89e15268b2b8a55cf8f11c7 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Mar 2023 19:43:11 +0400 Subject: [PATCH 10/16] Ignore topic data in chat list and notifications when chat is not a forum anymore --- .../TelegramCore/Sources/State/AccountStateManager.swift | 5 ++++- .../Sources/TelegramEngine/Messages/ChatList.swift | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramCore/Sources/State/AccountStateManager.swift b/submodules/TelegramCore/Sources/State/AccountStateManager.swift index aeb5bf16f0..5b1a321b0d 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManager.swift @@ -1026,7 +1026,7 @@ public final class AccountStateManager { } let _ = (signal - |> deliverOn(self.queue)).start(next: { [weak self] messages in + |> deliverOn(self.queue)).start(next: { [weak self] messages in if let strongSelf = self { strongSelf.notificationMessagesPipe.putNext(messages) } @@ -1953,6 +1953,9 @@ public func messagesForNotification(transaction: Transaction, id: MessageId, alw } if let channel = message.peers[message.id.peerId] as? TelegramChannel { + if !channel.flags.contains(.isForum) { + threadData = nil + } switch channel.participationStatus { case .kicked, .left: return ([], false, sound, false, threadData) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift index 96d56034ed..8fbfbca7e5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift @@ -457,8 +457,13 @@ extension EngineChatList.Item { let readCounters = readState.flatMap(EnginePeerReadCounters.init) - if let channel = renderedPeer.peer as? TelegramChannel, channel.flags.contains(.isForum) { - draft = nil + if let channel = renderedPeer.peer as? TelegramChannel { + if channel.flags.contains(.isForum) { + draft = nil + } else { + forumTopicDataValue = nil + topForumTopicItems = [] + } } self.init( From da558c40261c1e74c5eb382c47056e5b981b5344 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Mar 2023 19:44:09 +0400 Subject: [PATCH 11/16] Fix replying to threads from notifications --- submodules/TelegramUI/Sources/AppDelegate.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 6c233f21da..3c145fc513 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -2401,7 +2401,7 @@ private func extractAccountManagerState(records: AccountRecordsView map { messageIds -> MessageId? in if messageIds.isEmpty { return nil From d700f45c1c9f4bbc1f3d6d938ec79ad7e438bb2f Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Mar 2023 19:46:09 +0400 Subject: [PATCH 12/16] Do not show group message notifications in CarPlay Navigate to original message on tapping reaction notifications --- .../Sources/NotificationService.swift | 7 +- .../TelegramUI/Sources/AppDelegate.swift | 185 +++++++----------- .../Sources/ApplicationContext.swift | 57 +++--- 3 files changed, 110 insertions(+), 139 deletions(-) diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index 794b4bcdc1..f3a1f0d7f5 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -1054,7 +1054,12 @@ private final class NotificationServiceHandler { } if let category = aps["category"] as? String { - content.category = category + if peerId.isGroupOrChannel && ["r", "m"].contains(category) { + content.category = "g\(category)" + } else { + content.category = category + } + let _ = messageId diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 3c145fc513..bf0e6f878e 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -2395,7 +2395,7 @@ private func extractAccountManagerState(records: AccountRecordsView Void = { _ in }) { - if #available(iOS 10.0, *) { - let notificationCenter = UNUserNotificationCenter.current() - Logger.shared.log("App \(self.episodeId)", "register for notifications: get settings (authorize: \(authorize))") - notificationCenter.getNotificationSettings(completionHandler: { settings in - Logger.shared.log("App \(self.episodeId)", "register for notifications: received settings: \(settings.authorizationStatus)") - - switch (settings.authorizationStatus, authorize) { - case (.authorized, _), (.notDetermined, true): - var authorizationOptions: UNAuthorizationOptions = [.badge, .sound, .alert, .carPlay] - if #available(iOS 12.0, *) { - authorizationOptions.insert(.providesAppNotificationSettings) - } - if #available(iOS 13.0, *) { - authorizationOptions.insert(.announcement) - } - Logger.shared.log("App \(self.episodeId)", "register for notifications: request authorization") - notificationCenter.requestAuthorization(options: authorizationOptions, completionHandler: { result, _ in - Logger.shared.log("App \(self.episodeId)", "register for notifications: received authorization: \(result)") - completion(result) - if result { - Queue.mainQueue().async { - let reply = UNTextInputNotificationAction(identifier: "reply", title: replyString, options: [], textInputButtonTitle: replyString, textInputPlaceholder: messagePlaceholderString) - - let unknownMessageCategory: UNNotificationCategory - let replyMessageCategory: UNNotificationCategory - let replyLegacyMessageCategory: UNNotificationCategory - let replyLegacyMediaMessageCategory: UNNotificationCategory - let replyMediaMessageCategory: UNNotificationCategory - let legacyChannelMessageCategory: UNNotificationCategory - let muteMessageCategory: UNNotificationCategory - let muteMediaMessageCategory: UNNotificationCategory - - if #available(iOS 11.0, *) { - var options: UNNotificationCategoryOptions = [] - if includeNames { - options.insert(.hiddenPreviewsShowTitle) - } - - var carPlayOptions = options - carPlayOptions.insert(.allowInCarPlay) - if #available(iOS 13.2, *) { - carPlayOptions.insert(.allowAnnouncement) - } - - unknownMessageCategory = UNNotificationCategory(identifier: "unknown", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options) - replyMessageCategory = UNNotificationCategory(identifier: "withReply", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions) - replyLegacyMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions) - replyLegacyMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions) - replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions) - legacyChannelMessageCategory = UNNotificationCategory(identifier: "c", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options) - muteMessageCategory = UNNotificationCategory(identifier: "withMute", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options) - muteMediaMessageCategory = UNNotificationCategory(identifier: "withMuteMedia", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options) - } else { - let carPlayOptions: UNNotificationCategoryOptions = [.allowInCarPlay] - - unknownMessageCategory = UNNotificationCategory(identifier: "unknown", actions: [], intentIdentifiers: [], options: []) - replyMessageCategory = UNNotificationCategory(identifier: "withReply", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], options: carPlayOptions) - replyLegacyMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], options: carPlayOptions) - replyLegacyMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [], options: []) - replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], options: carPlayOptions) - legacyChannelMessageCategory = UNNotificationCategory(identifier: "c", actions: [], intentIdentifiers: [], options: []) - muteMessageCategory = UNNotificationCategory(identifier: "withMute", actions: [], intentIdentifiers: [], options: []) - muteMediaMessageCategory = UNNotificationCategory(identifier: "withMuteMedia", actions: [], intentIdentifiers: [], options: []) + let notificationCenter = UNUserNotificationCenter.current() + Logger.shared.log("App \(self.episodeId)", "register for notifications: get settings (authorize: \(authorize))") + notificationCenter.getNotificationSettings(completionHandler: { settings in + Logger.shared.log("App \(self.episodeId)", "register for notifications: received settings: \(settings.authorizationStatus)") + + switch (settings.authorizationStatus, authorize) { + case (.authorized, _), (.notDetermined, true): + var authorizationOptions: UNAuthorizationOptions = [.badge, .sound, .alert, .carPlay] + if #available(iOS 12.0, *) { + authorizationOptions.insert(.providesAppNotificationSettings) + } + if #available(iOS 13.0, *) { + authorizationOptions.insert(.announcement) + } + Logger.shared.log("App \(self.episodeId)", "register for notifications: request authorization") + notificationCenter.requestAuthorization(options: authorizationOptions, completionHandler: { result, _ in + Logger.shared.log("App \(self.episodeId)", "register for notifications: received authorization: \(result)") + completion(result) + if result { + Queue.mainQueue().async { + let reply = UNTextInputNotificationAction(identifier: "reply", title: replyString, options: [], textInputButtonTitle: replyString, textInputPlaceholder: messagePlaceholderString) + + let unknownMessageCategory: UNNotificationCategory + let repliableMessageCategory: UNNotificationCategory + let repliableMediaMessageCategory: UNNotificationCategory + let groupRepliableMessageCategory: UNNotificationCategory + let groupRepliableMediaMessageCategory: UNNotificationCategory + let channelMessageCategory: UNNotificationCategory + + if #available(iOS 11.0, *) { + var options: UNNotificationCategoryOptions = [] + if includeNames { + options.insert(.hiddenPreviewsShowTitle) } - UNUserNotificationCenter.current().setNotificationCategories([unknownMessageCategory, replyMessageCategory, replyLegacyMessageCategory, replyLegacyMediaMessageCategory, replyMediaMessageCategory, legacyChannelMessageCategory, muteMessageCategory, muteMediaMessageCategory]) + var carPlayOptions = options + carPlayOptions.insert(.allowInCarPlay) + if #available(iOS 13.2, *) { + carPlayOptions.insert(.allowAnnouncement) + } - Logger.shared.log("App \(self.episodeId)", "register for notifications: invoke registerForRemoteNotifications") - UIApplication.shared.registerForRemoteNotifications() + unknownMessageCategory = UNNotificationCategory(identifier: "unknown", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options) + repliableMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions) + repliableMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions) + groupRepliableMessageCategory = UNNotificationCategory(identifier: "gr", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options) + groupRepliableMediaMessageCategory = UNNotificationCategory(identifier: "gm", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options) + channelMessageCategory = UNNotificationCategory(identifier: "c", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options) + } else { + let carPlayOptions: UNNotificationCategoryOptions = [.allowInCarPlay] + + unknownMessageCategory = UNNotificationCategory(identifier: "unknown", actions: [], intentIdentifiers: [], options: []) + repliableMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], options: carPlayOptions) + repliableMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [], options: []) + groupRepliableMessageCategory = UNNotificationCategory(identifier: "gr", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], options: []) + groupRepliableMediaMessageCategory = UNNotificationCategory(identifier: "gm", actions: [reply], intentIdentifiers: [], options: []) + channelMessageCategory = UNNotificationCategory(identifier: "c", actions: [], intentIdentifiers: [], options: []) } + + UNUserNotificationCenter.current().setNotificationCategories([ + unknownMessageCategory, + repliableMessageCategory, + repliableMediaMessageCategory, + channelMessageCategory, + groupRepliableMessageCategory, + groupRepliableMediaMessageCategory + ]) + + Logger.shared.log("App \(self.episodeId)", "register for notifications: invoke registerForRemoteNotifications") + UIApplication.shared.registerForRemoteNotifications() } - }) - default: - break - } - }) - } else { - let reply = UIMutableUserNotificationAction() - reply.identifier = "reply" - reply.title = replyString - reply.isDestructive = false - reply.isAuthenticationRequired = false - reply.behavior = .textInput - reply.activationMode = .background - - let unknownMessageCategory = UIMutableUserNotificationCategory() - unknownMessageCategory.identifier = "unknown" - - let replyMessageCategory = UIMutableUserNotificationCategory() - replyMessageCategory.identifier = "withReply" - replyMessageCategory.setActions([reply], for: .default) - - let replyLegacyMessageCategory = UIMutableUserNotificationCategory() - replyLegacyMessageCategory.identifier = "r" - replyLegacyMessageCategory.setActions([reply], for: .default) - - let replyLegacyMediaMessageCategory = UIMutableUserNotificationCategory() - replyLegacyMediaMessageCategory.identifier = "m" - replyLegacyMediaMessageCategory.setActions([reply], for: .default) - - let replyMediaMessageCategory = UIMutableUserNotificationCategory() - replyMediaMessageCategory.identifier = "withReplyMedia" - replyMediaMessageCategory.setActions([reply], for: .default) - - let legacyChannelMessageCategory = UIMutableUserNotificationCategory() - legacyChannelMessageCategory.identifier = "c" - - let muteMessageCategory = UIMutableUserNotificationCategory() - muteMessageCategory.identifier = "withMute" - - let muteMediaMessageCategory = UIMutableUserNotificationCategory() - muteMediaMessageCategory.identifier = "withMuteMedia" - - let settings = UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: []) - UIApplication.shared.registerUserNotificationSettings(settings) - UIApplication.shared.registerForRemoteNotifications() - } + } + }) + default: + break + } + }) } @available(iOS 10.0, *) diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index 22e46ca77a..76e2e97d7b 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -101,8 +101,6 @@ final class AuthorizedApplicationContext { let rootController: TelegramRootController let notificationController: NotificationContainerController - private var scheduledOpenNotificationSettings: Bool = false - private var scheduledOpenChatWithPeerId: (PeerId, MessageId?, Bool)? private let scheduledCallPeerDisposable = MetaDisposable() private var scheduledOpenExternalUrl: URL? @@ -395,7 +393,7 @@ final class AuthorizedApplicationContext { } if inAppNotificationSettings.displayPreviews { - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } strongSelf.notificationController.enqueue(ChatMessageNotificationItem(context: strongSelf.context, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, messages: messages, threadData: threadData, tapAction: { if let strongSelf = self { var foundOverlay = false @@ -833,11 +831,7 @@ final class AuthorizedApplicationContext { } func openNotificationSettings() { - if self.rootController.rootTabController != nil { - self.rootController.pushViewController(notificationsAndSoundsController(context: self.context, exceptionsList: nil)) - } else { - self.scheduledOpenNotificationSettings = true - } + self.rootController.pushViewController(notificationsAndSoundsController(context: self.context, exceptionsList: nil)) } func startCall(peerId: PeerId, isVideo: Bool) { @@ -864,27 +858,40 @@ final class AuthorizedApplicationContext { } if visiblePeerId != peerId || messageId != nil { - if self.rootController.rootTabController != nil { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> deliverOnMainQueue).start(next: { peer in - guard let peer = peer else { - return - } - - let chatLocation: NavigateToChatControllerParams.Location - if let threadId = threadId { - chatLocation = .replyThread(ChatReplyThreadMessage( - messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false - )) + let isOutgoingMessage: Signal + if let messageId { + let accountPeerId = self.context.account.peerId + isOutgoingMessage = self.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) + |> map { message -> Bool in + if let message { + return !message._asMessage().effectivelyIncoming(accountPeerId) } else { - chatLocation = .peer(peer) + return false } - - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: chatLocation, subject: messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) }, activateInput: activateInput ? .text : nil)) - }) + } } else { - self.scheduledOpenChatWithPeerId = (peerId, messageId, activateInput) + isOutgoingMessage = .single(false) } + let _ = combineLatest( + queue: Queue.mainQueue(), + self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)), + isOutgoingMessage + ).start(next: { peer, isOutgoingMessage in + guard let peer = peer else { + return + } + + let chatLocation: NavigateToChatControllerParams.Location + if let threadId = threadId { + chatLocation = .replyThread(ChatReplyThreadMessage( + messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + )) + } else { + chatLocation = .peer(peer) + } + + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: chatLocation, subject: isOutgoingMessage ? messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) } : nil, activateInput: activateInput ? .text : nil)) + }) } } From c8e861a3d42d3a66d91d78c672b0ac434a9fc497 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Mar 2023 19:47:41 +0400 Subject: [PATCH 13/16] Fix topic icon color selection --- .../Sources/ChatListController.swift | 8 +-- .../Sources/ForumCreateTopicScreen.swift | 60 +++++++++++++++---- .../Sources/PeerInfo/PeerInfoScreen.swift | 2 +- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index aaa54fd9cc..5eacbd56e9 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1206,10 +1206,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create) controller.navigationPresentation = .modal - controller.completion = { [weak controller] title, fileId, _ in + controller.completion = { [weak controller] title, fileId, iconColor, _ in controller?.isInProgress = true - let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId) + let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId) |> deliverOnMainQueue).start(next: { topicId in let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text, keepStack: .never).start() }, error: { _ in @@ -2464,10 +2464,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create) controller.navigationPresentation = .modal - controller.completion = { [weak controller] title, fileId, _ in + controller.completion = { [weak controller] title, fileId, iconColor, _ in controller?.isInProgress = true - let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId) + let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId) |> deliverOnMainQueue).start(next: { topicId in if let navigationController = (sourceController.navigationController as? NavigationController) { let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text, keepStack: .never).start() diff --git a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift index 8d2c6c72b6..66de482c69 100644 --- a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift +++ b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift @@ -1,4 +1,5 @@ import Foundation +import Foundation import UIKit import Display import AsyncDisplayKit @@ -95,6 +96,7 @@ private final class TitleFieldComponent: Component { let iconColor: Int32 let text: String let placeholderText: String + let isEditing: Bool let textUpdated: (String) -> Void let iconPressed: () -> Void @@ -108,6 +110,7 @@ private final class TitleFieldComponent: Component { iconColor: Int32, text: String, placeholderText: String, + isEditing: Bool, textUpdated: @escaping (String) -> Void, iconPressed: @escaping () -> Void ) { @@ -120,6 +123,7 @@ private final class TitleFieldComponent: Component { self.iconColor = iconColor self.text = text self.placeholderText = placeholderText + self.isEditing = isEditing self.textUpdated = textUpdated self.iconPressed = iconPressed } @@ -152,6 +156,9 @@ private final class TitleFieldComponent: Component { if lhs.placeholderText != rhs.placeholderText { return false } + if lhs.isEditing != rhs.isEditing { + return false + } return true } @@ -237,6 +244,7 @@ private final class TitleFieldComponent: Component { iconContent = .animation(content: .customEmoji(fileId: component.fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: component.placeholderColor, themeColor: component.accentColor, loopMode: .count(2)) self.iconButton.isUserInteractionEnabled = false } + self.iconButton.isUserInteractionEnabled = !component.isEditing let placeholderSize = self.placeholderView.update( transition: .easeInOut(duration: 0.2), @@ -471,15 +479,26 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { let mode: ForumCreateTopicScreen.Mode let titleUpdated: (String) -> Void let iconUpdated: (Int64?) -> Void + let iconColorUpdated: (Int32) -> Void let isHiddenUpdated: (Bool) -> Void let openPremium: () -> Void - init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, isHiddenUpdated: @escaping (Bool) -> Void, openPremium: @escaping () -> Void) { + init( + context: AccountContext, + peerId: EnginePeer.Id, + mode: ForumCreateTopicScreen.Mode, + titleUpdated: @escaping (String) -> Void, + iconUpdated: @escaping (Int64?) -> Void, + iconColorUpdated: @escaping (Int32) -> Void, + isHiddenUpdated: @escaping (Bool) -> Void, + openPremium: @escaping () -> Void + ) { self.context = context self.peerId = peerId self.mode = mode self.titleUpdated = titleUpdated self.iconUpdated = iconUpdated + self.iconColorUpdated = iconColorUpdated self.isHiddenUpdated = isHiddenUpdated self.openPremium = openPremium } @@ -501,6 +520,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { private let context: AccountContext private let titleUpdated: (String) -> Void private let iconUpdated: (Int64?) -> Void + private let iconColorUpdated: (Int32) -> Void private let isHiddenUpdated: (Bool) -> Void private let openPremium: () -> Void @@ -520,10 +540,11 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { private var hasPremium: Bool = false - init(context: AccountContext, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, isHiddenUpdated: @escaping (Bool) -> Void, openPremium: @escaping () -> Void) { + init(context: AccountContext, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, iconColorUpdated: @escaping (Int32) -> Void, isHiddenUpdated: @escaping (Bool) -> Void, openPremium: @escaping () -> Void) { self.context = context self.titleUpdated = titleUpdated self.iconUpdated = iconUpdated + self.iconColorUpdated = iconColorUpdated self.isHiddenUpdated = isHiddenUpdated self.openPremium = openPremium @@ -534,6 +555,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { self.fileId = 0 self.iconColor = ForumCreateTopicScreen.iconColors.randomElement() ?? 0x0 self.isHidden = false + iconColorUpdated(self.iconColor) case let .edit(threadId, info, isHidden): self.isGeneral = threadId == 1 self.title = info.title @@ -647,6 +669,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { self.iconColor = colors.first ?? 0 } self.updated(transition: .immediate) + self.iconColorUpdated(self.iconColor) self.updateEmojiContent() } @@ -678,6 +701,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { mode: self.mode, titleUpdated: self.titleUpdated, iconUpdated: self.iconUpdated, + iconColorUpdated: self.iconColorUpdated, isHiddenUpdated: self.isHiddenUpdated, openPremium: self.openPremium ) @@ -753,6 +777,11 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + titleBackground.size.height / 2.0)) ) + var isEditing = false + if case .edit = context.component.mode { + isEditing = true + } + let titleField = titleField.update( component: TitleFieldComponent( context: context.component.context, @@ -764,6 +793,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { iconColor: state.iconColor, text: state.title, placeholderText: environment.strings.CreateTopic_EnterTopicTitlePlaceholder, + isEditing: isEditing, textUpdated: { [weak state] text in state?.updateTitle(text) }, @@ -999,8 +1029,8 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { private var doneBarItem: UIBarButtonItem? - private var state: (String, Int64?, Bool?) = ("", nil, nil) - public var completion: (String, Int64?, Bool?) -> Void = { _, _, _ in } + private var state: (title: String, icon: Int64?, iconColor: Int32, isHidden: Bool?) = ("", nil, 0, nil) + public var completion: (_ title: String, _ icon: Int64?, _ iconColor: Int32, _ isHidden: Bool?) -> Void = { _, _, _, _ in } public var isInProgress: Bool = false { didSet { @@ -1021,6 +1051,7 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { var titleUpdatedImpl: ((String) -> Void)? var iconUpdatedImpl: ((Int64?) -> Void)? + var iconColorUpdatedImpl: ((Int32) -> Void)? var isHiddenUpdatedImpl: ((Bool) -> Void)? var openPremiumImpl: (() -> Void)? @@ -1028,6 +1059,8 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { titleUpdatedImpl?(title) }, iconUpdated: { fileId in iconUpdatedImpl?(fileId) + }, iconColorUpdated: { iconColor in + iconColorUpdatedImpl?(iconColor) }, isHiddenUpdated: { isHidden in isHiddenUpdatedImpl?(isHidden) }, openPremium: { @@ -1045,7 +1078,7 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { title = presentationData.strings.CreateTopic_EditTitle doneTitle = presentationData.strings.Common_Done - self.state = (topic.title, topic.icon, threadId == 1 ? isHidden : nil) + self.state = (topic.title, topic.icon, topic.iconColor, threadId == 1 ? isHidden : nil) } self.title = title @@ -1066,23 +1099,28 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { } strongSelf.doneBarItem?.isEnabled = !title.isEmpty - strongSelf.state = (title, strongSelf.state.1, strongSelf.state.2) + strongSelf.state = (title, strongSelf.state.icon, strongSelf.state.iconColor, strongSelf.state.isHidden) } iconUpdatedImpl = { [weak self] fileId in guard let strongSelf = self else { return } - - strongSelf.state = (strongSelf.state.0, fileId, strongSelf.state.2) + strongSelf.state = (strongSelf.state.title, fileId, strongSelf.state.iconColor, strongSelf.state.isHidden) + } + + iconColorUpdatedImpl = { [weak self] iconColor in + guard let strongSelf = self else { + return + } + strongSelf.state = (strongSelf.state.title, strongSelf.state.icon, iconColor, strongSelf.state.isHidden) } isHiddenUpdatedImpl = { [weak self] isHidden in guard let strongSelf = self else { return } - - strongSelf.state = (strongSelf.state.0, strongSelf.state.1, isHidden) + strongSelf.state = (strongSelf.state.title, strongSelf.state.icon, strongSelf.state.iconColor, isHidden) } openPremiumImpl = { [weak self] in @@ -1113,6 +1151,6 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { } @objc private func createPressed() { - self.completion(self.state.0, self.state.1, self.state.2) + self.completion(self.state.title, self.state.icon, self.state.iconColor, self.state.isHidden) } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 7c3fa07e86..4a81a5e968 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3054,7 +3054,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate let controller = ForumCreateTopicScreen(context: strongSelf.context, peerId: strongSelf.peerId, mode: .edit(threadId: threadId, threadInfo: threadData.info, isHidden: threadData.isHidden)) controller.navigationPresentation = .modal let context = strongSelf.context - controller.completion = { [weak controller] title, fileId, isHidden in + controller.completion = { [weak controller] title, fileId, _, isHidden in let _ = (context.engine.peers.editForumChannelTopic(id: peerId, threadId: threadId, title: title, iconFileId: fileId) |> deliverOnMainQueue).start(completed: { controller?.dismiss() From b4c706b9a179c9951b72af612f3cfa6263455373 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Mar 2023 19:50:46 +0400 Subject: [PATCH 14/16] Remove some unnecessary main thread load when playing video messages --- .../ContainedViewLayoutTransition.swift | 8 ++- ...MessageInstantVideoBubbleContentNode.swift | 6 -- ...atMessageInteractiveInstantVideoNode.swift | 6 +- .../Sources/PeerMessagesMediaPlaylist.swift | 58 ++++++++++--------- 4 files changed, 40 insertions(+), 38 deletions(-) diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index ccd8868e4d..5686e645fb 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -1860,6 +1860,10 @@ final class ControlledTransitionProperty { let toValue: AnyValue private let completion: ((Bool) -> Void)? + private lazy var animationKey: String = { + return "MyCustomAnimation_\(Unmanaged.passUnretained(self).toOpaque())" + }() + init(layer: CALayer, path: String, fromValue: T, toValue: T, completion: ((Bool) -> Void)?) where T: AnyValueProviding { self.layer = layer self.path = path @@ -1871,7 +1875,7 @@ final class ControlledTransitionProperty { } deinit { - self.layer.removeAnimation(forKey: "MyCustomAnimation_\(Unmanaged.passUnretained(self).toOpaque())") + self.layer.removeAnimation(forKey: self.animationKey) } func update(at fraction: CGFloat) { @@ -1887,7 +1891,7 @@ final class ControlledTransitionProperty { animation.toValue = value.nsValue animation.timingFunction = CAMediaTimingFunction(name: .linear) animation.isRemovedOnCompletion = false - self.layer.add(animation, forKey: "MyCustomAnimation_\(Unmanaged.passUnretained(self).toOpaque())") + self.layer.add(animation, forKey: self.animationKey) } func complete(atEnd: Bool) { diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift index 3fdc0070e2..6e8a97de99 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift @@ -70,7 +70,6 @@ class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentNode { self.maskForeground.masksToBounds = true self.maskLayer.addSublayer(self.maskForeground) - self.addSubnode(self.interactiveFileNode) self.addSubnode(self.interactiveVideoNode) self.interactiveVideoNode.requestUpdateLayout = { [weak self] _ in @@ -285,14 +284,9 @@ class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentNode { return (finalSize, { [weak self] animation, synchronousLoads, applyInfo in if let strongSelf = self { - let firstTime = strongSelf.item == nil strongSelf.item = item strongSelf.isExpanded = isExpanded - if firstTime { - strongSelf.interactiveFileNode.isHidden = true - } - strongSelf.bubbleBackgroundNode?.layer.mask = strongSelf.maskLayer if let bubbleBackdropNode = strongSelf.bubbleBackdropNode, bubbleBackdropNode.hasImage && strongSelf.backdropMaskForeground.superlayer == nil { strongSelf.bubbleBackdropNode?.overrideMask = true diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index afb9e176de..2beb1ff859 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -1601,7 +1601,9 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { let duration: Double = 0.2 node.alpha = 1.0 - node.isHidden = false + if node.supernode == nil { + self.supernode?.insertSubnode(node, belowSubnode: self) + } self.alpha = 0.0 self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration) @@ -1715,7 +1717,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { node.alpha = 0.0 node.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, completion: { _ in - node.isHidden = true + node.removeFromSupernode() }) node.waveformView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration) diff --git a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift index 8cc15ce477..ae8af31130 100644 --- a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift @@ -60,7 +60,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { return MessageMediaPlaylistItemStableId(stableId: message.stableId) } - var playbackData: SharedMediaPlaybackData? { + lazy var playbackData: SharedMediaPlaybackData? = { if let file = extractFileMedia(self.message) { let fileReference = FileMediaReference.message(message: MessageReference(self.message), media: file) let source = SharedMediaPlaybackDataSource.telegramFile(reference: fileReference, isCopyProtected: self.message.isCopyProtected()) @@ -93,9 +93,9 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { } } return nil - } + }() - var displayData: SharedMediaPlaybackDisplayData? { + lazy var displayData: SharedMediaPlaybackDisplayData? = { if let file = extractFileMedia(self.message) { let text = self.message.text var entities: [MessageTextEntity] = [] @@ -108,40 +108,42 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { for attribute in file.attributes { switch attribute { - case let .Audio(isVoice, duration, title, performer, _): - if isVoice { - return SharedMediaPlaybackDisplayData.voice(author: self.message.effectiveAuthor, peer: self.message.peers[self.message.id.peerId]) - } else { - var updatedTitle = title - let updatedPerformer = performer - if (title ?? "").isEmpty && (performer ?? "").isEmpty { - updatedTitle = file.fileName ?? "" - } - - let albumArt: SharedMediaPlaybackAlbumArt? - if file.fileName?.lowercased().hasSuffix(".ogg") == true { - albumArt = nil - } else { - albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) - } - - return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0, caption: caption) + case let .Audio(isVoice, duration, title, performer, _): + let displayData: SharedMediaPlaybackDisplayData + if isVoice { + displayData = SharedMediaPlaybackDisplayData.voice(author: self.message.effectiveAuthor, peer: self.message.peers[self.message.id.peerId]) + } else { + var updatedTitle = title + let updatedPerformer = performer + if (title ?? "").isEmpty && (performer ?? "").isEmpty { + updatedTitle = file.fileName ?? "" } - case let .Video(_, _, flags): - if flags.contains(.instantRoundVideo) { - return SharedMediaPlaybackDisplayData.instantVideo(author: self.message.effectiveAuthor, peer: self.message.peers[self.message.id.peerId], timestamp: self.message.timestamp) + + let albumArt: SharedMediaPlaybackAlbumArt? + if file.fileName?.lowercased().hasSuffix(".ogg") == true { + albumArt = nil } else { - return nil + albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) } - default: - break + + displayData = SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0, caption: caption) + } + return displayData + case let .Video(_, _, flags): + if flags.contains(.instantRoundVideo) { + return SharedMediaPlaybackDisplayData.instantVideo(author: self.message.effectiveAuthor, peer: self.message.peers[self.message.id.peerId], timestamp: self.message.timestamp) + } else { + return nil + } + default: + break } } return SharedMediaPlaybackDisplayData.music(title: file.fileName ?? "", performer: self.message.effectiveAuthor?.debugDisplayTitle ?? "", albumArt: nil, long: false, caption: caption) } return nil - } + }() } private enum NavigatedMessageFromViewPosition { From 1e14a6c6874be14a2c73d6c3ddd61c78d9b1f16b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Mar 2023 20:03:17 +0400 Subject: [PATCH 15/16] Add text link editing Auto-paste url from pasteboard if any --- submodules/AttachmentUI/BUILD | 1 + .../Sources/AttachmentPanel.swift | 16 ++++++++-- .../Sources/ChatTextLinkEditController.swift | 17 +++++++++- .../TelegramUI/Sources/ChatController.swift | 32 +++++++++++++++---- .../Sources/PeerSelectionControllerNode.swift | 16 ++++++++-- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/submodules/AttachmentUI/BUILD b/submodules/AttachmentUI/BUILD index fc72bf71d8..e9c2f7b890 100644 --- a/submodules/AttachmentUI/BUILD +++ b/submodules/AttachmentUI/BUILD @@ -36,6 +36,7 @@ swift_library( "//submodules/Components/AnimatedStickerComponent:AnimatedStickerComponent", "//submodules/Components/MultilineTextComponent:MultilineTextComponent", "//submodules/ShimmerEffect:ShimmerEffect", + "//submodules/TextFormat:TextFormat", ], visibility = [ "//visibility:public", diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index bc1b81ada7..5e7cf29c0e 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -18,6 +18,7 @@ import SemanticStatusNode import MediaResources import MultilineTextComponent import ShimmerEffect +import TextFormat private let buttonSize = CGSize(width: 88.0, height: 49.0) private let smallButtonWidth: CGFloat = 69.0 @@ -885,20 +886,29 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { }, openLinkEditing: { [weak self] in if let strongSelf = self { var selectionRange: Range? - var text: String? + var text: NSAttributedString? var inputMode: ChatInputMode? strongSelf.updateChatPresentationInterfaceState(animated: true, { state in selectionRange = state.interfaceState.effectiveInputState.selectionRange if let selectionRange = selectionRange { - text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)).string + text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)) } inputMode = state.inputMode return state }) + + var link: String? + if let text { + text.enumerateAttributes(in: NSMakeRange(0, text.length)) { attributes, _, _ in + if let linkAttribute = attributes[ChatTextInputAttributes.textUrl] as? ChatTextInputTextUrlAttribute { + link = linkAttribute.url + } + } + } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text ?? "", link: nil, apply: { [weak self] link in + let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text?.string ?? "", link: link, apply: { [weak self] link in if let strongSelf = self, let inputMode = inputMode, let selectionRange = selectionRange { if let link = link { strongSelf.updateChatPresentationInterfaceState(animated: true, { state in diff --git a/submodules/ChatTextLinkEditUI/Sources/ChatTextLinkEditController.swift b/submodules/ChatTextLinkEditUI/Sources/ChatTextLinkEditController.swift index 0206d8f688..fd54f6dd23 100644 --- a/submodules/ChatTextLinkEditUI/Sources/ChatTextLinkEditController.swift +++ b/submodules/ChatTextLinkEditUI/Sources/ChatTextLinkEditController.swift @@ -12,7 +12,7 @@ import UrlEscaping private final class ChatTextLinkEditInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { private var theme: PresentationTheme private let backgroundNode: ASImageNode - private let textInputNode: EditableTextNode + fileprivate let textInputNode: EditableTextNode private let placeholderNode: ASTextNode var updateHeight: (() -> Void)? @@ -238,6 +238,21 @@ private final class ChatTextLinkEditAlertContentNode: AlertContentNode { } self.updateTheme(theme) + + if (link ?? "").isEmpty { + Queue.mainQueue().after(0.1, { + let pasteboard = UIPasteboard.general + if pasteboard.hasURLs { + if let url = pasteboard.url?.absoluteString, !url.isEmpty { + self.inputFieldNode.text = url + if let lastNode = self.actionNodes.last { + lastNode.actionEnabled = true + } + self.inputFieldNode.textInputNode.textView.selectAll(nil) + } + } + }) + } } deinit { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index c9cb7853bb..104af2439c 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -9777,18 +9777,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, openLinkEditing: { [weak self] in if let strongSelf = self { var selectionRange: Range? - var text: String? + var text: NSAttributedString? var inputMode: ChatInputMode? strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in selectionRange = state.interfaceState.effectiveInputState.selectionRange if let selectionRange = selectionRange { - text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)).string + text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)) } inputMode = state.inputMode return state }) - let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: strongSelf.updatedPresentationData, account: strongSelf.context.account, text: text ?? "", link: nil, apply: { [weak self] link in + var link: String? + if let text { + text.enumerateAttributes(in: NSMakeRange(0, text.length)) { attributes, _, _ in + if let linkAttribute = attributes[ChatTextInputAttributes.textUrl] as? ChatTextInputTextUrlAttribute { + link = linkAttribute.url + } + } + } + + let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: strongSelf.updatedPresentationData, account: strongSelf.context.account, text: text?.string ?? "", link: link, apply: { [weak self] link in if let strongSelf = self, let inputMode = inputMode, let selectionRange = selectionRange { if let link = link { strongSelf.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in @@ -12507,18 +12516,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, openLinkEditing: { [weak self] in if let strongSelf = self { var selectionRange: Range? - var text: String? + var text: NSAttributedString? var inputMode: ChatInputMode? updateChatPresentationInterfaceStateImpl?({ state in selectionRange = state.interfaceState.effectiveInputState.selectionRange if let selectionRange = selectionRange { - text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)).string + text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)) } inputMode = state.inputMode return state }) - let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text ?? "", link: nil, apply: { link in + var link: String? + if let text { + text.enumerateAttributes(in: NSMakeRange(0, text.length)) { attributes, _, _ in + if let linkAttribute = attributes[ChatTextInputAttributes.textUrl] as? ChatTextInputTextUrlAttribute { + link = linkAttribute.url + } + } + } + + let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text?.string ?? "", link: link, apply: { link in if let inputMode = inputMode, let selectionRange = selectionRange { if let link = link { updateChatPresentationInterfaceStateImpl?({ @@ -16013,7 +16031,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.dismiss() let navigateToLocation: NavigateToChatControllerParams.Location - if let message = messages.first, let threadId = message.threadId, threadId != 1 || (message.peers[message.id.peerId] as? TelegramChannel)?.flags.contains(.isForum) == true { + if let message = messages.first, let threadId = message.threadId, let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) { navigateToLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } else { navigateToLocation = .peer(peer) diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index cf986f9507..4f144e12ad 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -22,6 +22,7 @@ import AnimatedStickerNode import TelegramAnimatedStickerNode import SolidRoundedButtonNode import ContextUI +import TextFormat final class PeerSelectionControllerNode: ASDisplayNode { private let context: AccountContext @@ -611,19 +612,28 @@ final class PeerSelectionControllerNode: ASDisplayNode { }, openLinkEditing: { [weak self] in if let strongSelf = self { var selectionRange: Range? - var text: String? + var text: NSAttributedString? var inputMode: ChatInputMode? strongSelf.updateChatPresentationInterfaceState(animated: true, { state in selectionRange = state.interfaceState.effectiveInputState.selectionRange if let selectionRange = selectionRange { - text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)).string + text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)) } inputMode = state.inputMode return state }) - let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text ?? "", link: nil, apply: { [weak self] link in + var link: String? + if let text { + text.enumerateAttributes(in: NSMakeRange(0, text.length)) { attributes, _, _ in + if let linkAttribute = attributes[ChatTextInputAttributes.textUrl] as? ChatTextInputTextUrlAttribute { + link = linkAttribute.url + } + } + } + + let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text?.string ?? "", link: link, apply: { [weak self] link in if let strongSelf = self, let inputMode = inputMode, let selectionRange = selectionRange { if let link = link { strongSelf.updateChatPresentationInterfaceState(animated: true, { state in From cd9026f55b6e515bbb812cd632056008d1793bda Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Mar 2023 20:04:09 +0400 Subject: [PATCH 16/16] Various fixes --- .../BrowserUI/Sources/BrowserWebContent.swift | 5 ++- .../Sources/Node/ChatListItemStrings.swift | 8 ++--- ...SendMessageActionSheetControllerNode.swift | 33 +++---------------- 3 files changed, 9 insertions(+), 37 deletions(-) diff --git a/submodules/BrowserUI/Sources/BrowserWebContent.swift b/submodules/BrowserUI/Sources/BrowserWebContent.swift index 0d4d13012d..bd4f801433 100644 --- a/submodules/BrowserUI/Sources/BrowserWebContent.swift +++ b/submodules/BrowserUI/Sources/BrowserWebContent.swift @@ -26,9 +26,8 @@ final class BrowserWebContent: UIView, BrowserContent, UIScrollViewDelegate { let configuration = WKWebViewConfiguration() self.webView = WKWebView(frame: CGRect(), configuration: configuration) - if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - self.webView.allowsLinkPreview = false - } + self.webView.allowsLinkPreview = false + if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { self.webView.scrollView.contentInsetAdjustmentBehavior = .never } diff --git a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift index b4a7b82ea0..47e1f40e48 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift @@ -127,10 +127,8 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: case _ as TelegramMediaImage: if message.text.isEmpty { messageText = strings.Message_Photo - } else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - if enableMediaEmoji { - messageText = "🖼 \(messageText)" - } + } else if enableMediaEmoji { + messageText = "🖼 \(messageText)" } case let fileMedia as TelegramMediaFile: var processed = false @@ -188,7 +186,7 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: if message.text.isEmpty { messageText = strings.Message_Video processed = true - } else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { + } else { if enableMediaEmoji { if !fileMedia.isAnimated { messageText = "📹 \(messageText)" diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift index 7035561189..b1a74d9c1f 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift @@ -209,15 +209,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.cancel = cancel self.effectView = UIVisualEffectView() - if #available(iOS 9.0, *) { - } else { - if self.presentationData.theme.rootController.keyboardColor == .dark { - self.effectView.effect = UIBlurEffect(style: .dark) - } else { - self.effectView.effect = UIBlurEffect(style: .light) - } - self.effectView.alpha = 0.0 - } self.dimNode = ASDisplayNode() self.dimNode.alpha = 1.0 @@ -430,14 +421,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } self.presentationData = presentationData - if #available(iOS 9.0, *) { - } else { - if self.presentationData.theme.rootController.keyboardColor == .dark { - self.effectView.effect = UIBlurEffect(style: .dark) - } else { - self.effectView.effect = UIBlurEffect(style: .light) - } - } + self.effectView.effect = makeCustomZoomBlurEffect(isLight: self.presentationData.theme.rootController.keyboardColor == .light) self.dimNode.backgroundColor = presentationData.theme.contextMenu.dimColor @@ -465,11 +449,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.textInputNode.textView.setContentOffset(self.textInputNode.textView.contentOffset, animated: false) UIView.animate(withDuration: 0.2, animations: { - if #available(iOS 9.0, *) { - self.effectView.effect = makeCustomZoomBlurEffect(isLight: !self.presentationData.theme.overallDarkAppearance) - } else { - self.effectView.alpha = 1.0 - } + self.effectView.effect = makeCustomZoomBlurEffect(isLight: self.presentationData.theme.rootController.keyboardColor == .light) }, completion: { _ in }) self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) @@ -562,12 +542,8 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } } - UIView.animate(withDuration: 0.4, animations: { - if #available(iOS 9.0, *) { - self.effectView.effect = nil - } else { - self.effectView.alpha = 0.0 - } + UIView.animate(withDuration: 0.2, animations: { + self.effectView.effect = nil }, completion: { _ in completedEffect = true intermediateCompletion() @@ -596,7 +572,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } let duration = 0.4 - self.sendButtonNode.layer.animatePosition(from: self.sendButtonNode.position, to: self.sendButtonFrame.center, duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in completedButton = true intermediateCompletion()