diff --git a/submodules/Camera/Sources/Camera.swift b/submodules/Camera/Sources/Camera.swift index 61df7f7ef5..cff6761128 100644 --- a/submodules/Camera/Sources/Camera.swift +++ b/submodules/Camera/Sources/Camera.swift @@ -366,17 +366,17 @@ private final class CameraContext { |> map { first, second in return first && second } - |> filter { $0 } - |> take(1) - |> delay(0.1, queue: self.queue) - |> deliverOn(self.queue)).start(next: { [weak self] _ in + |> filter { $0 } + |> take(1) + |> delay(0.1, queue: self.queue) + |> deliverOn(self.queue)).start(next: { [weak self] _ in self?.modeChange = .none }) } else { let _ = (previewView.isPreviewing - |> filter { $0 } - |> take(1) - |> deliverOn(self.queue)).start(next: { [weak self] _ in + |> filter { $0 } + |> take(1) + |> deliverOn(self.queue)).start(next: { [weak self] _ in self?.modeChange = .none }) } diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index aac4fb4477..4c7013f09a 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -75,7 +75,6 @@ final class CameraOutput: NSObject { let videoOutput = AVCaptureVideoDataOutput() let audioOutput = AVCaptureAudioDataOutput() let metadataOutput = AVCaptureMetadataOutput() - private let faceLandmarksOutput = FaceLandmarksDataOutput() let exclusive: Bool @@ -85,17 +84,12 @@ final class CameraOutput: NSObject { private let queue = DispatchQueue(label: "") private let metadataQueue = DispatchQueue(label: "") - private let faceLandmarksQueue = DispatchQueue(label: "") private var photoCaptureRequests: [Int64: PhotoCaptureContext] = [:] private var videoRecorder: VideoRecorder? - - var activeFilter: CameraFilter? - var faceLandmarks: Bool = false - + var processSampleBuffer: ((CMSampleBuffer, CVImageBuffer, AVCaptureConnection) -> Void)? var processCodes: (([CameraCode]) -> Void)? - var processFaceLandmarks: (([VNFaceObservation]) -> Void)? init(exclusive: Bool) { self.exclusive = exclusive @@ -104,12 +98,6 @@ final class CameraOutput: NSObject { self.videoOutput.alwaysDiscardsLateVideoFrames = false self.videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] as [String : Any] - - self.faceLandmarksOutput.outputFaceObservations = { [weak self] observations in - if let self { - self.processFaceLandmarks?(observations) - } - } } deinit { @@ -273,7 +261,7 @@ final class CameraOutput: NSObject { } let uniqueId = settings.uniqueID - let photoCapture = PhotoCaptureContext(settings: settings, filter: self.activeFilter, mirror: mirror) + let photoCapture = PhotoCaptureContext(settings: settings, filter: nil, mirror: mirror) self.photoCaptureRequests[uniqueId] = photoCapture self.photoOutput.capturePhoto(with: settings, delegate: photoCapture) @@ -361,32 +349,10 @@ extension CameraOutput: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureA return } - if self.faceLandmarks { - self.faceLandmarksQueue.async { - self.faceLandmarksOutput.process(sampleBuffer: sampleBuffer) - } - } - if let videoPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { self.processSampleBuffer?(sampleBuffer, videoPixelBuffer, connection) } -// let finalSampleBuffer: CMSampleBuffer = sampleBuffer -// if let videoPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer), let formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer) { -// var finalVideoPixelBuffer = videoPixelBuffer -// if let filter = self.activeFilter { -// if !filter.isPrepared { -// filter.prepare(with: formatDescription, outputRetainedBufferCountHint: 3) -// } -// -// guard let filteredBuffer = filter.render(pixelBuffer: finalVideoPixelBuffer) else { -// return -// } -// finalVideoPixelBuffer = filteredBuffer -// } -// self.processSampleBuffer?(finalVideoPixelBuffer, connection) -// } - if let videoRecorder = self.videoRecorder, videoRecorder.isRecording { videoRecorder.appendSampleBuffer(sampleBuffer) } diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index f6774628f1..bc9a13aac0 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -2975,6 +2975,8 @@ public final class DrawingToolsInteraction { private var isActive = false private var validLayout: ContainerViewLayout? + private let startTimestamp = CACurrentMediaTime() + public init( context: AccountContext, drawingView: DrawingView, @@ -3126,6 +3128,10 @@ public final class DrawingToolsInteraction { if let textEntityView = entityView as? DrawingTextEntityView { textEntityView.beginEditing(accessoryView: self.textEditAccessoryView) } else { + if self.isVideo { + entityView.seek(to: 0.0) + } + entityView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) entityView.layer.animateScale(from: 0.1, to: entity.scale, duration: 0.2) diff --git a/submodules/MediaPickerUI/BUILD b/submodules/MediaPickerUI/BUILD index 837f51730f..b9a2a15a4f 100644 --- a/submodules/MediaPickerUI/BUILD +++ b/submodules/MediaPickerUI/BUILD @@ -43,7 +43,8 @@ swift_library( "//submodules/MoreButtonNode:MoreButtonNode", "//submodules/InvisibleInkDustNode:InvisibleInkDustNode", "//submodules/TelegramUI/Components/CameraScreen", - "//submodules/TelegramUI/Components/MediaEditor", + "//submodules/TelegramUI/Components/MediaEditor", + "//submodules/RadialStatusNode", ], visibility = [ "//visibility:public", diff --git a/submodules/MediaPickerUI/Sources/FetchAssets.swift b/submodules/MediaPickerUI/Sources/FetchAssets.swift index 411543fff4..7b236c40a7 100644 --- a/submodules/MediaPickerUI/Sources/FetchAssets.swift +++ b/submodules/MediaPickerUI/Sources/FetchAssets.swift @@ -17,7 +17,7 @@ final class AssetDownloadManager { let identifier: String let updated: () -> Void - var status: AssetDownloadStatus = .progress(0.0) + var status: AssetDownloadStatus = .none var disposable: Disposable? init(identifier: String, updated: @escaping () -> Void) { @@ -40,9 +40,7 @@ final class AssetDownloadManager { } func download(asset: PHAsset) { - if let currentAssetContext = self.currentAssetContext { - currentAssetContext.disposable?.dispose() - } + self.cancelAllDownloads() let queue = self.queue let identifier = asset.localIdentifier @@ -59,6 +57,7 @@ final class AssetDownloadManager { } } }) + self.currentAssetContext = assetContext assetContext.disposable = (downloadAssetMediaData(asset) |> deliverOn(queue)).start(next: { [weak self] status in guard let self else { @@ -69,13 +68,31 @@ final class AssetDownloadManager { currentAssetContext.updated() } }) - self.currentAssetContext = assetContext + } + + func cancelAllDownloads() { + if let currentAssetContext = self.currentAssetContext { + currentAssetContext.status = .none + currentAssetContext.updated() + currentAssetContext.disposable?.dispose() + self.queue.justDispatch { + if self.currentAssetContext === currentAssetContext { + self.currentAssetContext = nil + } + } + } } func cancel(identifier: String) { if let currentAssetContext = self.currentAssetContext, currentAssetContext.identifier == identifier { + currentAssetContext.status = .none + currentAssetContext.updated() currentAssetContext.disposable?.dispose() - self.currentAssetContext = nil + self.queue.justDispatch { + if self.currentAssetContext === currentAssetContext { + self.currentAssetContext = nil + } + } } } @@ -93,7 +110,7 @@ final class AssetDownloadManager { if let currentAssetContext = self.currentAssetContext, currentAssetContext.identifier == identifier { next(currentAssetContext.status) } else { - next(.progress(0.0)) + next(.none) } let queue = self.queue @@ -129,27 +146,35 @@ final class AssetDownloadManager { } func checkIfAssetIsLocal(_ asset: PHAsset) -> Signal { + if asset.isLocallyAvailable == true { + return .single(true) + } return Signal { subscriber in - let options = PHImageRequestOptions() - options.isNetworkAccessAllowed = false - let requestId: PHImageRequestID - if #available(iOS 13, *) { - requestId = imageManager.requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in - if data != nil { - subscriber.putNext(data != nil) - } + if case .video = asset.mediaType { + let options = PHVideoRequestOptions() + options.isNetworkAccessAllowed = false + + requestId = imageManager.requestAVAsset(forVideo: asset, options: options) { asset, _, _ in + subscriber.putNext(asset != nil) subscriber.putCompletion() } } else { - requestId = imageManager.requestImageData(for: asset, options: options) { data, _, _, _ in - if data != nil { + let options = PHImageRequestOptions() + options.isNetworkAccessAllowed = false + + if #available(iOS 13, *) { + requestId = imageManager.requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in subscriber.putNext(data != nil) + subscriber.putCompletion() + } + } else { + requestId = imageManager.requestImageData(for: asset, options: options) { data, _, _, _ in + subscriber.putNext(data != nil) + subscriber.putCompletion() } - subscriber.putCompletion() } } - return ActionDisposable { imageManager.cancelImageRequest(requestId) } @@ -157,32 +182,57 @@ func checkIfAssetIsLocal(_ asset: PHAsset) -> Signal { } enum AssetDownloadStatus { + case none case progress(Float) case completed } private func downloadAssetMediaData(_ asset: PHAsset) -> Signal { return Signal { subscriber in - let options = PHImageRequestOptions() - options.isNetworkAccessAllowed = true - options.progressHandler = { progress, _, _, _ in - subscriber.putNext(.progress(Float(progress))) - } - let requestId: PHImageRequestID - if #available(iOS 13, *) { - requestId = imageManager.requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in - if data != nil { + if case .video = asset.mediaType { + let options = PHVideoRequestOptions() + options.isNetworkAccessAllowed = true + options.progressHandler = { progress, _, _, _ in + subscriber.putNext(.progress(Float(progress))) + } + + subscriber.putNext(.progress(0.0)) + + requestId = imageManager.requestAVAsset(forVideo: asset, options: options) { asset, _, _ in + if asset != nil { subscriber.putNext(.completed) + } else { + subscriber.putNext(.none) } subscriber.putCompletion() } } else { - requestId = imageManager.requestImageData(for: asset, options: options) { data, _, _, _ in - if data != nil { - subscriber.putNext(.completed) + let options = PHImageRequestOptions() + options.isNetworkAccessAllowed = true + options.progressHandler = { progress, _, _, _ in + subscriber.putNext(.progress(Float(progress))) + } + + subscriber.putNext(.progress(0.0)) + if #available(iOS 13, *) { + requestId = imageManager.requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in + if data != nil { + subscriber.putNext(.completed) + } else { + subscriber.putNext(.none) + } + subscriber.putCompletion() + } + } else { + requestId = imageManager.requestImageData(for: asset, options: options) { data, _, _, _ in + if data != nil { + subscriber.putNext(.completed) + } else { + subscriber.putNext(.none) + } + subscriber.putCompletion() } - subscriber.putCompletion() } } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index ef5bb68a76..c01d1bbda6 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -16,6 +16,7 @@ import InvisibleInkDustNode import ImageBlur import FastBlur import MediaEditor +import RadialStatusNode enum MediaPickerGridItemContent: Equatable { case asset(PHFetchResult, Int) @@ -90,7 +91,9 @@ private let maskImage = generateImage(CGSize(width: 1.0, height: 36.0), opaque: final class MediaPickerGridItemNode: GridItemNode { var currentMediaState: (TGMediaSelectableItem, Int)? - var currentState: (PHFetchResult, Int)? + var currentAssetState: (PHFetchResult, Int)? + var currentAsset: PHAsset? + var currentDraftState: (MediaEditorDraft, Int)? var enableAnimations: Bool = true var stories: Bool = false @@ -103,6 +106,7 @@ final class MediaPickerGridItemNode: GridItemNode { private let typeIconNode: ASImageNode private let durationNode: ImmediateTextNode private let draftNode: ImmediateTextNode + private var statusNode: RadialStatusNode? private let activateAreaNode: AccessibilityAreaNode @@ -112,6 +116,8 @@ final class MediaPickerGridItemNode: GridItemNode { private let spoilerDisposable = MetaDisposable() var spoilerNode: SpoilerOverlayNode? + private let progressDisposable = MetaDisposable() + private var currentIsPreviewing = false var selected: (() -> Void)? @@ -140,7 +146,7 @@ final class MediaPickerGridItemNode: GridItemNode { self.activateAreaNode = AccessibilityAreaNode() self.activateAreaNode.accessibilityTraits = [.image] - + super.init() self.clipsToBounds = true @@ -168,7 +174,7 @@ final class MediaPickerGridItemNode: GridItemNode { var selectableItem: TGMediaSelectableItem? { if let (media, _) = self.currentMediaState { return media - } else if let (fetchResult, index) = self.currentState { + } else if let (fetchResult, index) = self.currentAssetState { return TGMediaAsset(phAsset: fetchResult[index]) } else { return nil @@ -179,7 +185,7 @@ final class MediaPickerGridItemNode: GridItemNode { var tag: Int32? { if let tag = self._cachedTag { return tag - } else if let (fetchResult, index) = self.currentState { + } else if let (fetchResult, index) = self.currentAssetState { let asset = fetchResult.object(at: index) if let localTimestamp = asset.creationDate?.timeIntervalSince1970 { let tag = Month(localTimestamp: Int32(localTimestamp)).packedValue @@ -251,6 +257,32 @@ final class MediaPickerGridItemNode: GridItemNode { self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:)))) } + func updateProgress(_ value: Float?, animated: Bool) { + if let value { + let statusNode: RadialStatusNode + if let current = self.statusNode { + statusNode = current + } else { + statusNode = RadialStatusNode(backgroundNodeColor: UIColor(rgb: 0x000000, alpha: 0.6)) + statusNode.isUserInteractionEnabled = false + self.addSubnode(statusNode) + self.statusNode = statusNode + } + let adjustedProgress = max(0.027, CGFloat(value)) + let state: RadialStatusNodeState = .progress(color: .white, lineWidth: nil, value: adjustedProgress, cancelEnabled: true, animateRotation: true) + statusNode.transitionToState(state) + } else if let statusNode = self.statusNode { + self.statusNode = nil + if animated { + statusNode.transitionToState(.none, animated: true, completion: { [weak statusNode] in + statusNode?.removeFromSupernode() + }) + } else { + statusNode.removeFromSupernode() + } + } + } + func setup(interaction: MediaPickerInteraction, draft: MediaEditorDraft, index: Int, theme: PresentationTheme, selectable: Bool, enableAnimations: Bool, stories: Bool) { self.interaction = interaction self.theme = theme @@ -259,14 +291,18 @@ final class MediaPickerGridItemNode: GridItemNode { self.backgroundColor = theme.list.mediaPlaceholderColor - if self.currentDraftState == nil || self.currentDraftState?.0.path != draft.path || self.currentDraftState!.1 != index || self.currentState != nil { + if self.currentDraftState == nil || self.currentDraftState?.0.path != draft.path || self.currentDraftState!.1 != index || self.currentAssetState != nil { let imageSignal: Signal = .single(draft.thumbnail) self.imageNode.setSignal(imageSignal) self.currentDraftState = (draft, index) - if self.currentState != nil { - self.currentState = nil + if self.currentAssetState != nil { + self.currentAsset = nil + self.currentAssetState = nil self.typeIconNode.removeFromSupernode() + + self.progressDisposable.set(nil) + self.updateProgress(nil, animated: false) } if self.draftNode.supernode == nil { @@ -354,11 +390,30 @@ final class MediaPickerGridItemNode: GridItemNode { self.draftNode.removeFromSupernode() } - if self.currentState == nil || self.currentState!.0 !== fetchResult || self.currentState!.1 != index || self.currentDraftState != nil { - self.backgroundNode.image = nil + if self.currentAssetState == nil || self.currentAssetState!.0 !== fetchResult || self.currentAssetState!.1 != index || self.currentDraftState != nil { let editingContext = interaction.editingState let asset = fetchResult.object(at: index) + if asset.localIdentifier == self.currentAsset?.localIdentifier { + return + } + + self.progressDisposable.set( + (interaction.downloadManager.downloadProgress(identifier: asset.localIdentifier) + |> deliverOnMainQueue).start(next: { [weak self] status in + if let self { + switch status { + case .none, .completed: + self.updateProgress(nil, animated: true) + case let .progress(progress): + self.updateProgress(progress, animated: true) + } + } + }) + ) + + self.backgroundNode.image = nil + if #available(iOS 15.0, *) { self.activateAreaNode.accessibilityLabel = "Photo \(asset.creationDate?.formatted(date: .abbreviated, time: .standard) ?? "")" } @@ -489,7 +544,8 @@ final class MediaPickerGridItemNode: GridItemNode { } } - self.currentState = (fetchResult, index) + self.currentAssetState = (fetchResult, index) + self.currentAsset = asset self.setNeedsLayout() } @@ -554,6 +610,11 @@ final class MediaPickerGridItemNode: GridItemNode { spoilerNode.frame = self.bounds spoilerNode.update(size: self.bounds.size, transition: .immediate) } + + let statusSize = CGSize(width: 40.0, height: 40.0) + if let statusNode = self.statusNode { + statusNode.view.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.bounds.width - statusSize.width) / 2.0), y: floorToScreenPixels((self.bounds.height - statusSize.height) / 2.0)), size: statusSize) + } } func transitionView(snapshot: Bool) -> UIView { @@ -589,10 +650,16 @@ final class MediaPickerGridItemNode: GridItemNode { self.interaction?.openDraft(draft, self.imageNode.image) return } - guard let (fetchResult, index) = self.currentState else { + guard let (fetchResult, index) = self.currentAssetState else { return } - self.interaction?.openMedia(fetchResult, index, self.imageNode.image) + if self.statusNode != nil { + if let asset = self.currentAsset { + self.interaction?.downloadManager.cancel(identifier: asset.localIdentifier) + } + } else { + self.interaction?.openMedia(fetchResult, index, self.imageNode.image) + } } } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 10a54ea68b..3952ce379b 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -25,6 +25,7 @@ import CameraScreen import MediaEditor final class MediaPickerInteraction { + let downloadManager: AssetDownloadManager let openMedia: (PHFetchResult, Int, UIImage?) -> Void let openSelectedMedia: (TGMediaSelectableItem, UIImage?) -> Void let openDraft: (MediaEditorDraft, UIImage?) -> Void @@ -36,7 +37,8 @@ final class MediaPickerInteraction { let editingState: TGMediaEditingContext var hiddenMediaId: String? - init(openMedia: @escaping (PHFetchResult, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, openDraft: @escaping (MediaEditorDraft, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool, Bool) -> Bool, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool, @escaping () -> Void) -> Void, schedule: @escaping () -> Void, dismissInput: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) { + init(downloadManager: AssetDownloadManager, openMedia: @escaping (PHFetchResult, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, openDraft: @escaping (MediaEditorDraft, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool, Bool) -> Bool, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool, @escaping () -> Void) -> Void, schedule: @escaping () -> Void, dismissInput: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) { + self.downloadManager = downloadManager self.openMedia = openMedia self.openSelectedMedia = openSelectedMedia self.openDraft = openDraft @@ -393,6 +395,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.selectionChangedDisposable?.dispose() self.itemsDimensionsUpdatedDisposable?.dispose() self.fastScrollDisposable?.dispose() + self.currentAssetDownloadDisposable.dispose() } override func didLoad() { @@ -777,8 +780,29 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { private weak var currentGalleryController: TGModernGalleryController? - private func requestAssetDownload(_ asset: PHAsset) { - + fileprivate var currentAssetDownloadDisposable = MetaDisposable() + + fileprivate func cancelAssetDownloads() { + guard let downloadManager = self.controller?.downloadManager else { + return + } + self.currentAssetDownloadDisposable.set(nil) + downloadManager.cancelAllDownloads() + } + + fileprivate func requestAssetDownload(asset: PHAsset) { + guard let downloadManager = self.controller?.downloadManager else { + return + } + downloadManager.download(asset: asset) + self.currentAssetDownloadDisposable.set( + (downloadManager.downloadProgress(identifier: asset.localIdentifier) + |> deliverOnMainQueue).start(next: { [weak self] status in + if let self, case .completed = status, let controller = self.controller, let customSelection = self.controller?.customSelection { + customSelection(controller, asset) + } + }) + ) } private var openingMedia = false @@ -794,26 +818,19 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.openingMedia = true let asset = fetchResult[index] - customSelection(controller, asset) - -// let isLocallyAvailable = asset.isLocallyAvailable -// -// if let isLocallyAvailable { -// if isLocallyAvailable { -// customSelection(controller, asset) -// } else { -// self.requestAssetDownload(asset) -// } -// } else { -// let _ = (checkIfAssetIsLocal(asset) -// |> deliverOnMainQueue).start(next: { [weak self] isLocallyAvailable in -// if isLocallyAvailable { -// customSelection(controller, asset) -// } else { -// self?.requestAssetDownload(asset) -// } -// }) -// } + + let _ = (checkIfAssetIsLocal(asset) + |> deliverOnMainQueue).start(next: { [weak self] isLocallyAvailable in + guard let self else { + return + } + if isLocallyAvailable { + self.cancelAssetDownloads() + customSelection(controller, asset) + } else { + self.requestAssetDownload(asset: asset) + } + }) Queue.mainQueue().after(0.3) { self.openingMedia = false @@ -1365,6 +1382,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } private let groupedPromise = ValuePromise(true) + private let downloadManager = AssetDownloadManager() + private var isDismissing = false fileprivate let mainButtonState: AttachmentMainButtonState? @@ -1538,13 +1557,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } } - self.interaction = MediaPickerInteraction(openMedia: { [weak self] fetchResult, index, immediateThumbnail in + self.interaction = MediaPickerInteraction(downloadManager: self.downloadManager, + openMedia: { [weak self] fetchResult, index, immediateThumbnail in self?.controllerNode.openMedia(fetchResult: fetchResult, index: index, immediateThumbnail: immediateThumbnail) - }, openSelectedMedia: { [weak self] item, immediateThumbnail in + }, + openSelectedMedia: { [weak self] item, immediateThumbnail in self?.controllerNode.openSelectedMedia(item: item, immediateThumbnail: immediateThumbnail) - }, openDraft: { [weak self] draft, immediateThumbnail in + }, + openDraft: { [weak self] draft, immediateThumbnail in self?.controllerNode.openDraft(draft: draft, immediateThumbnail: immediateThumbnail) - }, toggleSelection: { [weak self] item, value, suggestUndo in + }, + toggleSelection: { [weak self] item, value, suggestUndo in if let self = self, let selectionState = self.interaction?.selectionState { if let _ = item as? TGMediaPickerGalleryPhotoItem { if self.bannedSendPhotos != nil { @@ -1798,7 +1821,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.undoOverlayController?.dismissWithCommitAction() } - public func requestDismiss(completion: @escaping () -> Void) { + public func requestDismiss(completion: @escaping () -> Void) { if let selectionState = self.interaction?.selectionState, selectionState.count() > 0 { self.isDismissing = true @@ -1837,6 +1860,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.dismiss() } + public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + self.controllerNode.cancelAssetDownloads() + + super.dismiss(animated: flag, completion: completion) + } + @objc private func rightButtonPressed() { self.moreButtonNode.buttonPressed() } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift index 445370d041..b681db18d7 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift @@ -284,6 +284,7 @@ final class PendingStoryManager { self.currentPendingItemContext = pendingItemContext let stableId = firstItem.stableId + Logger.shared.log("PendingStoryManager", "setting up item context for: \(firstItem.stableId) randomId: \(firstItem.randomId)") pendingItemContext.disposable = (_internal_uploadStoryImpl(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, stateManager: self.stateManager, messageMediaPreuploadManager: self.messageMediaPreuploadManager, revalidationContext: self.revalidationContext, auxiliaryMethods: self.auxiliaryMethods, stableId: stableId, media: firstItem.media, text: firstItem.text, entities: firstItem.entities, embeddedStickers: firstItem.embeddedStickers, pin: firstItem.pin, privacy: firstItem.privacy, isForwardingDisabled: firstItem.isForwardingDisabled, period: Int(firstItem.period), randomId: firstItem.randomId) |> deliverOn(self.queue)).start(next: { [weak self] event in guard let `self` = self else { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index 60bfed09a9..0803c51456 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -746,6 +746,7 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text: period: Int32(period), randomId: randomId )) + Logger.shared.log("UploadStory", "Appended new pending item stableId: \(stableId) randomId: \(randomId)") transaction.setLocalStoryState(state: CodableEntry(currentState)) }).start() } @@ -789,6 +790,7 @@ private func _internal_putPendingStoryIdMapping(accountPeerId: PeerId, stableId: } func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId: PeerId, stateManager: AccountStateManager, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, stableId: Int32, media: Media, text: String, entities: [MessageTextEntity], embeddedStickers: [TelegramMediaFile], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal { + Logger.shared.log("UploadStory", "uploadStoryImpl for stableId: \(stableId) randomId: \(randomId)") let passFetchProgress = media is TelegramMediaFile let (contentSignal, originalMedia) = uploadedStoryContent(postbox: postbox, network: network, media: media, embeddedStickers: embeddedStickers, accountPeerId: accountPeerId, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, auxiliaryMethods: auxiliaryMethods, passFetchProgress: passFetchProgress) return contentSignal diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 6068c562d0..794c373982 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -303,6 +303,8 @@ private final class CameraScreenComponent: CombinedComponent { self.isPressingButton = true } self.buttonPressTimestamp = nil + self.buttonPressTimer?.invalidate() + self.buttonPressTimer = nil } }, queue: Queue.mainQueue()) self.buttonPressTimer?.start() @@ -641,11 +643,15 @@ private final class CameraScreenComponent: CombinedComponent { } controller.presentGallery() }, - swipeHintUpdated: { hint in - state.updateSwipeHint(hint) + swipeHintUpdated: { [weak state] hint in + if let state { + state.updateSwipeHint(hint) + } }, - zoomUpdated: { fraction in - state.updateZoom(fraction: fraction) + zoomUpdated: { [weak state] fraction in + if let state { + state.updateZoom(fraction: fraction) + } }, flipAnimationAction: animateFlipAction ), @@ -737,10 +743,9 @@ private final class CameraScreenComponent: CombinedComponent { component: CameraButton( content: flashContentComponent, action: { [weak state] in - guard let state else { - return + if let state { + state.toggleFlashMode() } - state.toggleFlashMode() } ).tagged(flashButtonTag), availableSize: CGSize(width: 40.0, height: 40.0), @@ -762,10 +767,9 @@ private final class CameraScreenComponent: CombinedComponent { ) ), action: { [weak state] in - guard let state else { - return + if let state { + state.toggleDualCamera() } - state.toggleDualCamera() } ).tagged(dualButtonTag), availableSize: CGSize(width: 40.0, height: 40.0), diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift index 808f100f2c..727aaaddb5 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift @@ -507,7 +507,9 @@ public final class MediaEditor { self.onPlaybackAction(.seek(start)) self.player?.play() self.additionalPlayer?.play() - self.onPlaybackAction(.play) + Queue.mainQueue().justDispatch { + self.onPlaybackAction(.play) + } } }) Queue.mainQueue().justDispatch { diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 85edd44d91..1a9dbaf97b 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3615,6 +3615,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } let fittedSize = resultImage.size.aspectFitted(CGSize(width: 128.0, height: 128.0)) + let context = self.context let saveImageDraft: (UIImage, PixelDimensions) -> Void = { image, dimensions in if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) { let path = "\(Int64.random(in: .min ... .max)).jpg" @@ -3622,9 +3623,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, duration: nil, values: values, caption: caption, privacy: privacy, timestamp: timestamp) try? data.write(to: URL(fileURLWithPath: draft.fullPath())) if let id { - saveStorySource(engine: self.context.engine, item: draft, id: id) + saveStorySource(engine: context.engine, item: draft, id: id) } else { - addStoryDraft(engine: self.context.engine, item: draft) + addStoryDraft(engine: context.engine, item: draft) } } } @@ -3636,9 +3637,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, duration: duration, values: values, caption: caption, privacy: privacy, timestamp: timestamp) try? FileManager.default.moveItem(atPath: videoPath, toPath: draft.fullPath()) if let id { - saveStorySource(engine: self.context.engine, item: draft, id: id) + saveStorySource(engine: context.engine, item: draft, id: id) } else { - addStoryDraft(engine: self.context.engine, item: draft) + addStoryDraft(engine: context.engine, item: draft) } } } @@ -3861,6 +3862,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let self { makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image ?? UIImage(), dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] coverImage in if let self { + Logger.shared.log("Media Editor", "completed with video \(videoResult)") self.completion(randomId, .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), caption, self.state.privacy, stickers, { [weak self] finished in self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in self?.dismiss() @@ -3883,6 +3885,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] resultImage in if let self, let resultImage { + Logger.shared.log("Media Editor", "completed with image \(resultImage)") self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), caption, self.state.privacy, stickers, { [weak self] finished in self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in self?.dismiss() diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index bde99851eb..2784378cb8 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -374,6 +374,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon case let .image(image, dimensions): if let imageData = compressImageToJPEG(image, quality: 0.7) { let entities = generateChatInputTextEntities(caption) + Logger.shared.log("MediaEditor", "Calling uploadStory for image, randomId \(randomId)") self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData, stickers: stickers), text: caption.string, entities: entities, pin: privacy.pin, privacy: privacy.privacy, isForwardingDisabled: privacy.isForwardingDisabled, period: privacy.timeout, randomId: randomId) Queue.mainQueue().justDispatch { commit({}) @@ -404,7 +405,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon return nil } } - + Logger.shared.log("MediaEditor", "Calling uploadStory for video, randomId \(randomId)") let entities = generateChatInputTextEntities(caption) self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: stickers), text: caption.string, entities: entities, pin: privacy.pin, privacy: privacy.privacy, isForwardingDisabled: privacy.isForwardingDisabled, period: privacy.timeout, randomId: randomId) Queue.mainQueue().justDispatch {