diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index e58dd0a6ea..6c72742b2c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -1022,6 +1022,16 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI return rects } + public func animateInstantVideoFromSnapshot(snapshotView: UIView, transition: CombinedTransition) { + for contentNode in self.contentNodes { + if let contentNode = contentNode as? ChatMessageInstantVideoBubbleContentNode { + snapshotView.frame = contentNode.interactiveVideoNode.view.convert(snapshotView.frame, from: self.view) + contentNode.interactiveVideoNode.animateFromSnapshot(snapshotView: snapshotView, transition: transition) + return + } + } + } + override public func didLoad() { super.didLoad() diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index 76821ce801..5566b8bb8c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -1778,9 +1778,16 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if !self.animatedFadeIn { self.animatedFadeIn = true self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.18) + if let durationNode = self.durationNode { durationNode.layer.animateAlpha(from: 0.0, to: durationNode.alpha, duration: 0.15, delay: 0.18) } + if let durationBackgroundNode = self.durationBackgroundNode { + durationBackgroundNode.layer.animateAlpha(from: 0.0, to: durationBackgroundNode.alpha, duration: 0.15, delay: 0.18) + } + if let audioTranscriptionButton = self.audioTranscriptionButton { + audioTranscriptionButton.layer.animateAlpha(from: 0.0, to: audioTranscriptionButton.alpha, duration: 0.15, delay: 0.18) + } } } diff --git a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/LoadingEffectView.swift b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/LoadingEffectView.swift new file mode 100644 index 0000000000..09d0508eb6 --- /dev/null +++ b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/LoadingEffectView.swift @@ -0,0 +1,115 @@ +import Foundation +import UIKit +import HierarchyTrackingLayer +import ComponentFlow +import Display + +private let shadowImage: UIImage? = { + UIImage(named: "Stories/PanelGradient") +}() + +final class LoadingEffectView: UIView { + private let duration: Double + + private let hierarchyTrackingLayer: HierarchyTrackingLayer + + private let gradientWidth: CGFloat + private let backgroundView: UIImageView + + private let borderGradientView: UIImageView + private let borderContainerView: UIView + let borderMaskLayer: SimpleShapeLayer + + init(effectAlpha: CGFloat, borderAlpha: CGFloat, gradientWidth: CGFloat = 200.0, duration: Double) { + self.hierarchyTrackingLayer = HierarchyTrackingLayer() + + self.duration = duration + + self.gradientWidth = gradientWidth + self.backgroundView = UIImageView() + + self.borderGradientView = UIImageView() + self.borderContainerView = UIView() + self.borderMaskLayer = SimpleShapeLayer() + + super.init(frame: .zero) + + self.layer.addSublayer(self.hierarchyTrackingLayer) + self.hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in + guard let self, self.bounds.width != 0.0 else { + return + } + self.updateAnimations(size: self.bounds.size) + } + + let generateGradient: (CGFloat) -> UIImage? = { baseAlpha in + return generateImage(CGSize(width: self.gradientWidth, height: 16.0), opaque: false, scale: 1.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + let foregroundColor = UIColor(white: 1.0, alpha: min(1.0, baseAlpha * 4.0)) + + if let shadowImage { + UIGraphicsPushContext(context) + + for i in 0 ..< 2 { + let shadowFrame = CGRect(origin: CGPoint(x: CGFloat(i) * (size.width * 0.5), y: 0.0), size: CGSize(width: size.width * 0.5, height: size.height)) + + context.saveGState() + context.translateBy(x: shadowFrame.midX, y: shadowFrame.midY) + context.rotate(by: CGFloat(i == 0 ? 1.0 : -1.0) * CGFloat.pi * 0.5) + let adjustedRect = CGRect(origin: CGPoint(x: -shadowFrame.height * 0.5, y: -shadowFrame.width * 0.5), size: CGSize(width: shadowFrame.height, height: shadowFrame.width)) + + context.clip(to: adjustedRect, mask: shadowImage.cgImage!) + context.setFillColor(foregroundColor.cgColor) + context.fill(adjustedRect) + + context.restoreGState() + } + + UIGraphicsPopContext() + } + }) + } + self.backgroundView.image = generateGradient(effectAlpha) + self.addSubview(self.backgroundView) + + self.borderGradientView.image = generateGradient(borderAlpha) + self.borderContainerView.addSubview(self.borderGradientView) + self.addSubview(self.borderContainerView) + self.borderContainerView.layer.mask = self.borderMaskLayer + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func updateAnimations(size: CGSize) { + if self.backgroundView.layer.animation(forKey: "shimmer") != nil { + return + } + + let animation = self.backgroundView.layer.makeAnimation(from: 0.0 as NSNumber, to: (size.width + self.gradientWidth + size.width * 0.2) as NSNumber, keyPath: "position.x", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: self.duration, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: true) + animation.repeatCount = Float.infinity + self.backgroundView.layer.add(animation, forKey: "shimmer") + self.borderGradientView.layer.add(animation, forKey: "shimmer") + } + + func update(size: CGSize, transition: Transition) { + if self.backgroundView.bounds.size != size { + self.backgroundView.layer.removeAllAnimations() + + self.borderMaskLayer.fillColor = nil + self.borderMaskLayer.strokeColor = UIColor.white.cgColor + let lineWidth: CGFloat = 3.0 + self.borderMaskLayer.lineWidth = lineWidth + self.borderMaskLayer.path = UIBezierPath(ovalIn: CGRect(origin: .zero, size: size)).cgPath + + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: size.height))) + + transition.setFrame(view: self.borderContainerView, frame: CGRect(origin: CGPoint(), size: size)) + transition.setFrame(view: self.borderGradientView, frame: CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: size.height))) + } + + self.updateAnimations(size: size) + } +} diff --git a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift index 825a83a6b3..dfe1b91825 100644 --- a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift +++ b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift @@ -76,8 +76,10 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { let context: AccountContext let cameraState: CameraState + let previewFrame: CGRect let isPreviewing: Bool let isMuted: Bool + let totalDuration: Double let getController: () -> VideoMessageCameraScreen? let present: (ViewController) -> Void let push: (ViewController) -> Void @@ -88,8 +90,10 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { init( context: AccountContext, cameraState: CameraState, + previewFrame: CGRect, isPreviewing: Bool, isMuted: Bool, + totalDuration: Double, getController: @escaping () -> VideoMessageCameraScreen?, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, @@ -99,8 +103,10 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { ) { self.context = context self.cameraState = cameraState + self.previewFrame = previewFrame self.isPreviewing = isPreviewing self.isMuted = isMuted + self.totalDuration = totalDuration self.getController = getController self.present = present self.push = push @@ -113,6 +119,9 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { if lhs.context !== rhs.context { return false } + if lhs.previewFrame != rhs.previewFrame { + return false + } if lhs.cameraState != rhs.cameraState { return false } @@ -122,6 +131,9 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { if lhs.isMuted != rhs.isMuted { return false } + if lhs.totalDuration != rhs.totalDuration { + return false + } return true } @@ -244,12 +256,12 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { let duration = initialDuration + recordingData.duration if let self, let controller = self.getController() { controller.updateCameraState({ $0.updatedDuration(duration) }, transition: .easeInOut(duration: 0.1)) - if duration > 59.0 { - self.stopVideoRecording() - } if isFirstRecording { controller.node.setupLiveUpload(filePath: recordingData.filePath) } + if duration > 59.5 { + controller.onStop() + } } })) } @@ -304,6 +316,8 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { let viewOnceButton = Child(PlainButtonComponent.self) let recordMoreButton = Child(PlainButtonComponent.self) + + let muteIcon = Child(ZStack.self) return { context in let environment = context.environment[ViewControllerComponentContainer.Environment.self].value @@ -319,15 +333,23 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { var showRecordMore = false if component.isPreviewing { showViewOnce = true - showRecordMore = true - - viewOnceOffset = 67.0 + if component.totalDuration < 59.0 { + showRecordMore = true + viewOnceOffset = 67.0 + } else { + viewOnceOffset = 14.0 + } } else if case .handsFree = component.cameraState.recording { showViewOnce = true } - if let controller = component.getController(), !controller.viewOnceAvailable { - showViewOnce = false + if let controller = component.getController() { + if controller.isSendingImmediately || controller.scheduledLock { + showViewOnce = true + } + if !controller.viewOnceAvailable { + showViewOnce = false + } } if !component.isPreviewing { @@ -444,14 +466,35 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { ) } -// var isVideoRecording = false -// if case .video = component.cameraState.mode { -// isVideoRecording = true -// } else if component.cameraState.recording != .none { -// isVideoRecording = true -// } + if component.isPreviewing && component.isMuted { + let muteIcon = muteIcon.update( + component: ZStack([ + AnyComponentWithIdentity( + id: "background", + component: AnyComponent( + RoundedRectangle(color: UIColor(rgb: 0x000000, alpha: 0.3), cornerRadius: 24.0) + ) + ), + AnyComponentWithIdentity( + id: "icon", + component: AnyComponent( + BundleIconComponent( + name: "Chat/Message/InstantVideoMute", + tintColor: .white + ) + ) + ) + ]), + availableSize: CGSize(width: 24.0, height: 24.0), + transition: context.transition + ) + context.add(muteIcon + .position(CGPoint(x: component.previewFrame.midX, y: component.previewFrame.maxY - 24.0)) + .appear(.default(scale: true, alpha: true)) + .disappear(.default(scale: true, alpha: true)) + ) + } - return availableSize } } @@ -483,13 +526,16 @@ public class VideoMessageCameraScreen: ViewController { fileprivate let containerView: UIView fileprivate let componentHost: ComponentView fileprivate let previewContainerView: UIView + private var previewSnapshotView: UIView? + private var previewBlurView: BlurView fileprivate var mainPreviewView: CameraSimplePreviewView fileprivate var additionalPreviewView: CameraSimplePreviewView private var progressView: RecordingProgressView + private let loadingView: LoadingEffectView private var resultPreviewView: ResultPreviewView? - + private var cameraStateDisposable: Disposable? private let idleTimerExtensionDisposable = MetaDisposable() @@ -560,6 +606,11 @@ public class VideoMessageCameraScreen: ViewController { self.progressView = RecordingProgressView(frame: .zero) + self.loadingView = LoadingEffectView(effectAlpha: 0.1, borderAlpha: 0.25, duration: 1.0) + + self.previewBlurView = BlurView() + self.previewBlurView.isUserInteractionEnabled = false + if isDualCameraEnabled { self.mainPreviewView.resetPlaceholder(front: false) self.additionalPreviewView.resetPlaceholder(front: true) @@ -589,17 +640,27 @@ public class VideoMessageCameraScreen: ViewController { self.previewContainerView.addSubview(self.mainPreviewView) self.previewContainerView.addSubview(self.additionalPreviewView) self.previewContainerView.addSubview(self.progressView) - + self.previewContainerView.addSubview(self.previewBlurView) + self.previewContainerView.addSubview(self.loadingView) + self.completion.connect { [weak self] result in if let self { self.addCaptureResult(result) } } - - self.mainPreviewView.removePlaceholder(delay: 0.0) + if isDualCameraEnabled { + self.mainPreviewView.removePlaceholder(delay: 0.0) + } self.withReadyCamera(isFirstTime: true, { - self.additionalPreviewView.removePlaceholder(delay: 0.35) - self.startRecording.invoke(Void()) + if isDualCameraEnabled { + self.mainPreviewView.removePlaceholder(delay: 0.0) + } + self.loadingView.alpha = 0.0 + self.additionalPreviewView.removePlaceholder(delay: 0.0) + + Queue.mainQueue().after(0.15) { + self.startRecording.invoke(Void()) + } }) self.idleTimerExtensionDisposable.set(self.context.sharedContext.applicationBindings.pushIdleTimerExtension()) @@ -744,10 +805,43 @@ public class VideoMessageCameraScreen: ViewController { func resumeCameraCapture() { if !self.mainPreviewView.isEnabled { + if let snapshotView = self.previewContainerView.snapshotView(afterScreenUpdates: false) { + self.previewContainerView.insertSubview(snapshotView, belowSubview: self.previewBlurView) + self.previewSnapshotView = snapshotView + } self.mainPreviewView.isEnabled = true self.additionalPreviewView.isEnabled = true self.camera?.startCapture() + UIView.animate(withDuration: 0.25, animations: { + self.loadingView.alpha = 1.0 + self.previewBlurView.effect = UIBlurEffect(style: .dark) + }) + + let action = { [weak self] in + guard let self else { + return + } + UIView.animate(withDuration: 0.4, animations: { + self.previewBlurView.effect = nil + self.previewSnapshotView?.alpha = 0.0 + }, completion: { _ in + self.previewSnapshotView?.removeFromSuperview() + self.previewSnapshotView = nil + }) + } + if #available(iOS 13.0, *) { + let _ = (self.mainPreviewView.isPreviewing + |> filter { $0 } + |> take(1)).startStandalone(next: { _ in + action() + }) + } else { + Queue.mainQueue().after(1.0) { + action() + } + } + self.cameraIsActive = true self.requestUpdateLayout(transition: .immediate) } @@ -776,10 +870,12 @@ public class VideoMessageCameraScreen: ViewController { self.transitioningToPreview = false - let composition = composition(with: self.results) - controller.updatePreviewState({ _ in - return PreviewState(composition: composition, trimRange: nil, isMuted: true) - }, transition: .spring(duration: 0.4)) + if !controller.isSendingImmediately { + let composition = composition(with: self.results) + controller.updatePreviewState({ _ in + return PreviewState(composition: composition, trimRange: nil, isMuted: true) + }, transition: .spring(duration: 0.4)) + } } private func debugSaveResult(path: String) { @@ -797,8 +893,16 @@ public class VideoMessageCameraScreen: ViewController { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let result = super.hitTest(point, with: event) + + if let resultPreviewView = self.resultPreviewView { + if resultPreviewView.bounds.contains(self.view.convert(point, to: resultPreviewView)) { + return resultPreviewView + } + } + if let controller = self.controller, let layout = self.validLayout { - if point.y > layout.size.height - controller.inputPanelFrame.height - 34.0 { + let insets = layout.insets(options: .input) + if point.y > layout.size.height - insets.bottom - controller.inputPanelFrame.height { if layout.metrics.isTablet { if point.x < layout.size.width * 0.33 { return result @@ -807,6 +911,7 @@ public class VideoMessageCameraScreen: ViewController { return nil } } + return result } @@ -916,8 +1021,6 @@ public class VideoMessageCameraScreen: ViewController { let isFirstTime = self.validLayout == nil self.validLayout = layout -// let isTablet = layout.metrics.isTablet - let environment = ViewControllerComponentContainer.Environment( statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: 0.0, @@ -944,7 +1047,45 @@ public class VideoMessageCameraScreen: ViewController { self.didAppear() } - let backgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: controller.inputPanelFrame.minY)) + var backgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: controller.inputPanelFrame.minY)) + if backgroundFrame.maxY < layout.size.height - 100.0 && (layout.inputHeight ?? 0.0).isZero { + backgroundFrame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: layout.size.height - layout.intrinsicInsets.bottom - controller.inputPanelFrame.height)) + } + + transition.setPosition(view: self.backgroundView, position: backgroundFrame.center) + transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: .zero, size: backgroundFrame.size)) + + transition.setPosition(view: self.containerView, position: backgroundFrame.center) + transition.setBounds(view: self.containerView, bounds: CGRect(origin: .zero, size: backgroundFrame.size)) + + let availableHeight = layout.size.height - (layout.inputHeight ?? 0.0) + let previewSide = min(369.0, layout.size.width - 24.0) + let previewFrame: CGRect + if layout.metrics.isTablet { + previewFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - previewSide) / 2.0), y: max(layout.statusBarHeight ?? 0.0 + 24.0, availableHeight * 0.2 - previewSide / 2.0)), size: CGSize(width: previewSide, height: previewSide)) + } else { + previewFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - previewSide) / 2.0), y: max(layout.statusBarHeight ?? 0.0 + 16.0, availableHeight * 0.4 - previewSide / 2.0)), size: CGSize(width: previewSide, height: previewSide)) + } + if !self.animatingIn { + transition.setFrame(view: self.previewContainerView, frame: previewFrame) + } + transition.setCornerRadius(layer: self.previewContainerView.layer, cornerRadius: previewSide / 2.0) + + let previewInnerFrame = CGRect(origin: .zero, size: previewFrame.size) + + let additionalPreviewSize = CGSize(width: previewFrame.size.width, height: previewFrame.size.width / 3.0 * 4.0) + let additionalPreviewInnerFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((previewFrame.height - additionalPreviewSize.height) / 2.0)), size: additionalPreviewSize) + self.mainPreviewView.frame = previewInnerFrame + self.additionalPreviewView.frame = additionalPreviewInnerFrame + + self.progressView.frame = previewInnerFrame + self.progressView.value = CGFloat(self.cameraState.duration / 60.0) + + transition.setAlpha(view: self.additionalPreviewView, alpha: self.cameraState.position == .front ? 1.0 : 0.0) + + self.previewBlurView.frame = previewInnerFrame + self.previewSnapshotView?.frame = previewInnerFrame + self.loadingView.update(size: previewInnerFrame.size, transition: .immediate) let componentSize = self.componentHost.update( transition: transition, @@ -952,8 +1093,10 @@ public class VideoMessageCameraScreen: ViewController { VideoMessageCameraScreenComponent( context: self.context, cameraState: self.cameraState, + previewFrame: previewFrame, isPreviewing: self.previewState != nil || self.transitioningToPreview, isMuted: self.previewState?.isMuted ?? true, + totalDuration: self.previewState?.composition.duration.seconds ?? 0.0, getController: { [weak self] in return self?.controller }, @@ -984,37 +1127,6 @@ public class VideoMessageCameraScreen: ViewController { transition.setFrame(view: componentView, frame: componentFrame) } - transition.setPosition(view: self.backgroundView, position: backgroundFrame.center) - transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: .zero, size: backgroundFrame.size)) - - transition.setPosition(view: self.containerView, position: backgroundFrame.center) - transition.setBounds(view: self.containerView, bounds: CGRect(origin: .zero, size: backgroundFrame.size)) - - let availableHeight = layout.size.height - (layout.inputHeight ?? 0.0) - let previewSide = min(369.0, layout.size.width - 24.0) - let previewFrame: CGRect - if layout.metrics.isTablet { - previewFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - previewSide) / 2.0), y: max(layout.statusBarHeight ?? 0.0 + 24.0, availableHeight * 0.2 - previewSide / 2.0)), size: CGSize(width: previewSide, height: previewSide)) - } else { - previewFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - previewSide) / 2.0), y: max(layout.statusBarHeight ?? 0.0 + 16.0, availableHeight * 0.4 - previewSide / 2.0)), size: CGSize(width: previewSide, height: previewSide)) - } - if !self.animatingIn { - transition.setFrame(view: self.previewContainerView, frame: previewFrame) - } - transition.setCornerRadius(layer: self.previewContainerView.layer, cornerRadius: previewSide / 2.0) - - let previewInnerFrame = CGRect(origin: .zero, size: previewFrame.size) - - let additionalPreviewSize = CGSize(width: previewFrame.size.width, height: previewFrame.size.width / 3.0 * 4.0) - let additionalPreviewInnerFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((previewFrame.height - additionalPreviewSize.height) / 2.0)), size: additionalPreviewSize) - self.mainPreviewView.frame = previewInnerFrame - self.additionalPreviewView.frame = additionalPreviewInnerFrame - - self.progressView.frame = previewInnerFrame - self.progressView.value = CGFloat(self.cameraState.duration / 60.0) - - transition.setAlpha(view: self.additionalPreviewView, alpha: self.cameraState.position == .front ? 1.0 : 0.0) - if let previewState = self.previewState { if previewState.composition !== self.resultPreviewView?.composition { self.resultPreviewView?.removeFromSuperview() @@ -1105,10 +1217,7 @@ public class VideoMessageCameraScreen: ViewController { private let micLevelValue = ValuePromise(0.0) private let durationValue = ValuePromise(0.0) public let recordingStatus: RecordingStatus - - public var onDismiss: (Bool) -> Void = { _ in - } - + public var onStop: () -> Void = { } @@ -1251,6 +1360,7 @@ public class VideoMessageCameraScreen: ViewController { super.displayNodeDidLoad() } + fileprivate var isSendingImmediately = false public func sendVideoRecording() { if case .none = self.cameraState.recording, self.node.results.isEmpty { self.completion(nil) @@ -1259,6 +1369,7 @@ public class VideoMessageCameraScreen: ViewController { if case .none = self.cameraState.recording { } else { + self.isSendingImmediately = true self.waitingForNextResult = true self.node.stopRecording.invoke(Void()) } @@ -1362,6 +1473,8 @@ public class VideoMessageCameraScreen: ViewController { private var waitingForNextResult = false public func stopVideoRecording() -> Bool { + self.node.dismissAllTooltips() + self.waitingForNextResult = true self.node.transitioningToPreview = true self.node.requestUpdateLayout(transition: .spring(duration: 0.4)) @@ -1376,6 +1489,7 @@ public class VideoMessageCameraScreen: ViewController { public func lockVideoRecording() { if case .none = self.cameraState.recording { self.scheduledLock = true + self.node.requestUpdateLayout(transition: .spring(duration: 0.4)) } else { self.updateCameraState({ $0.updatedRecording(.handsFree) }, transition: .spring(duration: 0.4)) } @@ -1396,7 +1510,7 @@ public class VideoMessageCameraScreen: ViewController { } public func hideVideoSnapshot() { - self.node.previewContainerView.alpha = 0.02 + self.node.previewContainerView.isHidden = true } public func updateTrimRange(start: Double, end: Double, updatedEnd: Bool, apply: Bool) { @@ -1465,3 +1579,46 @@ private func composition(with results: [VideoMessageCameraScreen.CaptureResult]) } return composition } + +private class BlurView: UIVisualEffectView { + private func setup() { + for subview in self.subviews { + if subview.description.contains("VisualEffectSubview") { + subview.isHidden = true + } + } + + if let sublayer = self.layer.sublayers?[0], let filters = sublayer.filters { + sublayer.backgroundColor = nil + sublayer.isOpaque = false + let allowedKeys: [String] = [ + "gaussianBlur" + ] + sublayer.filters = filters.filter { filter in + guard let filter = filter as? NSObject else { + return true + } + let filterName = String(describing: filter) + if !allowedKeys.contains(filterName) { + return false + } + return true + } + } + } + + override var effect: UIVisualEffect? { + get { + return super.effect + } + set { + super.effect = newValue + self.setup() + } + } + + override func didAddSubview(_ subview: UIView) { + super.didAddSubview(subview) + self.setup() + } +} diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 3f1ef78082..12a2a1b8e8 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -6195,19 +6195,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let videoRecorder = videoRecorder { strongSelf.recorderFeedback?.impact(.light) - videoRecorder.onDismiss = { [weak self] isCancelled in - self?.chatDisplayNode.updateRecordedMediaDeleted(isCancelled) - self?.beginMediaRecordingRequestId += 1 - self?.lockMediaRecordingRequestId = nil - self?.videoRecorder.set(.single(nil)) - } videoRecorder.onStop = { if let strongSelf = self { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { - $0.updatedInputTextPanelState { panelState in - return panelState.withUpdatedMediaRecordingState(.video(status: .editing, isLocked: false)) - } - }) + strongSelf.dismissMediaRecorder(.pause) } } strongSelf.present(videoRecorder, in: .window(.root)) @@ -15352,7 +15342,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G func deactivateRaiseGesture() { self.raiseToListenActivateRecordingTimer?.invalidate() self.raiseToListenActivateRecordingTimer = nil - self.dismissMediaRecorder(.preview) + self.dismissMediaRecorder(.pause) } func requestAudioRecorder(beginWithTone: Bool) { @@ -15617,12 +15607,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } })) - -// self.updateChatPresentationInterfaceState(animated: true, interactive: true, { -// $0.updatedInputTextPanelState { panelState in -// return panelState.withUpdatedMediaRecordingState(.video(status: .editing, isLocked: false)) -// } -// }) } default: self.recorderDataDisposable.set(nil) @@ -15687,6 +15671,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.videoRecorder.set(.single(nil)) } + self.recorderDataDisposable.set(nil) self.chatDisplayNode.updateRecordedMediaDeleted(true) self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedRecordedMediaPreview(nil) diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift index 267bfdc3e2..63002ef7ca 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift @@ -635,7 +635,7 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran case let .videoMessage(videoMessage): let combinedTransition = CombinedTransition(horizontal: .animated(duration: horizontalDuration, curve: ChatMessageTransitionNodeImpl.horizontalAnimationCurve), vertical: .animated(duration: verticalDuration, curve: ChatMessageTransitionNodeImpl.verticalAnimationCurve)) - if let itemNode = self.itemNode as? ChatMessageInstantVideoItemNode { + if let itemNode = self.itemNode as? ChatMessageBubbleItemNode { itemNode.cancelInsertionAnimations() self.contextSourceNode.isExtractedToContextPreview = true @@ -659,7 +659,7 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran strongSelf.endAnimation() }) - itemNode.animateFromSnapshot(snapshotView: videoMessage.view, transition: combinedTransition) + itemNode.animateInstantVideoFromSnapshot(snapshotView: videoMessage.view, transition: combinedTransition) } case let .mediaInput(mediaInput): if let snapshotView = mediaInput.extractSnapshot() {