diff --git a/submodules/ComponentFlow/Source/Components/Button.swift b/submodules/ComponentFlow/Source/Components/Button.swift index 3a494c1e95..41211e34ba 100644 --- a/submodules/ComponentFlow/Source/Components/Button.swift +++ b/submodules/ComponentFlow/Source/Components/Button.swift @@ -7,6 +7,7 @@ public final class Button: Component { public let tag: AnyObject? public let automaticHighlight: Bool public let isEnabled: Bool + public let isExclusive: Bool public let action: () -> Void public let holdAction: (() -> Void)? public let highlightedAction: ActionSlot? @@ -36,6 +37,7 @@ public final class Button: Component { tag: AnyObject? = nil, automaticHighlight: Bool = true, isEnabled: Bool = true, + isExclusive: Bool = true, action: @escaping () -> Void, holdAction: (() -> Void)?, highlightedAction: ActionSlot? @@ -45,6 +47,7 @@ public final class Button: Component { self.tag = tag self.automaticHighlight = automaticHighlight self.isEnabled = isEnabled + self.isExclusive = isExclusive self.action = action self.holdAction = holdAction self.highlightedAction = highlightedAction @@ -57,12 +60,28 @@ public final class Button: Component { tag: self.tag, automaticHighlight: self.automaticHighlight, isEnabled: self.isEnabled, + isExclusive: self.isExclusive, action: self.action, holdAction: self.holdAction, highlightedAction: self.highlightedAction ) } + public func withIsExclusive(_ isExclusive: Bool) -> Button { + return Button( + content: self.content, + minSize: self.minSize, + tag: self.tag, + automaticHighlight: self.automaticHighlight, + isEnabled: self.isEnabled, + isExclusive: isExclusive, + action: self.action, + holdAction: self.holdAction, + highlightedAction: self.highlightedAction + ) + } + + public func withHoldAction(_ holdAction: (() -> Void)?) -> Button { return Button( content: self.content, @@ -70,6 +89,7 @@ public final class Button: Component { tag: self.tag, automaticHighlight: self.automaticHighlight, isEnabled: self.isEnabled, + isExclusive: self.isExclusive, action: self.action, holdAction: holdAction, highlightedAction: self.highlightedAction @@ -83,6 +103,7 @@ public final class Button: Component { tag: tag, automaticHighlight: self.automaticHighlight, isEnabled: self.isEnabled, + isExclusive: self.isExclusive, action: self.action, holdAction: self.holdAction, highlightedAction: self.highlightedAction @@ -105,6 +126,9 @@ public final class Button: Component { if lhs.isEnabled != rhs.isEnabled { return false } + if lhs.isExclusive != rhs.isExclusive { + return false + } return true } @@ -157,8 +181,6 @@ public final class Button: Component { super.init(frame: frame) - self.isExclusiveTouch = true - self.addSubview(self.contentView) self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) @@ -267,6 +289,7 @@ public final class Button: Component { self.updateAlpha(transition: transition) self.isEnabled = component.isEnabled + self.isExclusiveTouch = component.isExclusive transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - contentSize.width) / 2.0), y: floor((size.height - contentSize.height) / 2.0)), size: contentSize), completion: nil) diff --git a/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift index 78c0da2bf3..dfe7aec55c 100644 --- a/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift @@ -14,7 +14,50 @@ import UniversalMediaPlayer import TelegramPresentationData import TelegramUniversalVideoContent -public class DrawingStickerEntityView: DrawingEntityView { +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() + } +} + +public class DrawingStickerEntityView: DrawingEntityView { var stickerEntity: DrawingStickerEntity { return self.entity as! DrawingStickerEntity } @@ -342,15 +385,59 @@ public class DrawingStickerEntityView: DrawingEntityView { guard let cameraPreviewView = self.cameraPreviewView else { return } - Queue.mainQueue().after(0.5, { + Queue.mainQueue().after(0.3, { self.cameraPreviewView = nil - cameraPreviewView.removeFromSuperview() + cameraPreviewView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + cameraPreviewView.removeFromSuperview() + }) }) self.progressLayer.removeFromSuperlayer() self.progressLayer.path = nil self.progressDisposable.set(nil) } + private var cameraBlurView: BlurView? + private var cameraSnapshotView: UIView? + public func beginCameraSwitch() { + guard let cameraPreviewView = self.cameraPreviewView, self.cameraBlurView == nil else { + return + } + if let snapshot = cameraPreviewView.snapshotView(afterScreenUpdates: false) { + self.cameraSnapshotView = snapshot + self.addSubview(snapshot) + } + + let blurView = BlurView(effect: nil) + blurView.clipsToBounds = true + blurView.frame = self.bounds + blurView.layer.cornerRadius = self.bounds.width / 2.0 + self.addSubview(blurView) + UIView.transition(with: self, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: { + blurView.effect = UIBlurEffect(style: .dark) + }) + self.cameraBlurView = blurView + } + + public func commitCameraSwitch() { + if let cameraBlurView = self.cameraBlurView { + self.cameraBlurView = nil + UIView.animate(withDuration: 0.4, animations: { + cameraBlurView.effect = nil + }, completion: { _ in + cameraBlurView.removeFromSuperview() + }) + } + + if let cameraSnapshotView = self.cameraSnapshotView { + self.cameraSnapshotView = nil + UIView.animate(withDuration: 0.25, animations: { + cameraSnapshotView.alpha = 0.0 + }, completion: { _ in + cameraSnapshotView.removeFromSuperview() + }) + } + } + private var didApplyVisibility = false public override func layoutSubviews() { super.layoutSubviews() diff --git a/submodules/DrawingUI/Sources/VideoRecorder.swift b/submodules/DrawingUI/Sources/VideoRecorder.swift index b835582b5f..8c78d1cd03 100644 --- a/submodules/DrawingUI/Sources/VideoRecorder.swift +++ b/submodules/DrawingUI/Sources/VideoRecorder.swift @@ -14,11 +14,14 @@ public final class EntityVideoRecorder { private let camera: Camera private let previewView: CameraSimplePreviewView private let entity: DrawingStickerEntity + private weak var entityView: DrawingStickerEntityView? private var recordingDisposable = MetaDisposable() private let durationPromise = ValuePromise() private let micLevelPromise = Promise() + private var changingPositionDisposable: Disposable? + public var duration: Signal { return self.durationPromise.get() } @@ -79,10 +82,23 @@ public final class EntityVideoRecorder { let start = mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0 mediaEditor.stop() mediaEditor.seek(start, andPlay: false) + + self.changingPositionDisposable = (camera.modeChange + |> deliverOnMainQueue).start(next: { [weak self] modeChange in + guard let self else { + return + } + if case .position = modeChange { + self.entityView?.beginCameraSwitch() + } else { + self.entityView?.commitCameraSwitch() + } + }) } deinit { self.recordingDisposable.dispose() + self.changingPositionDisposable?.dispose() } public func setup( @@ -105,6 +121,8 @@ public final class EntityVideoRecorder { ) self.previewView.resetPlaceholder(front: true) entityView.animateInsertion() + + self.entityView = entityView } self.entitiesView?.selectEntity(nil) @@ -160,8 +178,10 @@ public final class EntityVideoRecorder { } if let entityView = entitiesView.getView(for: self.entity.uuid) as? DrawingStickerEntityView { - entityView.invalidateCameraPreviewView() - + mediaEditor.onFirstAdditionalDisplay = { [weak entityView] in + entityView?.invalidateCameraPreviewView() + } + let entity = self.entity let update = { [weak mediaEditor, weak entity] in if let mediaEditor, let entity { diff --git a/submodules/StatisticsUI/Sources/MessageStatsController.swift b/submodules/StatisticsUI/Sources/MessageStatsController.swift index 1c5e356e89..3a9ed4abf4 100644 --- a/submodules/StatisticsUI/Sources/MessageStatsController.swift +++ b/submodules/StatisticsUI/Sources/MessageStatsController.swift @@ -166,17 +166,36 @@ private enum StatsEntry: ItemListNodeEntry { }, sectionId: self.section, style: .blocks) case let .publicForward(_, _, _, _, message): var views: Int32 = 0 + var forwards: Int32 = 0 + var reactions: Int32 = 0 for attribute in message.attributes { if let viewsAttribute = attribute as? ViewCountMessageAttribute { views = Int32(viewsAttribute.count) - break + } else if let forwardsAttribute = attribute as? ForwardCountMessageAttribute { + forwards = Int32(forwardsAttribute.count) + } else if let reactionsAttribute = attribute as? ReactionsMessageAttribute { + reactions = reactionsAttribute.reactions.reduce(0, { partialResult, reaction in + return partialResult + reaction.count + }) } } - - let text: String = presentationData.strings.Stats_MessageViews(views) - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(message.peers[message.id.peerId]!), height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(text, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: { + let peer = message.peers[message.id.peerId]! + return StatsMessageItem(context: arguments.context, presentationData: presentationData, peer: peer, item: .message(message._asMessage()), views: views, reactions: reactions, forwards: forwards, isPeer: true, sectionId: self.section, style: .blocks, action: { arguments.openMessage(message.id) - }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil) + }, openStory: { _ in }, contextAction: nil) +// var views: Int32 = 0 +// for attribute in message.attributes { +// if let viewsAttribute = attribute as? ViewCountMessageAttribute { +// views = Int32(viewsAttribute.count) +// break +// } +// } +// +// +// let text: String = presentationData.strings.Stats_MessageViews(views) +// return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(message.peers[message.id.peerId]!), height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(text, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: { +// arguments.openMessage(message.id) +// }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil) } } } diff --git a/submodules/StatisticsUI/Sources/StatsMessageItem.swift b/submodules/StatisticsUI/Sources/StatsMessageItem.swift index e8c3b21f10..e7de7bf930 100644 --- a/submodules/StatisticsUI/Sources/StatsMessageItem.swift +++ b/submodules/StatisticsUI/Sources/StatsMessageItem.swift @@ -22,13 +22,14 @@ public class StatsMessageItem: ListViewItem, ItemListItem { let views: Int32 let reactions: Int32 let forwards: Int32 + let isPeer: Bool public let sectionId: ItemListSectionId let style: ItemListStyle let action: (() -> Void)? let openStory: (UIView) -> Void let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? - init(context: AccountContext, presentationData: ItemListPresentationData, peer: Peer, item: StatsPostItem, views: Int32, reactions: Int32, forwards: Int32, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, openStory: @escaping (UIView) -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) { + init(context: AccountContext, presentationData: ItemListPresentationData, peer: Peer, item: StatsPostItem, views: Int32, reactions: Int32, forwards: Int32, isPeer: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, openStory: @escaping (UIView) -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) { self.context = context self.presentationData = presentationData self.peer = peer @@ -36,6 +37,7 @@ public class StatsMessageItem: ListViewItem, ItemListItem { self.views = views self.reactions = reactions self.forwards = forwards + self.isPeer = isPeer self.sectionId = sectionId self.style = style self.action = action diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift index 5623da4a07..8ca98d2550 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift @@ -261,6 +261,7 @@ public final class MediaEditor { } public var onFirstDisplay: () -> Void = {} + public var onFirstAdditionalDisplay: () -> Void = {} public func playerState(framesCount: Int) -> Signal { func artistAndTitleForTrack(_ audioTrack: MediaAudioTrack) -> (artist: String?, title: String?) { @@ -713,6 +714,10 @@ public final class MediaEditor { }) } + public func setOnNextAdditionalDisplay(_ f: @escaping () -> Void) { + self.renderer.onNextAdditionalRender = f + } + private func setupTimeObservers() { var observedPlayer = self.player if observedPlayer == nil { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift index 5dfd119b82..0242d8dc45 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift @@ -106,6 +106,7 @@ final class MediaEditorRenderer { var needsDisplay = false var onNextRender: (() -> Void)? + var onNextAdditionalRender: (() -> Void)? public init() { @@ -277,6 +278,15 @@ final class MediaEditorRenderer { onNextRender() } } + + if let onNextAdditionalRender = self.onNextAdditionalRender { + if self.currentAdditionalInput != nil { + self.onNextAdditionalRender = nil + Queue.mainQueue().after(0.016) { + onNextAdditionalRender() + } + } + } } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 3a3b544c86..96f0b39e2b 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -1743,7 +1743,7 @@ final class MediaEditorScreenComponent: Component { controller.node.recording.togglePosition() } } - )), + ).withIsExclusive(false)), environment: {}, containerSize: CGSize(width: 48.0, height: 48.0) ) diff --git a/submodules/TelegramUI/Resources/Animations/BoostReplace.tgs b/submodules/TelegramUI/Resources/Animations/BoostReplace.tgs new file mode 100644 index 0000000000..6524877a99 Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/BoostReplace.tgs differ