diff --git a/submodules/GalleryUI/BUILD b/submodules/GalleryUI/BUILD index 9398674cc6..1ebf1cad4c 100644 --- a/submodules/GalleryUI/BUILD +++ b/submodules/GalleryUI/BUILD @@ -58,7 +58,9 @@ swift_library( "//submodules/TelegramUI/Components/RasterizedCompositionComponent", "//submodules/TelegramUI/Components/BadgeComponent", "//submodules/TelegramUI/Components/AnimatedTextComponent", + "//submodules/TelegramUI/Components/LottieComponent", "//submodules/Components/MultilineTextComponent", + "//submodules/Components/BalancedTextComponent", "//submodules/Components/ComponentDisplayAdapters", "//submodules/ComponentFlow", ], diff --git a/submodules/GalleryUI/Sources/GalleryRateToastAnimationComponent.swift b/submodules/GalleryUI/Sources/GalleryRateToastAnimationComponent.swift index 6b543c5cc4..e596760fc2 100644 --- a/submodules/GalleryUI/Sources/GalleryRateToastAnimationComponent.swift +++ b/submodules/GalleryUI/Sources/GalleryRateToastAnimationComponent.swift @@ -5,10 +5,16 @@ import ComponentFlow import AppBundle final class GalleryRateToastAnimationComponent: Component { - init() { + let speedFraction: CGFloat + + init(speedFraction: CGFloat) { + self.speedFraction = speedFraction } static func ==(lhs: GalleryRateToastAnimationComponent, rhs: GalleryRateToastAnimationComponent) -> Bool { + if lhs.speedFraction != rhs.speedFraction { + return false + } return true } @@ -16,6 +22,10 @@ final class GalleryRateToastAnimationComponent: Component { private let itemViewContainer: UIView private var itemViews: [UIImageView] = [] + private var link: SharedDisplayLinkDriver.Link? + private var timeValue: CGFloat = 0.0 + private var speedFraction: CGFloat = 1.0 + override init(frame: CGRect) { self.itemViewContainer = UIView() @@ -36,41 +46,49 @@ final class GalleryRateToastAnimationComponent: Component { fatalError("init(coder:) has not been implemented") } + deinit { + self.link?.invalidate() + } + private func setupAnimations() { - let beginTime = self.layer.convertTime(CACurrentMediaTime(), from: nil) - - for i in 0 ..< self.itemViews.count { - if self.itemViews[i].layer.animation(forKey: "idle-opacity") != nil { - continue + if self.link == nil { + var previousTimestamp = CACurrentMediaTime() + self.link = SharedDisplayLinkDriver.shared.add { [weak self] _ in + guard let self else { + return + } + + let timestamp = CACurrentMediaTime() + let deltaMultiplier = 1.0 * (1.0 - self.speedFraction) + 3.0 * self.speedFraction + let deltaTime = (timestamp - previousTimestamp) * deltaMultiplier + previousTimestamp = timestamp + + self.timeValue += deltaTime + + let duration: CGFloat = 1.2 + + for i in 0 ..< self.itemViews.count { + var itemFraction = (self.timeValue + CGFloat(i) * 0.1).truncatingRemainder(dividingBy: duration) / duration + + if itemFraction >= 0.5 { + itemFraction = (1.0 - itemFraction) / 0.5 + } else { + itemFraction = itemFraction / 0.5 + } + + let itemAlpha = 0.6 * (1.0 - itemFraction) + 1.0 * itemFraction + let itemScale = 0.9 * (1.0 - itemFraction) + 1.1 * itemFraction + + self.itemViews[i].alpha = itemAlpha + self.itemViews[i].transform = CGAffineTransformMakeScale(itemScale, itemScale) + } } - - let delay = Double(i) * 0.1 - - let animation = CABasicAnimation(keyPath: "opacity") - animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) - animation.beginTime = beginTime + delay - animation.fromValue = 0.6 as NSNumber - animation.toValue = 1.0 as NSNumber - animation.repeatCount = Float.infinity - animation.autoreverses = true - animation.fillMode = .both - animation.duration = 0.4 - self.itemViews[i].layer.add(animation, forKey: "idle-opacity") - - let scaleAnimation = CABasicAnimation(keyPath: "transform.scale") - scaleAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) - scaleAnimation.beginTime = beginTime + delay - scaleAnimation.fromValue = 0.9 as NSNumber - scaleAnimation.toValue = 1.1 as NSNumber - scaleAnimation.repeatCount = Float.infinity - scaleAnimation.autoreverses = true - scaleAnimation.fillMode = .both - scaleAnimation.duration = 0.4 - self.itemViews[i].layer.add(scaleAnimation, forKey: "idle-scale") } } func update(component: GalleryRateToastAnimationComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.speedFraction = component.speedFraction + let itemSize = self.itemViews[0].image?.size ?? CGSize(width: 10.0, height: 10.0) let itemSpacing: CGFloat = 1.0 @@ -78,7 +96,10 @@ final class GalleryRateToastAnimationComponent: Component { for i in 0 ..< self.itemViews.count { let itemFrame = CGRect(origin: CGPoint(x: CGFloat(i) * (itemSize.width + itemSpacing), y: UIScreenPixel), size: itemSize) - self.itemViews[i].frame = itemFrame + self.itemViews[i].center = itemFrame.center + self.itemViews[i].bounds = CGRect(origin: CGPoint(), size: itemFrame.size) + + self.itemViews[i].layer.speed = Float(1.0 * (1.0 - component.speedFraction) + 2.0 * component.speedFraction) } self.setupAnimations() diff --git a/submodules/GalleryUI/Sources/GalleryRateToastComponent.swift b/submodules/GalleryUI/Sources/GalleryRateToastComponent.swift index d0a2951526..caadc1e407 100644 --- a/submodules/GalleryUI/Sources/GalleryRateToastComponent.swift +++ b/submodules/GalleryUI/Sources/GalleryRateToastComponent.swift @@ -3,20 +3,26 @@ import UIKit import Display import ComponentFlow import AppBundle -import MultilineTextComponent +import BalancedTextComponent import AnimatedTextComponent +import LottieComponent final class GalleryRateToastComponent: Component { let rate: Double + let displayTooltip: String? - init(rate: Double) { + init(rate: Double, displayTooltip: String?) { self.rate = rate + self.displayTooltip = displayTooltip } static func ==(lhs: GalleryRateToastComponent, rhs: GalleryRateToastComponent) -> Bool { if lhs.rate != rhs.rate { return false } + if lhs.displayTooltip != rhs.displayTooltip { + return false + } return true } @@ -25,6 +31,14 @@ final class GalleryRateToastComponent: Component { private let text = ComponentView() private let arrows = ComponentView() + private var tooltipText: ComponentView? + private var tooltipAnimation: ComponentView? + + private var tooltipIsHidden: Bool = false + private var tooltipTimer: Foundation.Timer? + + private weak var state: EmptyComponentState? + override init(frame: CGRect) { super.init(frame: frame) } @@ -33,7 +47,13 @@ final class GalleryRateToastComponent: Component { fatalError("init(coder:) has not been implemented") } + deinit { + self.tooltipTimer?.invalidate() + } + func update(component: GalleryRateToastComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.state = state + let insets = UIEdgeInsets(top: 5.0, left: 11.0, bottom: 5.0, right: 16.0) let spacing: CGFloat = 5.0 @@ -55,7 +75,7 @@ final class GalleryRateToastComponent: Component { let textSize = self.text.update( transition: transition, component: AnyComponent(AnimatedTextComponent( - font: Font.semibold(17.0), + font: Font.with(size: 17.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), color: .white, items: textItems )), @@ -63,9 +83,11 @@ final class GalleryRateToastComponent: Component { containerSize: CGSize(width: 100.0, height: 100.0) ) + var speedFraction = (component.rate - 1.0) / (2.5 - 1.0) + speedFraction = max(0.0, min(1.0, speedFraction)) let arrowsSize = self.arrows.update( transition: transition, - component: AnyComponent(GalleryRateToastAnimationComponent()), + component: AnyComponent(GalleryRateToastAnimationComponent(speedFraction: speedFraction)), environment: {}, containerSize: CGSize(width: 200.0, height: 100.0) ) @@ -82,7 +104,7 @@ final class GalleryRateToastComponent: Component { environment: {}, containerSize: size ) - let backgroundFrame = CGRect(origin: CGPoint(), size: size) + let backgroundFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - size.width) * 0.5), y: 0.0), size: size) if let backgroundView = self.background.view { if backgroundView.superview == nil { self.addSubview(backgroundView) @@ -90,7 +112,7 @@ final class GalleryRateToastComponent: Component { transition.setFrame(view: backgroundView, frame: backgroundFrame) } - let textFrame = CGRect(origin: CGPoint(x: insets.left, y: floorToScreenPixels((size.height - textSize.height) * 0.5)), size: textSize) + let textFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + insets.left, y: backgroundFrame.minY + floorToScreenPixels((size.height - textSize.height) * 0.5)), size: textSize) if let textView = self.text.view { if textView.superview == nil { textView.layer.anchorPoint = CGPoint() @@ -100,7 +122,7 @@ final class GalleryRateToastComponent: Component { textView.bounds = CGRect(origin: CGPoint(), size: textFrame.size) } - let arrowsFrame = CGRect(origin: CGPoint(x: textFrame.maxX + spacing, y: floorToScreenPixels((size.height - arrowsSize.height) * 0.5)), size: arrowsSize) + let arrowsFrame = CGRect(origin: CGPoint(x: textFrame.maxX + spacing, y: backgroundFrame.minY + floorToScreenPixels((size.height - arrowsSize.height) * 0.5)), size: arrowsSize) if let arrowsView = self.arrows.view { if arrowsView.superview == nil { self.addSubview(arrowsView) @@ -108,7 +130,97 @@ final class GalleryRateToastComponent: Component { transition.setFrame(view: arrowsView, frame: arrowsFrame) } - return size + if let displayTooltip = component.displayTooltip { + var tooltipTransition = transition + + let tooltipText: ComponentView + if let current = self.tooltipText { + tooltipText = current + } else { + tooltipText = ComponentView() + self.tooltipText = tooltipText + tooltipTransition = tooltipTransition.withAnimation(.none) + } + + let tooltipAnimation: ComponentView + if let current = self.tooltipAnimation { + tooltipAnimation = current + } else { + tooltipAnimation = ComponentView() + self.tooltipAnimation = tooltipAnimation + } + + let tooltipTextSize = tooltipText.update( + transition: .immediate, + component: AnyComponent(BalancedTextComponent( + text: .plain(NSAttributedString(string: displayTooltip, font: Font.regular(15.0), textColor: UIColor(white: 1.0, alpha: 0.8))), + horizontalAlignment: .center, + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - 8.0 * 2.0, height: 1000.0) + ) + let tooltipTextFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - tooltipTextSize.width) * 0.5), y: backgroundFrame.maxY + 10.0), size: tooltipTextSize) + if let tooltipTextView = tooltipText.view { + if tooltipTextView.superview == nil { + self.addSubview(tooltipTextView) + } + tooltipTransition.setPosition(view: tooltipTextView, position: tooltipTextFrame.center) + tooltipTextView.bounds = CGRect(origin: CGPoint(), size: tooltipTextFrame.size) + + transition.setAlpha(view: tooltipTextView, alpha: self.tooltipIsHidden ? 0.0 : 1.0) + } + + let tooltipAnimationSize = tooltipAnimation.update( + transition: .immediate, + component: AnyComponent(LottieComponent( + content: LottieComponent.AppBundleContent(name: "video_toast_speedup"), + color: .white, + startingPosition: .begin, + loop: true + )), + environment: {}, + containerSize: CGSize(width: 60.0, height: 60.0) + ) + let tooltipAnimationFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - tooltipAnimationSize.width) * 0.5), y: tooltipTextFrame.maxY + 10.0), size: tooltipAnimationSize) + if let tooltipAnimationView = tooltipAnimation.view { + if tooltipAnimationView.superview == nil { + self.addSubview(tooltipAnimationView) + } + tooltipTransition.setFrame(view: tooltipAnimationView, frame: tooltipAnimationFrame) + + transition.setAlpha(view: tooltipAnimationView, alpha: self.tooltipIsHidden ? 0.0 : 0.8) + } + } else { + if let tooltipText = self.tooltipText { + self.tooltipText = nil + if let tooltipTextView = tooltipText.view { + transition.setAlpha(view: tooltipTextView, alpha: 0.0, completion: { [weak tooltipTextView] _ in + tooltipTextView?.removeFromSuperview() + }) + } + } + if let tooltipAnimation = self.tooltipAnimation { + self.tooltipAnimation = nil + if let tooltipAnimationView = tooltipAnimation.view { + transition.setAlpha(view: tooltipAnimationView, alpha: 0.0, completion: { [weak tooltipAnimationView] _ in + tooltipAnimationView?.removeFromSuperview() + }) + } + } + } + + if self.tooltipTimer == nil { + self.tooltipTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false, block: { [weak self] _ in + guard let self else { + return + } + self.tooltipIsHidden = true + self.state?.updated(transition: .easeInOut(duration: 0.25), isLocal: true) + }) + } + + return CGSize(width: availableSize.width, height: size.height) } } diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 7333f1b8f6..25d86232d9 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -1619,13 +1619,15 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { activeEdgeRateIndicatorTransition = .immediate } + //TODO:localize let activeEdgeRateIndicatorSize = activeEdgeRateIndicator.update( transition: ComponentTransition(activeEdgeRateIndicatorTransition), component: AnyComponent(GalleryRateToastComponent( - rate: activeEdgeRateState.currentRate + rate: activeEdgeRateState.currentRate, + displayTooltip: "Swipe sideways to adjust speed." )), environment: {}, - containerSize: CGSize(width: 200.0, height: 100.0) + containerSize: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: 100.0) ) let activeEdgeRateIndicatorFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - activeEdgeRateIndicatorSize.width) * 0.5), y: max(navigationBarHeight, layout.statusBarHeight ?? 0.0) + 8.0), size: activeEdgeRateIndicatorSize) if let activeEdgeRateIndicatorView = activeEdgeRateIndicator.view { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index 7d076a23c5..f5510b8649 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -362,7 +362,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { selectedStarsForeground: themeColors.reactionStarsActiveForeground.argb, extractedBackground: arguments.presentationData.theme.theme.contextMenu.backgroundColor.argb, extractedForeground: arguments.presentationData.theme.theme.contextMenu.primaryColor.argb, - extractedSelectedForeground: arguments.presentationData.theme.theme.overallDarkAppearance ? themeColors.reactionActiveForeground.argb : arguments.presentationData.theme.theme.list.itemCheckColors.foregroundColor.argb, + extractedSelectedForeground: arguments.presentationData.theme.theme.overallDarkAppearance ? themeColors.reactionActiveForeground.argb : arguments.presentationData.theme.theme.contextMenu.primaryColor.argb, deselectedMediaPlaceholder: themeColors.reactionInactiveMediaPlaceholder.argb, selectedMediaPlaceholder: themeColors.reactionActiveMediaPlaceholder.argb ) diff --git a/submodules/TelegramUI/Resources/Animations/video_toast_speedup.json b/submodules/TelegramUI/Resources/Animations/video_toast_speedup.json new file mode 100644 index 0000000000..a6a099f10f --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/video_toast_speedup.json @@ -0,0 +1 @@ +{"v":"5.12.1","fr":60,"ip":0,"op":60,"w":512,"h":512,"nm":"SpeedUp","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"L","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-149.676,-134.674,0],"ix":2,"l":2},"a":{"a":0,"k":[-149.676,-134.674,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-118.501,-90.586],[-180.85,-131.391],[-120.298,-178.762]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"R 2","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":25,"s":[0]},{"t":37,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[148.867,-138.259,0],"ix":2,"l":2},"a":{"a":0,"k":[86.867,-138.259,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[55.693,-182.348],[118.041,-141.542],[57.489,-94.171]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":238,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"R","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":15.525,"s":[315.867,117.741,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.24,"y":1},"o":{"x":0.333,"y":0},"t":39,"s":[386.867,113.741,0],"to":[0,0,0],"ti":[0,0,0]},{"t":58,"s":[361.867,113.741,0]}],"ix":2,"l":2},"a":{"a":0,"k":[86.867,-138.259,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[55.693,-182.348],[118.041,-141.542],[57.489,-94.171]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":238,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":15,"s":[-8]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":40.369,"s":[2]},{"t":59,"s":[0]}],"ix":10},"p":{"a":0,"k":[-18.5,36.537,0],"ix":2,"l":2},"a":{"a":0,"k":[-18.5,36.537,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.184,-29.147],[0,0],[0,0]],"o":[[0,0],[0,0],[0.179,-28.358],[0,0],[0,0],[0,0]],"v":[[-42.122,74.94],[-42.656,-41.628],[-42.954,-106.447],[2.666,-106.159],[2.179,-28.956],[1.757,30.035]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Hand","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":15,"s":[-27]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":39,"s":[-4]},{"t":58,"s":[-8]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.788,"y":1},"o":{"x":0.675,"y":0.1},"t":15,"s":[324.228,369.807,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":35.711,"s":[332.157,343.048,0],"to":[0,0,0],"ti":[0,0,0]},{"t":54.341796875,"s":[331.157,340.048,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28,102.537,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[0,0],[0,0],[0.191,-30.324],[3.985,-27.467],[66.688,0.421],[12.251,16.781],[45.025,26.208],[-30.441,-3.454],[-8.116,-7.778]],"o":[[0,0],[0.191,-30.324],[0,0],[-3.778,26.039],[-53.508,-0.338],[-21.42,-29.674],[-23.406,-13.363],[30.592,3.823],[0,0]],"v":[[90.706,47.786],[90.902,16.644],[132.787,16.908],[132.141,119.309],[36.922,202.939],[-54.145,150.224],[-124.225,81.972],[-99.575,44.059],[-41.999,74.829]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":29.5,"s":[{"i":[[0,0],[0,0],[0.191,-30.324],[3.985,-27.467],[66.688,0.421],[15.266,14.266],[43.037,9.57],[-40.297,5.526],[-13.417,-4.962]],"o":[[0,0],[0.191,-30.324],[0,0],[-3.778,26.039],[-53.508,-0.338],[-21.287,-19.893],[-27.722,-6.164],[30.686,-4.208],[0,0]],"v":[[90.706,47.786],[90.902,16.644],[132.787,16.908],[132.141,119.309],[36.922,202.939],[-54.531,154.659],[-124.202,111.503],[-111.986,60.033],[-41.999,74.829]],"c":false}]},{"t":48.130859375,"s":[{"i":[[0,0],[0,0],[0.191,-30.324],[3.985,-27.467],[66.688,0.421],[11.838,17.125],[45.297,28.484],[-29.093,-4.682],[-7.391,-8.163]],"o":[[0,0],[0.191,-30.324],[0,0],[-3.778,26.039],[-53.508,-0.338],[-21.438,-31.012],[-22.816,-14.347],[30.58,4.921],[0,0]],"v":[[90.706,47.786],[90.902,16.644],[132.787,16.908],[132.141,119.309],[36.922,202.939],[-54.092,149.617],[-124.228,77.933],[-97.877,41.873],[-41.999,74.829]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.19,-30.193],[0,0]],"o":[[0,0],[0.19,-30.193],[0,0],[0,0]],"v":[[44.474,37.139],[44.686,3.459],[90.983,3.751],[90.706,47.786]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.288,-28.763],[0,0]],"o":[[0,0],[0.181,-28.763],[0,0],[0,0]],"v":[[1.812,29.305],[2.067,-11.134],[44.776,-10.865],[44.474,37.139]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"ct":1,"bm":0}],"markers":[{"tm":0,"cm":"1","dr":0},{"tm":38,"cm":"2","dr":0}],"props":{}} \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 25ad446c6b..76e276e40e 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -4613,7 +4613,7 @@ extension ChatControllerImpl { title: "Improving video...", text: "The video will be published after it's optimized for the bese viewing experience.", customUndoText: nil, - timeout: 3.5 + timeout: 5.0 ), elevatedLayout: false, position: .top, diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 3154a570f5..0c5e9c5a36 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -10358,7 +10358,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let location = CGPoint(x: bounds.midX, y: bounds.minY - 8.0) //TODO:localize - let tooltipController = TooltipController(content: .text("Processing video may take a few minutes."), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize, balancedTextLayout: true, isBlurred: true, timeout: 3.5, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true) + let tooltipController = TooltipController(content: .text("Processing video may take a few minutes."), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize, balancedTextLayout: true, isBlurred: true, timeout: 4.5, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true) self.checksTooltipController = tooltipController tooltipController.dismissed = { [weak self, weak tooltipController] _ in if let strongSelf = self, let tooltipController = tooltipController, strongSelf.checksTooltipController === tooltipController { diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 7b756fe096..566e3f5b24 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -223,23 +223,6 @@ func chatHistoryEntriesForView( } if groupMessages || reverseGroupedMessages { - /*if !groupBucket.isEmpty && message.groupInfo != groupBucket[0].0.groupInfo { - if reverseGroupedMessages { - groupBucket.reverse() - } - if groupMessages { - let groupStableId = groupBucket[0].0.groupInfo!.stableId - if !existingGroupStableIds.contains(groupStableId) { - existingGroupStableIds.append(groupStableId) - entries.append(.MessageGroupEntry(groupBucket[0].0.groupInfo!, groupBucket, presentationData)) - } - } else { - for (message, isRead, selection, attributes, location) in groupBucket { - entries.append(.MessageEntry(message, presentationData, isRead, location, selection, attributes)) - } - } - groupBucket.removeAll() - }*/ if let messageGroupingKey = message.groupingKey, (groupMessages || reverseGroupedMessages) { let selection: ChatHistoryMessageSelection if let selectedMessages = selectedMessages { @@ -286,14 +269,6 @@ func chatHistoryEntriesForView( if !found { entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, attributes)) } - - /*let selection: ChatHistoryMessageSelection - if let selectedMessages = selectedMessages { - selection = .selectable(selected: selectedMessages.contains(message.id)) - } else { - selection = .none - } - groupBucket.append((message, isRead, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: false, isCentered: false, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }), entry.location))*/ } else { let selection: ChatHistoryMessageSelection if let selectedMessages = selectedMessages { @@ -316,27 +291,64 @@ func chatHistoryEntriesForView( } else { selection = .none } + entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId, isCentered: false, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }))) } } - - /*if !groupBucket.isEmpty { - assert(groupMessages || reverseGroupedMessages) - if reverseGroupedMessages { - groupBucket.reverse() - } - if groupMessages { - let groupStableId = groupBucket[0].0.groupInfo!.stableId - if !existingGroupStableIds.contains(groupStableId) { - existingGroupStableIds.append(groupStableId) - entries.append(.MessageGroupEntry(groupBucket[0].0.groupInfo!, groupBucket, presentationData)) + + let insertPendingProcessingMessage: ([Message], Int) -> Void = { messages, index in + //TODO:localize + let serviceMessage = Message( + stableId: UInt32.max - messages[0].stableId, + stableVersion: 0, + id: MessageId(peerId: messages[0].id.peerId, namespace: -1, id: messages[0].id.id), + globallyUniqueId: nil, + groupingKey: nil, + groupInfo: nil, + threadId: nil, + timestamp: messages[0].timestamp, + flags: [.Incoming], + tags: [], + globalTags: [], + localTags: [], + customTags: [], + forwardInfo: nil, + author: nil, + text: "", + attributes: [], + media: [TelegramMediaAction(action: .customText(text: "This video will be published once converted and optimized", entities: [], additionalAttributes: nil))], + peers: SimpleDictionary(), + associatedMessages: SimpleDictionary(), + associatedMessageIds: [], + associatedMedia: [:], + associatedThreadInfo: nil, + associatedStories: [:] + ) + entries.insert(.MessageEntry(serviceMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil)), at: index) + } + + for i in (0 ..< entries.count).reversed() { + switch entries[i] { + case let .MessageEntry(message, _, _, _, _, _): + if message.id.namespace == Namespaces.Message.ScheduledCloud && message.pendingProcessingAttribute != nil { + insertPendingProcessingMessage([message], i) } - } else { - for (message, isRead, selection, attributes, location) in groupBucket { - entries.append(.MessageEntry(message, presentationData, isRead, location, selection, attributes)) + case let .MessageGroupEntry(_, messages, _): + if !messages.isEmpty && messages[0].0.id.namespace == Namespaces.Message.ScheduledCloud { + var videoCount = 0 + for message in messages { + if message.0.pendingProcessingAttribute != nil { + videoCount += 1 + } + } + if videoCount != 0 { + insertPendingProcessingMessage(messages.map(\.0), i) + } } + default: + break } - }*/ + } if let lowerTimestamp = view.entries.last?.message.timestamp, let upperTimestamp = view.entries.first?.message.timestamp { if let joinMessage { @@ -657,6 +669,9 @@ func chatHistoryEntriesForView( if reverse { return (entries.reversed(), currentState) } else { + #if DEBUG + assert(entries.map(\.stableId) == entries.sorted().map(\.stableId)) + #endif return (entries, currentState) } } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 49206a7995..343a4e02a8 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -476,6 +476,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState guard let interfaceInteraction = interfaceInteraction, let controllerInteraction = controllerInteraction else { return .single(ContextController.Items(content: .list([]))) } + if let message = messages.first, message.id.namespace < 0 { + return .single(ContextController.Items(content: .list([]))) + } var isEmbeddedMode = false if case .standard(.embedded) = chatPresentationInterfaceState.mode {