@@ -14,6 +14,7 @@ import TextFormat
1414import TelegramPresentationData
1515import ReactionSelectionNode
1616import BundleIconComponent
17+ import LottieComponent
1718import Markdown
1819
1920private let glassColor = UIColor ( rgb: 0x25272e , alpha: 0.72 )
@@ -22,6 +23,7 @@ final class MessageItemComponent: Component {
2223 public enum Icon : Equatable {
2324 case peer( EnginePeer )
2425 case icon( String )
26+ case animation( String )
2527 }
2628
2729 private let context : AccountContext
@@ -30,7 +32,7 @@ final class MessageItemComponent: Component {
3032 private let text : String
3133 private let entities : [ MessageTextEntity ]
3234 private let availableReactions : [ ReactionItem ] ?
33- private let avatarTapped : ( ) -> Void
35+ private let openPeer : ( ( EnginePeer ) -> Void ) ?
3436
3537 init (
3638 context: AccountContext ,
@@ -39,15 +41,15 @@ final class MessageItemComponent: Component {
3941 text: String ,
4042 entities: [ MessageTextEntity ] ,
4143 availableReactions: [ ReactionItem ] ? ,
42- avatarTapped : @escaping ( ) -> Void = { }
44+ openPeer : ( ( EnginePeer ) -> Void ) ?
4345 ) {
4446 self . context = context
4547 self . icon = icon
4648 self . isNotification = isNotification
4749 self . text = text
4850 self . entities = entities
4951 self . availableReactions = availableReactions
50- self . avatarTapped = avatarTapped
52+ self . openPeer = openPeer
5153 }
5254
5355 static func == ( lhs: MessageItemComponent , rhs: MessageItemComponent ) -> Bool {
@@ -77,6 +79,9 @@ final class MessageItemComponent: Component {
7779 private let text : ComponentView < Empty >
7880 weak var standaloneReactionAnimation : StandaloneReactionAnimation ?
7981
82+ private var cachedEntities : [ MessageTextEntity ] ?
83+ private var entityFiles : [ MediaId : TelegramMediaFile ] = [ : ]
84+
8085 private var component : MessageItemComponent ?
8186
8287 override init ( frame: CGRect ) {
@@ -95,12 +100,21 @@ final class MessageItemComponent: Component {
95100 self . addSubview ( self . container)
96101 self . container. addSubview ( self . background)
97102 self . container. addSubview ( self . avatarNode. view)
103+
104+ self . avatarNode. view. addGestureRecognizer ( UITapGestureRecognizer ( target: self , action: #selector( self . avatarTapped) ) )
98105 }
99106
100107 required init ? ( coder: NSCoder ) {
101108 fatalError ( " init(coder:) has not been implemented " )
102109 }
103110
111+ @objc private func avatarTapped( ) {
112+ guard let component = self . component, case let . peer( peer) = component. icon else {
113+ return
114+ }
115+ component. openPeer ? ( peer)
116+ }
117+
104118 func animateFrom( globalFrame: CGRect , cornerRadius: CGFloat , textSnapshotView: UIView , transition: ComponentTransition ) {
105119 guard let superview = self . superview? . superview? . superview else {
106120 return
@@ -143,8 +157,17 @@ final class MessageItemComponent: Component {
143157 transition. animateScale ( view: self . avatarNode. view, from: 0.01 , to: 1.0 )
144158 }
145159
146- private var cachedEntities : [ MessageTextEntity ] ?
147- private var entityFiles : [ MediaId : TelegramMediaFile ] = [ : ]
160+ override func point( inside point: CGPoint , with event: UIEvent ? ) -> Bool {
161+ if !self . avatarNode. isHidden, self . avatarNode. frame. contains ( point) {
162+ return true
163+ }
164+ if let textView = self . text. view as? MultilineTextWithEntitiesComponent . View , let ( _, attributes) = textView. attributes ( at: self . convert ( point, to: textView) ) {
165+ if let _ = attributes [ NSAttributedString . Key ( rawValue: TelegramTextAttributes . Spoiler) ] , textView. isSpoilerConcealed {
166+ return true
167+ }
168+ }
169+ return false
170+ }
148171
149172 func update( component: MessageItemComponent , availableSize: CGSize , state: EmptyComponentState , environment: Environment < Empty > , transition: ComponentTransition ) -> CGSize {
150173 let isFirstTime = self . component == nil
@@ -158,6 +181,9 @@ final class MessageItemComponent: Component {
158181
159182 let textFont = Font . regular ( 14.0 )
160183 let boldTextFont = Font . semibold ( 14.0 )
184+ let italicFont = Font . italic ( 14.0 )
185+ let boldItalicTextFont = Font . semiboldItalic ( 14.0 )
186+ let monospaceFont = Font . monospace ( 14.0 )
161187 let textColor : UIColor = . white
162188 let linkColor : UIColor = UIColor ( rgb: 0x59b6fa )
163189
@@ -167,38 +193,9 @@ final class MessageItemComponent: Component {
167193 let avatarSize = CGSize ( width: component. isNotification ? 30.0 : 28.0 , height: component. isNotification ? 30.0 : 28.0 )
168194 let avatarSpacing : CGFloat = 10.0
169195 let iconSpacing : CGFloat = 10.0
170- let rightInset : CGFloat = 13.0
196+ let rightInset : CGFloat = component . isNotification ? 15.0 : 13.0
171197
172198 let avatarFrame = CGRect ( origin: CGPoint ( x: avatarInset, y: avatarInset) , size: avatarSize)
173- if case let . peer( peer) = component. icon {
174- if peer. smallProfileImage != nil {
175- self . avatarNode. setPeerV2 (
176- context: component. context,
177- theme: theme,
178- peer: peer,
179- authorOfMessage: nil ,
180- overrideImage: nil ,
181- emptyColor: nil ,
182- clipStyle: . round,
183- synchronousLoad: true ,
184- displayDimensions: avatarSize
185- )
186- } else {
187- self . avatarNode. setPeer (
188- context: component. context,
189- theme: theme,
190- peer: peer,
191- clipStyle: . round,
192- synchronousLoad: true ,
193- displayDimensions: avatarSize
194- )
195- }
196- }
197- if self . avatarNode. bounds. isEmpty {
198- self . avatarNode. frame = avatarFrame
199- } else {
200- transition. setFrame ( view: self . avatarNode. view, frame: avatarFrame)
201- }
202199
203200 var peerName = " "
204201 if !component. isNotification, case let . peer( peer) = component. icon {
@@ -240,7 +237,7 @@ final class MessageItemComponent: Component {
240237 )
241238 )
242239 } else {
243- let textWithAppliedEntities = stringWithAppliedEntities ( text, entities: entities, baseColor: textColor, linkColor: linkColor, baseFont: textFont, linkFont: textFont, boldFont: boldTextFont, italicFont: textFont , boldItalicFont: boldTextFont , fixedFont: textFont , blockQuoteFont: textFont, message: nil , entityFiles: self . entityFiles) . mutableCopy ( ) as! NSMutableAttributedString
240+ let textWithAppliedEntities = stringWithAppliedEntities ( text, entities: entities, baseColor: textColor, linkColor: linkColor, baseFont: textFont, linkFont: textFont, boldFont: boldTextFont, italicFont: italicFont , boldItalicFont: boldItalicTextFont , fixedFont: monospaceFont , blockQuoteFont: textFont, message: nil , entityFiles: self . entityFiles) . mutableCopy ( ) as! NSMutableAttributedString
244241 if !peerName. isEmpty {
245242 textWithAppliedEntities. insert ( NSAttributedString ( string: peerName + " " , font: boldTextFont, textColor: textColor) , at: 0 )
246243 }
@@ -253,6 +250,8 @@ final class MessageItemComponent: Component {
253250 spacing = avatarSpacing
254251 case . icon:
255252 spacing = iconSpacing
253+ case . animation:
254+ spacing = iconSpacing
256255 }
257256
258257 let textSize = self . text. update (
@@ -265,15 +264,46 @@ final class MessageItemComponent: Component {
265264 text: . plain( attributedText) ,
266265 maximumNumberOfLines: 0 ,
267266 lineSpacing: 0.0 ,
268- spoilerColor: . white
267+ spoilerColor: . white,
268+ handleSpoilers: true
269269 ) ) ,
270270 environment: { } ,
271271 containerSize: CGSize ( width: availableSize. width - avatarInset - avatarSize. width - spacing - rightInset, height: . greatestFiniteMagnitude)
272272 )
273273
274274 let size = CGSize ( width: avatarInset + avatarSize. width + spacing + textSize. width + rightInset, height: max ( minimalHeight, textSize. height + 15.0 ) )
275275
276- if case let . icon( iconName) = component. icon {
276+ switch component. icon {
277+ case let . peer( peer) :
278+ if peer. smallProfileImage != nil {
279+ self . avatarNode. setPeerV2 (
280+ context: component. context,
281+ theme: theme,
282+ peer: peer,
283+ authorOfMessage: nil ,
284+ overrideImage: nil ,
285+ emptyColor: nil ,
286+ clipStyle: . round,
287+ synchronousLoad: true ,
288+ displayDimensions: avatarSize
289+ )
290+ } else {
291+ self . avatarNode. setPeer (
292+ context: component. context,
293+ theme: theme,
294+ peer: peer,
295+ clipStyle: . round,
296+ synchronousLoad: true ,
297+ displayDimensions: avatarSize
298+ )
299+ }
300+ if self . avatarNode. bounds. isEmpty {
301+ self . avatarNode. frame = avatarFrame
302+ } else {
303+ transition. setFrame ( view: self . avatarNode. view, frame: avatarFrame)
304+ }
305+ self . avatarNode. isHidden = false
306+ case let . icon( iconName) :
277307 let iconSize = self . icon. update (
278308 transition: transition,
279309 component: AnyComponent ( BundleIconComponent ( name: iconName, tintColor: . white) ) ,
@@ -287,6 +317,31 @@ final class MessageItemComponent: Component {
287317 }
288318 transition. setFrame ( view: iconView, frame: iconFrame)
289319 }
320+ self . avatarNode. isHidden = true
321+ case let . animation( animationName) :
322+ let iconSize = self . icon. update (
323+ transition: transition,
324+ component: AnyComponent ( LottieComponent (
325+ content: LottieComponent . AppBundleContent (
326+ name: animationName
327+ ) ,
328+ placeholderColor: nil ,
329+ startingPosition: . end,
330+ size: CGSize ( width: 40.0 , height: 40.0 ) ,
331+ loop: false
332+ ) ) ,
333+ environment: { } ,
334+ containerSize: CGSize ( width: 40.0 , height: 40.0 )
335+ )
336+ let iconFrame = CGRect ( origin: CGPoint ( x: avatarInset - 3.0 , y: floorToScreenPixels ( ( size. height - iconSize. height) / 2.0 ) ) , size: iconSize)
337+ if let iconView = self . icon. view as? LottieComponent . View {
338+ if iconView. superview == nil {
339+ self . container. addSubview ( iconView)
340+ iconView. playOnce ( )
341+ }
342+ transition. setFrame ( view: iconView, frame: iconFrame)
343+ }
344+ self . avatarNode. isHidden = true
290345 }
291346
292347 let textFrame = CGRect ( origin: CGPoint ( x: avatarInset + avatarSize. width + spacing, y: floorToScreenPixels ( ( size. height - textSize. height) / 2.0 ) ) , size: textSize)
@@ -362,8 +417,6 @@ final class MessageItemComponent: Component {
362417 }
363418 }
364419 }
365-
366-
367420 return size
368421 }
369422 }
0 commit comments