diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 6de88cee9e..c7b6ffae17 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -441,7 +441,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att if case let .assets(_, mode) = controller.subject { switch mode { - case .default, .story, .poll: + case .default, .poll: self.containerNode.view.addSubview(self.bottomEdgeEffectView) default: break diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 0d2a373bb0..5d66b24aa6 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -3162,7 +3162,6 @@ private func albumArtFullSizeDatas(engine: TelegramEngine, file: FileMediaRefere return .single(Tuple(nil, nil, false)) } } - } } |> distinctUntilChanged(isEqual: { lhs, rhs in @@ -3229,25 +3228,32 @@ public func playerAlbumArt(postbox: Postbox, engine: TelegramEngine, fileReferen } ) } - - var immediateArtworkData: Signal, NoError> = .single(Tuple(nil, nil, false)) - - if let fileReference = fileReference, let smallestRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) { + + func previewArtworkData(fileReference: FileMediaReference) -> Signal { + guard let smallestRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) else { + return .single(nil) + } + let thumbnailResource = smallestRepresentation.resource - let fetchedThumbnail = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: .other, userContentType: .image, reference: fileReference.resourceReference(thumbnailResource)) - - let thumbnail = Signal { subscriber in + + return Signal { subscriber in let fetchedDisposable = fetchedThumbnail.start() let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource, attemptSynchronously: attemptSynchronously).start(next: { next in subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) }, error: subscriber.putError, completed: subscriber.putCompletion) - + return ActionDisposable { fetchedDisposable.dispose() thumbnailDisposable.dispose() } } + } + + var immediateArtworkData: Signal, NoError> = .single(Tuple(nil, nil, false)) + + if let fileReference = fileReference, thumbnail, smallestImageRepresentation(fileReference.media.previewRepresentations) != nil { + let thumbnail = previewArtworkData(fileReference: fileReference) immediateArtworkData = thumbnail |> map { thumbnailData in return Tuple(thumbnailData, nil, false) @@ -3259,7 +3265,36 @@ public func playerAlbumArt(postbox: Postbox, engine: TelegramEngine, fileReferen return Tuple(thumbnailData, nil, false) } } else { - immediateArtworkData = albumArtFullSizeDatas(engine: engine, file: fileReference, thumbnail: albumArt.thumbnailResource, fullSize: albumArt.fullSizeResource) + let previewData: Signal + if let fileReference = fileReference { + previewData = previewArtworkData(fileReference: fileReference) + } else { + previewData = .single(nil) + } + + immediateArtworkData = combineLatest( + albumArtFullSizeDatas(engine: engine, file: fileReference, thumbnail: albumArt.thumbnailResource, fullSize: albumArt.fullSizeResource), + previewData + ) + |> map { remoteArtworkData, previewData in + let remoteFullSizeData = remoteArtworkData._1 + let shouldUsePreviewFallback: Bool + if remoteArtworkData._2 { + if let remoteFullSizeData = remoteFullSizeData { + shouldUsePreviewFallback = remoteFullSizeData.isEmpty + } else { + shouldUsePreviewFallback = true + } + } else { + shouldUsePreviewFallback = false + } + + if shouldUsePreviewFallback { + return Tuple(previewData, nil, false) + } else { + return remoteArtworkData + } + } } } else { immediateArtworkData = .single(Tuple(nil, nil, false)) diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 2862519fe9..97d8168aaf 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -17,6 +17,7 @@ import UndoUI import ChatHistoryEntry import MultilineTextComponent import GlassControls +import PhotoResources final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestureRecognizerDelegate { let ready = Promise() @@ -61,6 +62,10 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu private var replacementHistoryNode: ChatHistoryListNodeImpl? private var replacementHistoryNodeFloatingOffset: CGFloat? + private var currentAlbumArt: (FileMediaReference, SharedMediaPlaybackAlbumArt)? + private let albumArtBackground: UIVisualEffectView + private let albumArtNode = TransformImageNode() + private var saveMediaDisposable: MetaDisposable? private var validLayout: ContainerViewLayout? @@ -335,6 +340,10 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.collapseNode.displaysAsynchronously = false self.collapseNode.setImage(generateCollapseIcon(theme: self.presentationData.theme), for: []) + self.albumArtBackground = UIVisualEffectView() + self.albumArtBackground.contentView.addSubview(self.albumArtNode.view) + self.albumArtNode.isUserInteractionEnabled = false + super.init() self.backgroundColor = nil @@ -376,6 +385,19 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } } + self.controlsNode.requestAlbumArtDisplay = { [weak self] fileReferenceAndAlbumArt in + guard let self, let layout = self.validLayout else { + return + } + self.currentAlbumArt = fileReferenceAndAlbumArt + + if let (fileReference, albumArt) = fileReferenceAndAlbumArt { + self.albumArtNode.setSignal(playerAlbumArt(postbox: self.context.account.postbox, engine: self.context.engine, fileReference: fileReference, albumArt: albumArt, thumbnail: false)) + } + + self.containerLayoutUpdated(layout, transition: .animated(duration: 0.25, curve: .easeInOut)) + } + self.controlsNode.requestCollapse = { [weak self] in self?.requestDismiss() } @@ -542,6 +564,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu ) self.setupReordering() + + self.albumArtBackground.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.albumArtTapped(_:)))) } deinit { @@ -841,7 +865,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu let controlsHeight = self.controlsNode.updateLayout(width: layout.size.width, leftInset: 0.0, rightInset: 0.0, bottomInset: layout.intrinsicInsets.bottom, maxHeight: layout.size.height, savedMusic: self.isSaved, transition: transition) let controlsFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - controlsHeight), size: CGSize(width: layout.size.width, height: controlsHeight)) - transition.updateFrame(node: self.controlsNode, frame: controlsFrame) + let controlsTransition = self.controlsNode.frame.width > 0.0 ? transition : .immediate + controlsTransition.updateFrame(node: self.controlsNode, frame: controlsFrame) let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top) var insets = UIEdgeInsets() @@ -962,6 +987,49 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.updateHistoryContentOffset(self.historyNode.visibleContentOffset(), transition: transition) + self.albumArtBackground.frame = CGRect(origin: .zero, size: layout.size) + + if let _ = self.currentAlbumArt { + var animateIn = false + if self.albumArtBackground.superview == nil { + self.view.addSubview(self.albumArtBackground) + animateIn = true + } + let albumArtSide = min(360.0, layout.size.width - 32.0) + let albumArtSize = CGSize(width: albumArtSide, height: albumArtSide) + self.albumArtNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - albumArtSize.width) / 2.0), y: floorToScreenPixels((layout.size.height - albumArtSize.height) / 2.0)), size: albumArtSize) + + let makeLargeAlbumArtLayout = self.albumArtNode.asyncLayout() + let applyLargeAlbumArt = makeLargeAlbumArtLayout(TransformImageArguments(corners: ImageCorners(radius: 4.0), imageSize: albumArtSize, boundingSize: albumArtSize, intrinsicInsets: UIEdgeInsets())) + applyLargeAlbumArt() + + if animateIn { + self.controlsNode.albumArtNode.alpha = 0.0 + + let sourceFrame = self.controlsNode.albumArtNode.view.convert(self.controlsNode.albumArtNode.bounds, to: self.albumArtBackground.contentView) + ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring).animateFrame(node: self.albumArtNode, from: sourceFrame) + UIView.animate(withDuration: 0.2, animations: { + self.albumArtBackground.effect = UIBlurEffect(style: self.presentationData.theme.overallDarkAppearance ? .dark : .light) + }) + } + } else { + self.controlsNode.albumArtNode.alpha = 1.0 + + if self.albumArtBackground.superview != nil { + let fadeTransition = ComponentTransition(transition) + fadeTransition.setBlur(layer: self.albumArtNode.layer, radius: 10.0) + fadeTransition.setAlpha(layer: self.albumArtNode.layer, alpha: 0.0) + + UIView.animate(withDuration: 0.2, animations: { + self.albumArtBackground.effect = nil + }, completion: { _ in + self.albumArtBackground.removeFromSuperview() + ComponentTransition.immediate.setBlur(layer: self.albumArtNode.layer, radius: 0.0) + ComponentTransition.immediate.setAlpha(layer: self.albumArtNode.layer, alpha: 1.0) + }) + } + } + var layout = layout layout.intrinsicInsets.bottom = controlsHeight + (self.historyNode.hasAnyMessages ? 0.0 : 8.0) self.getParentController()?.presentationContext.containerLayoutUpdated(layout, transition: transition) @@ -999,6 +1067,13 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.requestDismiss() } } + + @objc func albumArtTapped(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state, let layout = self.validLayout { + self.currentAlbumArt = nil + self.containerLayoutUpdated(layout, transition: .animated(duration: 0.3, curve: .easeInOut)) + } + } override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if let recognizer = gestureRecognizer as? UIPanGestureRecognizer { @@ -1566,7 +1641,7 @@ private func generateCollapseIcon(theme: PresentationTheme) -> UIImage? { private func generateCornersImage(theme: PresentationTheme) -> UIImage? { return generateImage(CGSize(width: 56.0, height: 56.0), rotatedContext: { (size, context) in let bounds = CGRect(origin: CGPoint(), size: size) - context.setFillColor(theme.list.blocksBackgroundColor.cgColor) + context.setFillColor(theme.list.modalBlocksBackgroundColor.cgColor) context.fill(bounds) context.setBlendMode(.clear) diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControlsNode.swift index 546ddc9884..295df935ca 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControlsNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControlsNode.swift @@ -148,8 +148,7 @@ final class OverlayAudioPlayerControlsNode: ASDisplayNode { private let backgroundNode: ASImageNode - private let albumArtNode: TransformImageNode - private var largeAlbumArtNode: TransformImageNode? + let albumArtNode: TransformImageNode private let titleNode: TextNode private let title: ComponentView private let descriptionNode: TextNode @@ -193,6 +192,7 @@ final class OverlayAudioPlayerControlsNode: ASDisplayNode { var isExpanded = false var updateIsExpanded: (() -> Void)? + var requestAlbumArtDisplay: (((FileMediaReference, SharedMediaPlaybackAlbumArt)?) -> Void)? var requestCollapse: (() -> Void)? var requestShare: ((ShareControllerSubject) -> Void)? @@ -222,7 +222,6 @@ final class OverlayAudioPlayerControlsNode: ASDisplayNode { private let hapticFeedback = HapticFeedback() - private var scrubbingDisposable: Disposable? private var leftDurationLabelPushed = false private var rightDurationLabelPushed = false private var infoNodePushed = false @@ -356,33 +355,7 @@ final class OverlayAudioPlayerControlsNode: ASDisplayNode { self.scrubberNode.status = mappedStatus self.leftDurationLabel.status = mappedStatus self.rightDurationLabel.status = mappedStatus - -// self.scrubbingDisposable = (self.scrubberNode.scrubbingPosition -// |> deliverOnMainQueue).startStrict(next: { [weak self] value in -// guard let strongSelf = self else { -// return -// } -// let leftDurationLabelPushed: Bool -// let rightDurationLabelPushed: Bool -// let infoNodePushed: Bool -// if let value = value { -// leftDurationLabelPushed = value < 0.16 -// rightDurationLabelPushed = value > (strongSelf.rateButton.isHidden ? 0.84 : 0.74) -// infoNodePushed = value >= 0.16 && value <= 0.84 -// } else { -// leftDurationLabelPushed = false -// rightDurationLabelPushed = false -// infoNodePushed = false -// } -// if leftDurationLabelPushed != strongSelf.leftDurationLabelPushed || rightDurationLabelPushed != strongSelf.rightDurationLabelPushed || infoNodePushed != strongSelf.infoNodePushed { -// strongSelf.leftDurationLabelPushed = leftDurationLabelPushed -// strongSelf.rightDurationLabelPushed = rightDurationLabelPushed -// strongSelf.infoNodePushed = infoNodePushed -// -// strongSelf.requestLayout(transition: .animated(duration: 0.35, curve: .spring)) -// } -// }) - + self.statusDisposable = combineLatest( queue: Queue.mainQueue(), delayedStatus, @@ -592,7 +565,6 @@ final class OverlayAudioPlayerControlsNode: ASDisplayNode { deinit { self.statusDisposable?.dispose() self.chapterDisposable?.dispose() - self.scrubbingDisposable?.dispose() } override func didLoad() { @@ -788,9 +760,7 @@ final class OverlayAudioPlayerControlsNode: ASDisplayNode { self.currentAlbumArtInitialized = true self.currentAlbumArt = albumArt self.albumArtNode.setSignal(playerAlbumArt(postbox: self.account.postbox, engine: self.engine, fileReference: self.currentFileReference, albumArt: albumArt, thumbnail: true)) - if let largeAlbumArtNode = self.largeAlbumArtNode { - largeAlbumArtNode.setSignal(playerAlbumArt(postbox: self.account.postbox, engine: self.engine, fileReference: self.currentFileReference, albumArt: albumArt, thumbnail: false)) - } + self.requestAlbumArtDisplay?(nil) } } @@ -880,72 +850,8 @@ final class OverlayAudioPlayerControlsNode: ASDisplayNode { let applyAlbumArt = makeAlbumArtLayout(TransformImageArguments(corners: ImageCorners(radius: 10.0), imageSize: albumArtSize, boundingSize: albumArtSize, intrinsicInsets: UIEdgeInsets())) applyAlbumArt() let albumArtFrame = CGRect(origin: CGPoint(x: leftInset + sideInset, y: infoVerticalOrigin - 3.0), size: albumArtSize) - let previousAlbumArtNodeFrame = self.albumArtNode.frame transition.updateFrame(node: self.albumArtNode, frame: albumArtFrame) - if self.isExpanded { - let largeAlbumArtNode: TransformImageNode - var animateIn = false - if let current = self.largeAlbumArtNode { - largeAlbumArtNode = current - } else { - animateIn = true - largeAlbumArtNode = TransformImageNode() - self.largeAlbumArtNode = largeAlbumArtNode - self.addSubnode(largeAlbumArtNode) - if self.currentAlbumArtInitialized { - largeAlbumArtNode.setSignal(playerAlbumArt(postbox: self.account.postbox, engine: self.engine, fileReference: self.currentFileReference, albumArt: self.currentAlbumArt, thumbnail: false)) - } - } - - let albumArtHeight = max(1.0, panelHeight - OverlayAudioPlayerControlsNode.basePanelHeight - 24.0) - - let largeAlbumArtSize = CGSize(width: albumArtHeight, height: albumArtHeight) - let makeLargeAlbumArtLayout = largeAlbumArtNode.asyncLayout() - let applyLargeAlbumArt = makeLargeAlbumArtLayout(TransformImageArguments(corners: ImageCorners(radius: 4.0), imageSize: largeAlbumArtSize, boundingSize: largeAlbumArtSize, intrinsicInsets: UIEdgeInsets())) - applyLargeAlbumArt() - - let largeAlbumArtFrame = CGRect(origin: CGPoint(x: floor((width - largeAlbumArtSize.width) / 2.0), y: 34.0), size: largeAlbumArtSize) - - if animateIn && transition.isAnimated { - largeAlbumArtNode.frame = largeAlbumArtFrame - transition.animatePositionAdditive(node: largeAlbumArtNode, offset: CGPoint(x: previousAlbumArtNodeFrame.center.x - largeAlbumArtFrame.center.x, y: previousAlbumArtNodeFrame.center.y - largeAlbumArtFrame.center.y)) - //largeAlbumArtNode.layer.animatePosition(from: CGPoint(x: -50.0, y: 0.0), to: CGPoint(), duration: 0.15, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, additive: true) - transition.animateTransformScale(node: largeAlbumArtNode, from: previousAlbumArtNodeFrame.size.height / largeAlbumArtFrame.size.height) - largeAlbumArtNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12) - if let copyView = self.albumArtNode.view.snapshotContentTree() { - copyView.frame = previousAlbumArtNodeFrame - copyView.center = largeAlbumArtFrame.center - self.view.insertSubview(copyView, belowSubview: largeAlbumArtNode.view) - transition.animatePositionAdditive(layer: copyView.layer, offset: CGPoint(x: previousAlbumArtNodeFrame.center.x - largeAlbumArtFrame.center.x, y: previousAlbumArtNodeFrame.center.y - largeAlbumArtFrame.center.y), completion: { [weak copyView] _ in - copyView?.removeFromSuperview() - }) - //copyView.layer.animatePosition(from: CGPoint(x: -50.0, y: 0.0), to: CGPoint(), duration: 0.15, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, additive: true) - copyView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.28, removeOnCompletion: false) - transition.updateTransformScale(layer: copyView.layer, scale: largeAlbumArtFrame.size.height / previousAlbumArtNodeFrame.size.height) - } - } else { - transition.updateFrame(node: largeAlbumArtNode, frame: largeAlbumArtFrame) - } - self.albumArtNode.isHidden = true - } else if let largeAlbumArtNode = self.largeAlbumArtNode { - self.largeAlbumArtNode = nil - self.albumArtNode.isHidden = false - if transition.isAnimated { - transition.animatePosition(node: self.albumArtNode, from: largeAlbumArtNode.frame.center) - transition.animateTransformScale(node: self.albumArtNode, from: largeAlbumArtNode.frame.height / self.albumArtNode.frame.height) - self.albumArtNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12) - - transition.updatePosition(node: largeAlbumArtNode, position: self.albumArtNode.frame.center, completion: { [weak largeAlbumArtNode] _ in - largeAlbumArtNode?.removeFromSupernode() - }) - largeAlbumArtNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.28, removeOnCompletion: false) - transition.updateTransformScale(node: largeAlbumArtNode, scale: self.albumArtNode.frame.height / largeAlbumArtNode.frame.height) - } else { - largeAlbumArtNode.removeFromSupernode() - } - } - let scrubberVerticalOrigin: CGFloat = infoVerticalOrigin + 58.0 let scrubberInset: CGFloat = 9.0 @@ -1283,15 +1189,16 @@ final class OverlayAudioPlayerControlsNode: ASDisplayNode { } @objc func albumArtTap(_ recognizer: UITapGestureRecognizer) { - if !"".isEmpty, case .ended = recognizer.state { + if case .ended = recognizer.state { if let supernode = self.supernode { let bounds = supernode.bounds if bounds.width > bounds.height { return } } - self.isExpanded = !self.isExpanded - self.updateIsExpanded?() + if let currentFileReference = self.currentFileReference, let currentAlbumArt = self.currentAlbumArt { + self.requestAlbumArtDisplay?((currentFileReference, currentAlbumArt)) + } } }