@@ -3,20 +3,26 @@ import UIKit
33import Display
44import ComponentFlow
55import AppBundle
6- import MultilineTextComponent
6+ import BalancedTextComponent
77import AnimatedTextComponent
8+ import LottieComponent
89
910final class GalleryRateToastComponent : Component {
1011 let rate : Double
12+ let displayTooltip : String ?
1113
12- init ( rate: Double ) {
14+ init ( rate: Double , displayTooltip : String ? ) {
1315 self . rate = rate
16+ self . displayTooltip = displayTooltip
1417 }
1518
1619 static func == ( lhs: GalleryRateToastComponent , rhs: GalleryRateToastComponent ) -> Bool {
1720 if lhs. rate != rhs. rate {
1821 return false
1922 }
23+ if lhs. displayTooltip != rhs. displayTooltip {
24+ return false
25+ }
2026 return true
2127 }
2228
@@ -25,6 +31,14 @@ final class GalleryRateToastComponent: Component {
2531 private let text = ComponentView < Empty > ( )
2632 private let arrows = ComponentView < Empty > ( )
2733
34+ private var tooltipText : ComponentView < Empty > ?
35+ private var tooltipAnimation : ComponentView < Empty > ?
36+
37+ private var tooltipIsHidden : Bool = false
38+ private var tooltipTimer : Foundation . Timer ?
39+
40+ private weak var state : EmptyComponentState ?
41+
2842 override init ( frame: CGRect ) {
2943 super. init ( frame: frame)
3044 }
@@ -33,7 +47,13 @@ final class GalleryRateToastComponent: Component {
3347 fatalError ( " init(coder:) has not been implemented " )
3448 }
3549
50+ deinit {
51+ self . tooltipTimer? . invalidate ( )
52+ }
53+
3654 func update( component: GalleryRateToastComponent , availableSize: CGSize , state: EmptyComponentState , environment: Environment < Empty > , transition: ComponentTransition ) -> CGSize {
55+ self . state = state
56+
3757 let insets = UIEdgeInsets ( top: 5.0 , left: 11.0 , bottom: 5.0 , right: 16.0 )
3858 let spacing : CGFloat = 5.0
3959
@@ -55,17 +75,19 @@ final class GalleryRateToastComponent: Component {
5575 let textSize = self . text. update (
5676 transition: transition,
5777 component: AnyComponent ( AnimatedTextComponent (
58- font: Font . semibold ( 17.0 ) ,
78+ font: Font . with ( size : 17.0 , design : . round , weight : . semibold , traits : [ . monospacedNumbers ] ) ,
5979 color: . white,
6080 items: textItems
6181 ) ) ,
6282 environment: { } ,
6383 containerSize: CGSize ( width: 100.0 , height: 100.0 )
6484 )
6585
86+ var speedFraction = ( component. rate - 1.0 ) / ( 2.5 - 1.0 )
87+ speedFraction = max ( 0.0 , min ( 1.0 , speedFraction) )
6688 let arrowsSize = self . arrows. update (
6789 transition: transition,
68- component: AnyComponent ( GalleryRateToastAnimationComponent ( ) ) ,
90+ component: AnyComponent ( GalleryRateToastAnimationComponent ( speedFraction : speedFraction ) ) ,
6991 environment: { } ,
7092 containerSize: CGSize ( width: 200.0 , height: 100.0 )
7193 )
@@ -82,15 +104,15 @@ final class GalleryRateToastComponent: Component {
82104 environment: { } ,
83105 containerSize: size
84106 )
85- let backgroundFrame = CGRect ( origin: CGPoint ( ) , size: size)
107+ let backgroundFrame = CGRect ( origin: CGPoint ( x : floor ( ( availableSize . width - size . width ) * 0.5 ) , y : 0.0 ) , size: size)
86108 if let backgroundView = self . background. view {
87109 if backgroundView. superview == nil {
88110 self . addSubview ( backgroundView)
89111 }
90112 transition. setFrame ( view: backgroundView, frame: backgroundFrame)
91113 }
92114
93- let textFrame = CGRect ( origin: CGPoint ( x: insets. left, y: floorToScreenPixels ( ( size. height - textSize. height) * 0.5 ) ) , size: textSize)
115+ let textFrame = CGRect ( origin: CGPoint ( x: backgroundFrame . minX + insets. left, y: backgroundFrame . minY + floorToScreenPixels( ( size. height - textSize. height) * 0.5 ) ) , size: textSize)
94116 if let textView = self . text. view {
95117 if textView. superview == nil {
96118 textView. layer. anchorPoint = CGPoint ( )
@@ -100,15 +122,105 @@ final class GalleryRateToastComponent: Component {
100122 textView. bounds = CGRect ( origin: CGPoint ( ) , size: textFrame. size)
101123 }
102124
103- let arrowsFrame = CGRect ( origin: CGPoint ( x: textFrame. maxX + spacing, y: floorToScreenPixels ( ( size. height - arrowsSize. height) * 0.5 ) ) , size: arrowsSize)
125+ let arrowsFrame = CGRect ( origin: CGPoint ( x: textFrame. maxX + spacing, y: backgroundFrame . minY + floorToScreenPixels( ( size. height - arrowsSize. height) * 0.5 ) ) , size: arrowsSize)
104126 if let arrowsView = self . arrows. view {
105127 if arrowsView. superview == nil {
106128 self . addSubview ( arrowsView)
107129 }
108130 transition. setFrame ( view: arrowsView, frame: arrowsFrame)
109131 }
110132
111- return size
133+ if let displayTooltip = component. displayTooltip {
134+ var tooltipTransition = transition
135+
136+ let tooltipText : ComponentView < Empty >
137+ if let current = self . tooltipText {
138+ tooltipText = current
139+ } else {
140+ tooltipText = ComponentView ( )
141+ self . tooltipText = tooltipText
142+ tooltipTransition = tooltipTransition. withAnimation ( . none)
143+ }
144+
145+ let tooltipAnimation : ComponentView < Empty >
146+ if let current = self . tooltipAnimation {
147+ tooltipAnimation = current
148+ } else {
149+ tooltipAnimation = ComponentView ( )
150+ self . tooltipAnimation = tooltipAnimation
151+ }
152+
153+ let tooltipTextSize = tooltipText. update (
154+ transition: . immediate,
155+ component: AnyComponent ( BalancedTextComponent (
156+ text: . plain( NSAttributedString ( string: displayTooltip, font: Font . regular ( 15.0 ) , textColor: UIColor ( white: 1.0 , alpha: 0.8 ) ) ) ,
157+ horizontalAlignment: . center,
158+ maximumNumberOfLines: 0
159+ ) ) ,
160+ environment: { } ,
161+ containerSize: CGSize ( width: availableSize. width - 8.0 * 2.0 , height: 1000.0 )
162+ )
163+ let tooltipTextFrame = CGRect ( origin: CGPoint ( x: floor ( ( availableSize. width - tooltipTextSize. width) * 0.5 ) , y: backgroundFrame. maxY + 10.0 ) , size: tooltipTextSize)
164+ if let tooltipTextView = tooltipText. view {
165+ if tooltipTextView. superview == nil {
166+ self . addSubview ( tooltipTextView)
167+ }
168+ tooltipTransition. setPosition ( view: tooltipTextView, position: tooltipTextFrame. center)
169+ tooltipTextView. bounds = CGRect ( origin: CGPoint ( ) , size: tooltipTextFrame. size)
170+
171+ transition. setAlpha ( view: tooltipTextView, alpha: self . tooltipIsHidden ? 0.0 : 1.0 )
172+ }
173+
174+ let tooltipAnimationSize = tooltipAnimation. update (
175+ transition: . immediate,
176+ component: AnyComponent ( LottieComponent (
177+ content: LottieComponent . AppBundleContent ( name: " video_toast_speedup " ) ,
178+ color: . white,
179+ startingPosition: . begin,
180+ loop: true
181+ ) ) ,
182+ environment: { } ,
183+ containerSize: CGSize ( width: 60.0 , height: 60.0 )
184+ )
185+ let tooltipAnimationFrame = CGRect ( origin: CGPoint ( x: floor ( ( availableSize. width - tooltipAnimationSize. width) * 0.5 ) , y: tooltipTextFrame. maxY + 10.0 ) , size: tooltipAnimationSize)
186+ if let tooltipAnimationView = tooltipAnimation. view {
187+ if tooltipAnimationView. superview == nil {
188+ self . addSubview ( tooltipAnimationView)
189+ }
190+ tooltipTransition. setFrame ( view: tooltipAnimationView, frame: tooltipAnimationFrame)
191+
192+ transition. setAlpha ( view: tooltipAnimationView, alpha: self . tooltipIsHidden ? 0.0 : 0.8 )
193+ }
194+ } else {
195+ if let tooltipText = self . tooltipText {
196+ self . tooltipText = nil
197+ if let tooltipTextView = tooltipText. view {
198+ transition. setAlpha ( view: tooltipTextView, alpha: 0.0 , completion: { [ weak tooltipTextView] _ in
199+ tooltipTextView? . removeFromSuperview ( )
200+ } )
201+ }
202+ }
203+ if let tooltipAnimation = self . tooltipAnimation {
204+ self . tooltipAnimation = nil
205+ if let tooltipAnimationView = tooltipAnimation. view {
206+ transition. setAlpha ( view: tooltipAnimationView, alpha: 0.0 , completion: { [ weak tooltipAnimationView] _ in
207+ tooltipAnimationView? . removeFromSuperview ( )
208+ } )
209+ }
210+ }
211+ }
212+
213+ if self . tooltipTimer == nil {
214+ self . tooltipTimer = Foundation . Timer. scheduledTimer ( withTimeInterval: 2.0 , repeats: false , block: { [ weak self] _ in
215+ guard let self else {
216+ return
217+ }
218+ self . tooltipIsHidden = true
219+ self . state? . updated ( transition: . easeInOut( duration: 0.25 ) , isLocal: true )
220+ } )
221+ }
222+
223+ return CGSize ( width: availableSize. width, height: size. height)
112224 }
113225 }
114226
0 commit comments