mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
148947a0e9
commit
7b15dbe31c
@ -366,17 +366,17 @@ private final class CameraContext {
|
|||||||
|> map { first, second in
|
|> map { first, second in
|
||||||
return first && second
|
return first && second
|
||||||
}
|
}
|
||||||
|> filter { $0 }
|
|> filter { $0 }
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> delay(0.1, queue: self.queue)
|
|> delay(0.1, queue: self.queue)
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] _ in
|
|> deliverOn(self.queue)).start(next: { [weak self] _ in
|
||||||
self?.modeChange = .none
|
self?.modeChange = .none
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let _ = (previewView.isPreviewing
|
let _ = (previewView.isPreviewing
|
||||||
|> filter { $0 }
|
|> filter { $0 }
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] _ in
|
|> deliverOn(self.queue)).start(next: { [weak self] _ in
|
||||||
self?.modeChange = .none
|
self?.modeChange = .none
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,6 @@ final class CameraOutput: NSObject {
|
|||||||
let videoOutput = AVCaptureVideoDataOutput()
|
let videoOutput = AVCaptureVideoDataOutput()
|
||||||
let audioOutput = AVCaptureAudioDataOutput()
|
let audioOutput = AVCaptureAudioDataOutput()
|
||||||
let metadataOutput = AVCaptureMetadataOutput()
|
let metadataOutput = AVCaptureMetadataOutput()
|
||||||
private let faceLandmarksOutput = FaceLandmarksDataOutput()
|
|
||||||
|
|
||||||
let exclusive: Bool
|
let exclusive: Bool
|
||||||
|
|
||||||
@ -85,17 +84,12 @@ final class CameraOutput: NSObject {
|
|||||||
|
|
||||||
private let queue = DispatchQueue(label: "")
|
private let queue = DispatchQueue(label: "")
|
||||||
private let metadataQueue = DispatchQueue(label: "")
|
private let metadataQueue = DispatchQueue(label: "")
|
||||||
private let faceLandmarksQueue = DispatchQueue(label: "")
|
|
||||||
|
|
||||||
private var photoCaptureRequests: [Int64: PhotoCaptureContext] = [:]
|
private var photoCaptureRequests: [Int64: PhotoCaptureContext] = [:]
|
||||||
private var videoRecorder: VideoRecorder?
|
private var videoRecorder: VideoRecorder?
|
||||||
|
|
||||||
var activeFilter: CameraFilter?
|
|
||||||
var faceLandmarks: Bool = false
|
|
||||||
|
|
||||||
var processSampleBuffer: ((CMSampleBuffer, CVImageBuffer, AVCaptureConnection) -> Void)?
|
var processSampleBuffer: ((CMSampleBuffer, CVImageBuffer, AVCaptureConnection) -> Void)?
|
||||||
var processCodes: (([CameraCode]) -> Void)?
|
var processCodes: (([CameraCode]) -> Void)?
|
||||||
var processFaceLandmarks: (([VNFaceObservation]) -> Void)?
|
|
||||||
|
|
||||||
init(exclusive: Bool) {
|
init(exclusive: Bool) {
|
||||||
self.exclusive = exclusive
|
self.exclusive = exclusive
|
||||||
@ -104,12 +98,6 @@ final class CameraOutput: NSObject {
|
|||||||
|
|
||||||
self.videoOutput.alwaysDiscardsLateVideoFrames = false
|
self.videoOutput.alwaysDiscardsLateVideoFrames = false
|
||||||
self.videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] as [String : Any]
|
self.videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] as [String : Any]
|
||||||
|
|
||||||
self.faceLandmarksOutput.outputFaceObservations = { [weak self] observations in
|
|
||||||
if let self {
|
|
||||||
self.processFaceLandmarks?(observations)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -273,7 +261,7 @@ final class CameraOutput: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let uniqueId = settings.uniqueID
|
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.photoCaptureRequests[uniqueId] = photoCapture
|
||||||
self.photoOutput.capturePhoto(with: settings, delegate: photoCapture)
|
self.photoOutput.capturePhoto(with: settings, delegate: photoCapture)
|
||||||
|
|
||||||
@ -361,32 +349,10 @@ extension CameraOutput: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureA
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.faceLandmarks {
|
|
||||||
self.faceLandmarksQueue.async {
|
|
||||||
self.faceLandmarksOutput.process(sampleBuffer: sampleBuffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let videoPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
|
if let videoPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
|
||||||
self.processSampleBuffer?(sampleBuffer, videoPixelBuffer, connection)
|
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 {
|
if let videoRecorder = self.videoRecorder, videoRecorder.isRecording {
|
||||||
videoRecorder.appendSampleBuffer(sampleBuffer)
|
videoRecorder.appendSampleBuffer(sampleBuffer)
|
||||||
}
|
}
|
||||||
|
@ -2975,6 +2975,8 @@ public final class DrawingToolsInteraction {
|
|||||||
private var isActive = false
|
private var isActive = false
|
||||||
private var validLayout: ContainerViewLayout?
|
private var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
|
private let startTimestamp = CACurrentMediaTime()
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
drawingView: DrawingView,
|
drawingView: DrawingView,
|
||||||
@ -3126,6 +3128,10 @@ public final class DrawingToolsInteraction {
|
|||||||
if let textEntityView = entityView as? DrawingTextEntityView {
|
if let textEntityView = entityView as? DrawingTextEntityView {
|
||||||
textEntityView.beginEditing(accessoryView: self.textEditAccessoryView)
|
textEntityView.beginEditing(accessoryView: self.textEditAccessoryView)
|
||||||
} else {
|
} else {
|
||||||
|
if self.isVideo {
|
||||||
|
entityView.seek(to: 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
entityView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
entityView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
entityView.layer.animateScale(from: 0.1, to: entity.scale, duration: 0.2)
|
entityView.layer.animateScale(from: 0.1, to: entity.scale, duration: 0.2)
|
||||||
|
|
||||||
|
@ -43,7 +43,8 @@ swift_library(
|
|||||||
"//submodules/MoreButtonNode:MoreButtonNode",
|
"//submodules/MoreButtonNode:MoreButtonNode",
|
||||||
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
||||||
"//submodules/TelegramUI/Components/CameraScreen",
|
"//submodules/TelegramUI/Components/CameraScreen",
|
||||||
"//submodules/TelegramUI/Components/MediaEditor",
|
"//submodules/TelegramUI/Components/MediaEditor",
|
||||||
|
"//submodules/RadialStatusNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -17,7 +17,7 @@ final class AssetDownloadManager {
|
|||||||
let identifier: String
|
let identifier: String
|
||||||
let updated: () -> Void
|
let updated: () -> Void
|
||||||
|
|
||||||
var status: AssetDownloadStatus = .progress(0.0)
|
var status: AssetDownloadStatus = .none
|
||||||
var disposable: Disposable?
|
var disposable: Disposable?
|
||||||
|
|
||||||
init(identifier: String, updated: @escaping () -> Void) {
|
init(identifier: String, updated: @escaping () -> Void) {
|
||||||
@ -40,9 +40,7 @@ final class AssetDownloadManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func download(asset: PHAsset) {
|
func download(asset: PHAsset) {
|
||||||
if let currentAssetContext = self.currentAssetContext {
|
self.cancelAllDownloads()
|
||||||
currentAssetContext.disposable?.dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
let identifier = asset.localIdentifier
|
let identifier = asset.localIdentifier
|
||||||
@ -59,6 +57,7 @@ final class AssetDownloadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
self.currentAssetContext = assetContext
|
||||||
assetContext.disposable = (downloadAssetMediaData(asset)
|
assetContext.disposable = (downloadAssetMediaData(asset)
|
||||||
|> deliverOn(queue)).start(next: { [weak self] status in
|
|> deliverOn(queue)).start(next: { [weak self] status in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -69,13 +68,31 @@ final class AssetDownloadManager {
|
|||||||
currentAssetContext.updated()
|
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) {
|
func cancel(identifier: String) {
|
||||||
if let currentAssetContext = self.currentAssetContext, currentAssetContext.identifier == identifier {
|
if let currentAssetContext = self.currentAssetContext, currentAssetContext.identifier == identifier {
|
||||||
|
currentAssetContext.status = .none
|
||||||
|
currentAssetContext.updated()
|
||||||
currentAssetContext.disposable?.dispose()
|
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 {
|
if let currentAssetContext = self.currentAssetContext, currentAssetContext.identifier == identifier {
|
||||||
next(currentAssetContext.status)
|
next(currentAssetContext.status)
|
||||||
} else {
|
} else {
|
||||||
next(.progress(0.0))
|
next(.none)
|
||||||
}
|
}
|
||||||
|
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
@ -129,27 +146,35 @@ final class AssetDownloadManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkIfAssetIsLocal(_ asset: PHAsset) -> Signal<Bool, NoError> {
|
func checkIfAssetIsLocal(_ asset: PHAsset) -> Signal<Bool, NoError> {
|
||||||
|
if asset.isLocallyAvailable == true {
|
||||||
|
return .single(true)
|
||||||
|
}
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let options = PHImageRequestOptions()
|
|
||||||
options.isNetworkAccessAllowed = false
|
|
||||||
|
|
||||||
let requestId: PHImageRequestID
|
let requestId: PHImageRequestID
|
||||||
if #available(iOS 13, *) {
|
if case .video = asset.mediaType {
|
||||||
requestId = imageManager.requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in
|
let options = PHVideoRequestOptions()
|
||||||
if data != nil {
|
options.isNetworkAccessAllowed = false
|
||||||
subscriber.putNext(data != nil)
|
|
||||||
}
|
requestId = imageManager.requestAVAsset(forVideo: asset, options: options) { asset, _, _ in
|
||||||
|
subscriber.putNext(asset != nil)
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
requestId = imageManager.requestImageData(for: asset, options: options) { data, _, _, _ in
|
let options = PHImageRequestOptions()
|
||||||
if data != nil {
|
options.isNetworkAccessAllowed = false
|
||||||
|
|
||||||
|
if #available(iOS 13, *) {
|
||||||
|
requestId = imageManager.requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in
|
||||||
subscriber.putNext(data != nil)
|
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 {
|
return ActionDisposable {
|
||||||
imageManager.cancelImageRequest(requestId)
|
imageManager.cancelImageRequest(requestId)
|
||||||
}
|
}
|
||||||
@ -157,32 +182,57 @@ func checkIfAssetIsLocal(_ asset: PHAsset) -> Signal<Bool, NoError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum AssetDownloadStatus {
|
enum AssetDownloadStatus {
|
||||||
|
case none
|
||||||
case progress(Float)
|
case progress(Float)
|
||||||
case completed
|
case completed
|
||||||
}
|
}
|
||||||
|
|
||||||
private func downloadAssetMediaData(_ asset: PHAsset) -> Signal<AssetDownloadStatus, NoError> {
|
private func downloadAssetMediaData(_ asset: PHAsset) -> Signal<AssetDownloadStatus, NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let options = PHImageRequestOptions()
|
|
||||||
options.isNetworkAccessAllowed = true
|
|
||||||
options.progressHandler = { progress, _, _, _ in
|
|
||||||
subscriber.putNext(.progress(Float(progress)))
|
|
||||||
}
|
|
||||||
|
|
||||||
let requestId: PHImageRequestID
|
let requestId: PHImageRequestID
|
||||||
if #available(iOS 13, *) {
|
if case .video = asset.mediaType {
|
||||||
requestId = imageManager.requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in
|
let options = PHVideoRequestOptions()
|
||||||
if data != nil {
|
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)
|
subscriber.putNext(.completed)
|
||||||
|
} else {
|
||||||
|
subscriber.putNext(.none)
|
||||||
}
|
}
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
requestId = imageManager.requestImageData(for: asset, options: options) { data, _, _, _ in
|
let options = PHImageRequestOptions()
|
||||||
if data != nil {
|
options.isNetworkAccessAllowed = true
|
||||||
subscriber.putNext(.completed)
|
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import InvisibleInkDustNode
|
|||||||
import ImageBlur
|
import ImageBlur
|
||||||
import FastBlur
|
import FastBlur
|
||||||
import MediaEditor
|
import MediaEditor
|
||||||
|
import RadialStatusNode
|
||||||
|
|
||||||
enum MediaPickerGridItemContent: Equatable {
|
enum MediaPickerGridItemContent: Equatable {
|
||||||
case asset(PHFetchResult<PHAsset>, Int)
|
case asset(PHFetchResult<PHAsset>, Int)
|
||||||
@ -90,7 +91,9 @@ private let maskImage = generateImage(CGSize(width: 1.0, height: 36.0), opaque:
|
|||||||
|
|
||||||
final class MediaPickerGridItemNode: GridItemNode {
|
final class MediaPickerGridItemNode: GridItemNode {
|
||||||
var currentMediaState: (TGMediaSelectableItem, Int)?
|
var currentMediaState: (TGMediaSelectableItem, Int)?
|
||||||
var currentState: (PHFetchResult<PHAsset>, Int)?
|
var currentAssetState: (PHFetchResult<PHAsset>, Int)?
|
||||||
|
var currentAsset: PHAsset?
|
||||||
|
|
||||||
var currentDraftState: (MediaEditorDraft, Int)?
|
var currentDraftState: (MediaEditorDraft, Int)?
|
||||||
var enableAnimations: Bool = true
|
var enableAnimations: Bool = true
|
||||||
var stories: Bool = false
|
var stories: Bool = false
|
||||||
@ -103,6 +106,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
private let typeIconNode: ASImageNode
|
private let typeIconNode: ASImageNode
|
||||||
private let durationNode: ImmediateTextNode
|
private let durationNode: ImmediateTextNode
|
||||||
private let draftNode: ImmediateTextNode
|
private let draftNode: ImmediateTextNode
|
||||||
|
private var statusNode: RadialStatusNode?
|
||||||
|
|
||||||
private let activateAreaNode: AccessibilityAreaNode
|
private let activateAreaNode: AccessibilityAreaNode
|
||||||
|
|
||||||
@ -112,6 +116,8 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
private let spoilerDisposable = MetaDisposable()
|
private let spoilerDisposable = MetaDisposable()
|
||||||
var spoilerNode: SpoilerOverlayNode?
|
var spoilerNode: SpoilerOverlayNode?
|
||||||
|
|
||||||
|
private let progressDisposable = MetaDisposable()
|
||||||
|
|
||||||
private var currentIsPreviewing = false
|
private var currentIsPreviewing = false
|
||||||
|
|
||||||
var selected: (() -> Void)?
|
var selected: (() -> Void)?
|
||||||
@ -140,7 +146,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
self.activateAreaNode = AccessibilityAreaNode()
|
self.activateAreaNode = AccessibilityAreaNode()
|
||||||
self.activateAreaNode.accessibilityTraits = [.image]
|
self.activateAreaNode.accessibilityTraits = [.image]
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.clipsToBounds = true
|
self.clipsToBounds = true
|
||||||
@ -168,7 +174,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
var selectableItem: TGMediaSelectableItem? {
|
var selectableItem: TGMediaSelectableItem? {
|
||||||
if let (media, _) = self.currentMediaState {
|
if let (media, _) = self.currentMediaState {
|
||||||
return media
|
return media
|
||||||
} else if let (fetchResult, index) = self.currentState {
|
} else if let (fetchResult, index) = self.currentAssetState {
|
||||||
return TGMediaAsset(phAsset: fetchResult[index])
|
return TGMediaAsset(phAsset: fetchResult[index])
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
@ -179,7 +185,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
var tag: Int32? {
|
var tag: Int32? {
|
||||||
if let tag = self._cachedTag {
|
if let tag = self._cachedTag {
|
||||||
return tag
|
return tag
|
||||||
} else if let (fetchResult, index) = self.currentState {
|
} else if let (fetchResult, index) = self.currentAssetState {
|
||||||
let asset = fetchResult.object(at: index)
|
let asset = fetchResult.object(at: index)
|
||||||
if let localTimestamp = asset.creationDate?.timeIntervalSince1970 {
|
if let localTimestamp = asset.creationDate?.timeIntervalSince1970 {
|
||||||
let tag = Month(localTimestamp: Int32(localTimestamp)).packedValue
|
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(_:))))
|
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) {
|
func setup(interaction: MediaPickerInteraction, draft: MediaEditorDraft, index: Int, theme: PresentationTheme, selectable: Bool, enableAnimations: Bool, stories: Bool) {
|
||||||
self.interaction = interaction
|
self.interaction = interaction
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
@ -259,14 +291,18 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
self.backgroundColor = theme.list.mediaPlaceholderColor
|
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<UIImage?, NoError> = .single(draft.thumbnail)
|
let imageSignal: Signal<UIImage?, NoError> = .single(draft.thumbnail)
|
||||||
self.imageNode.setSignal(imageSignal)
|
self.imageNode.setSignal(imageSignal)
|
||||||
|
|
||||||
self.currentDraftState = (draft, index)
|
self.currentDraftState = (draft, index)
|
||||||
if self.currentState != nil {
|
if self.currentAssetState != nil {
|
||||||
self.currentState = nil
|
self.currentAsset = nil
|
||||||
|
self.currentAssetState = nil
|
||||||
self.typeIconNode.removeFromSupernode()
|
self.typeIconNode.removeFromSupernode()
|
||||||
|
|
||||||
|
self.progressDisposable.set(nil)
|
||||||
|
self.updateProgress(nil, animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.draftNode.supernode == nil {
|
if self.draftNode.supernode == nil {
|
||||||
@ -354,11 +390,30 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
self.draftNode.removeFromSupernode()
|
self.draftNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.currentState == nil || self.currentState!.0 !== fetchResult || self.currentState!.1 != index || self.currentDraftState != nil {
|
if self.currentAssetState == nil || self.currentAssetState!.0 !== fetchResult || self.currentAssetState!.1 != index || self.currentDraftState != nil {
|
||||||
self.backgroundNode.image = nil
|
|
||||||
let editingContext = interaction.editingState
|
let editingContext = interaction.editingState
|
||||||
let asset = fetchResult.object(at: index)
|
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, *) {
|
if #available(iOS 15.0, *) {
|
||||||
self.activateAreaNode.accessibilityLabel = "Photo \(asset.creationDate?.formatted(date: .abbreviated, time: .standard) ?? "")"
|
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()
|
self.setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,6 +610,11 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
spoilerNode.frame = self.bounds
|
spoilerNode.frame = self.bounds
|
||||||
spoilerNode.update(size: self.bounds.size, transition: .immediate)
|
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 {
|
func transitionView(snapshot: Bool) -> UIView {
|
||||||
@ -589,10 +650,16 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
self.interaction?.openDraft(draft, self.imageNode.image)
|
self.interaction?.openDraft(draft, self.imageNode.image)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let (fetchResult, index) = self.currentState else {
|
guard let (fetchResult, index) = self.currentAssetState else {
|
||||||
return
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import CameraScreen
|
|||||||
import MediaEditor
|
import MediaEditor
|
||||||
|
|
||||||
final class MediaPickerInteraction {
|
final class MediaPickerInteraction {
|
||||||
|
let downloadManager: AssetDownloadManager
|
||||||
let openMedia: (PHFetchResult<PHAsset>, Int, UIImage?) -> Void
|
let openMedia: (PHFetchResult<PHAsset>, Int, UIImage?) -> Void
|
||||||
let openSelectedMedia: (TGMediaSelectableItem, UIImage?) -> Void
|
let openSelectedMedia: (TGMediaSelectableItem, UIImage?) -> Void
|
||||||
let openDraft: (MediaEditorDraft, UIImage?) -> Void
|
let openDraft: (MediaEditorDraft, UIImage?) -> Void
|
||||||
@ -36,7 +37,8 @@ final class MediaPickerInteraction {
|
|||||||
let editingState: TGMediaEditingContext
|
let editingState: TGMediaEditingContext
|
||||||
var hiddenMediaId: String?
|
var hiddenMediaId: String?
|
||||||
|
|
||||||
init(openMedia: @escaping (PHFetchResult<PHAsset>, 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<PHAsset>, 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.openMedia = openMedia
|
||||||
self.openSelectedMedia = openSelectedMedia
|
self.openSelectedMedia = openSelectedMedia
|
||||||
self.openDraft = openDraft
|
self.openDraft = openDraft
|
||||||
@ -393,6 +395,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.selectionChangedDisposable?.dispose()
|
self.selectionChangedDisposable?.dispose()
|
||||||
self.itemsDimensionsUpdatedDisposable?.dispose()
|
self.itemsDimensionsUpdatedDisposable?.dispose()
|
||||||
self.fastScrollDisposable?.dispose()
|
self.fastScrollDisposable?.dispose()
|
||||||
|
self.currentAssetDownloadDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
@ -777,8 +780,29 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
|
|
||||||
private weak var currentGalleryController: TGModernGalleryController?
|
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
|
private var openingMedia = false
|
||||||
@ -794,26 +818,19 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.openingMedia = true
|
self.openingMedia = true
|
||||||
|
|
||||||
let asset = fetchResult[index]
|
let asset = fetchResult[index]
|
||||||
customSelection(controller, asset)
|
|
||||||
|
let _ = (checkIfAssetIsLocal(asset)
|
||||||
// let isLocallyAvailable = asset.isLocallyAvailable
|
|> deliverOnMainQueue).start(next: { [weak self] isLocallyAvailable in
|
||||||
//
|
guard let self else {
|
||||||
// if let isLocallyAvailable {
|
return
|
||||||
// if isLocallyAvailable {
|
}
|
||||||
// customSelection(controller, asset)
|
if isLocallyAvailable {
|
||||||
// } else {
|
self.cancelAssetDownloads()
|
||||||
// self.requestAssetDownload(asset)
|
customSelection(controller, asset)
|
||||||
// }
|
} else {
|
||||||
// } else {
|
self.requestAssetDownload(asset: asset)
|
||||||
// let _ = (checkIfAssetIsLocal(asset)
|
}
|
||||||
// |> deliverOnMainQueue).start(next: { [weak self] isLocallyAvailable in
|
})
|
||||||
// if isLocallyAvailable {
|
|
||||||
// customSelection(controller, asset)
|
|
||||||
// } else {
|
|
||||||
// self?.requestAssetDownload(asset)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
Queue.mainQueue().after(0.3) {
|
Queue.mainQueue().after(0.3) {
|
||||||
self.openingMedia = false
|
self.openingMedia = false
|
||||||
@ -1365,6 +1382,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
private let groupedPromise = ValuePromise<Bool>(true)
|
private let groupedPromise = ValuePromise<Bool>(true)
|
||||||
|
|
||||||
|
private let downloadManager = AssetDownloadManager()
|
||||||
|
|
||||||
private var isDismissing = false
|
private var isDismissing = false
|
||||||
|
|
||||||
fileprivate let mainButtonState: AttachmentMainButtonState?
|
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)
|
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)
|
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)
|
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 self = self, let selectionState = self.interaction?.selectionState {
|
||||||
if let _ = item as? TGMediaPickerGalleryPhotoItem {
|
if let _ = item as? TGMediaPickerGalleryPhotoItem {
|
||||||
if self.bannedSendPhotos != nil {
|
if self.bannedSendPhotos != nil {
|
||||||
@ -1798,7 +1821,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.undoOverlayController?.dismissWithCommitAction()
|
self.undoOverlayController?.dismissWithCommitAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func requestDismiss(completion: @escaping () -> Void) {
|
public func requestDismiss(completion: @escaping () -> Void) {
|
||||||
if let selectionState = self.interaction?.selectionState, selectionState.count() > 0 {
|
if let selectionState = self.interaction?.selectionState, selectionState.count() > 0 {
|
||||||
self.isDismissing = true
|
self.isDismissing = true
|
||||||
|
|
||||||
@ -1837,6 +1860,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.dismiss()
|
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() {
|
@objc private func rightButtonPressed() {
|
||||||
self.moreButtonNode.buttonPressed()
|
self.moreButtonNode.buttonPressed()
|
||||||
}
|
}
|
||||||
|
@ -284,6 +284,7 @@ final class PendingStoryManager {
|
|||||||
self.currentPendingItemContext = pendingItemContext
|
self.currentPendingItemContext = pendingItemContext
|
||||||
|
|
||||||
let stableId = firstItem.stableId
|
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)
|
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
|
|> deliverOn(self.queue)).start(next: { [weak self] event in
|
||||||
guard let `self` = self else {
|
guard let `self` = self else {
|
||||||
|
@ -746,6 +746,7 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text:
|
|||||||
period: Int32(period),
|
period: Int32(period),
|
||||||
randomId: randomId
|
randomId: randomId
|
||||||
))
|
))
|
||||||
|
Logger.shared.log("UploadStory", "Appended new pending item stableId: \(stableId) randomId: \(randomId)")
|
||||||
transaction.setLocalStoryState(state: CodableEntry(currentState))
|
transaction.setLocalStoryState(state: CodableEntry(currentState))
|
||||||
}).start()
|
}).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<StoryUploadResult, NoError> {
|
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<StoryUploadResult, NoError> {
|
||||||
|
Logger.shared.log("UploadStory", "uploadStoryImpl for stableId: \(stableId) randomId: \(randomId)")
|
||||||
let passFetchProgress = media is TelegramMediaFile
|
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)
|
let (contentSignal, originalMedia) = uploadedStoryContent(postbox: postbox, network: network, media: media, embeddedStickers: embeddedStickers, accountPeerId: accountPeerId, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, auxiliaryMethods: auxiliaryMethods, passFetchProgress: passFetchProgress)
|
||||||
return contentSignal
|
return contentSignal
|
||||||
|
@ -303,6 +303,8 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
self.isPressingButton = true
|
self.isPressingButton = true
|
||||||
}
|
}
|
||||||
self.buttonPressTimestamp = nil
|
self.buttonPressTimestamp = nil
|
||||||
|
self.buttonPressTimer?.invalidate()
|
||||||
|
self.buttonPressTimer = nil
|
||||||
}
|
}
|
||||||
}, queue: Queue.mainQueue())
|
}, queue: Queue.mainQueue())
|
||||||
self.buttonPressTimer?.start()
|
self.buttonPressTimer?.start()
|
||||||
@ -641,11 +643,15 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
controller.presentGallery()
|
controller.presentGallery()
|
||||||
},
|
},
|
||||||
swipeHintUpdated: { hint in
|
swipeHintUpdated: { [weak state] hint in
|
||||||
state.updateSwipeHint(hint)
|
if let state {
|
||||||
|
state.updateSwipeHint(hint)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
zoomUpdated: { fraction in
|
zoomUpdated: { [weak state] fraction in
|
||||||
state.updateZoom(fraction: fraction)
|
if let state {
|
||||||
|
state.updateZoom(fraction: fraction)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
flipAnimationAction: animateFlipAction
|
flipAnimationAction: animateFlipAction
|
||||||
),
|
),
|
||||||
@ -737,10 +743,9 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
component: CameraButton(
|
component: CameraButton(
|
||||||
content: flashContentComponent,
|
content: flashContentComponent,
|
||||||
action: { [weak state] in
|
action: { [weak state] in
|
||||||
guard let state else {
|
if let state {
|
||||||
return
|
state.toggleFlashMode()
|
||||||
}
|
}
|
||||||
state.toggleFlashMode()
|
|
||||||
}
|
}
|
||||||
).tagged(flashButtonTag),
|
).tagged(flashButtonTag),
|
||||||
availableSize: CGSize(width: 40.0, height: 40.0),
|
availableSize: CGSize(width: 40.0, height: 40.0),
|
||||||
@ -762,10 +767,9 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
action: { [weak state] in
|
action: { [weak state] in
|
||||||
guard let state else {
|
if let state {
|
||||||
return
|
state.toggleDualCamera()
|
||||||
}
|
}
|
||||||
state.toggleDualCamera()
|
|
||||||
}
|
}
|
||||||
).tagged(dualButtonTag),
|
).tagged(dualButtonTag),
|
||||||
availableSize: CGSize(width: 40.0, height: 40.0),
|
availableSize: CGSize(width: 40.0, height: 40.0),
|
||||||
|
@ -507,7 +507,9 @@ public final class MediaEditor {
|
|||||||
self.onPlaybackAction(.seek(start))
|
self.onPlaybackAction(.seek(start))
|
||||||
self.player?.play()
|
self.player?.play()
|
||||||
self.additionalPlayer?.play()
|
self.additionalPlayer?.play()
|
||||||
self.onPlaybackAction(.play)
|
Queue.mainQueue().justDispatch {
|
||||||
|
self.onPlaybackAction(.play)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
|
@ -3615,6 +3615,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
let fittedSize = resultImage.size.aspectFitted(CGSize(width: 128.0, height: 128.0))
|
let fittedSize = resultImage.size.aspectFitted(CGSize(width: 128.0, height: 128.0))
|
||||||
|
|
||||||
|
let context = self.context
|
||||||
let saveImageDraft: (UIImage, PixelDimensions) -> Void = { image, dimensions in
|
let saveImageDraft: (UIImage, PixelDimensions) -> Void = { image, dimensions in
|
||||||
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
||||||
let path = "\(Int64.random(in: .min ... .max)).jpg"
|
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)
|
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()))
|
try? data.write(to: URL(fileURLWithPath: draft.fullPath()))
|
||||||
if let id {
|
if let id {
|
||||||
saveStorySource(engine: self.context.engine, item: draft, id: id)
|
saveStorySource(engine: context.engine, item: draft, id: id)
|
||||||
} else {
|
} 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)
|
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())
|
try? FileManager.default.moveItem(atPath: videoPath, toPath: draft.fullPath())
|
||||||
if let id {
|
if let id {
|
||||||
saveStorySource(engine: self.context.engine, item: draft, id: id)
|
saveStorySource(engine: context.engine, item: draft, id: id)
|
||||||
} else {
|
} 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 {
|
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
|
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 {
|
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.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?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||||
self?.dismiss()
|
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
|
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 {
|
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.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?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
|
@ -374,6 +374,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
case let .image(image, dimensions):
|
case let .image(image, dimensions):
|
||||||
if let imageData = compressImageToJPEG(image, quality: 0.7) {
|
if let imageData = compressImageToJPEG(image, quality: 0.7) {
|
||||||
let entities = generateChatInputTextEntities(caption)
|
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)
|
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 {
|
Queue.mainQueue().justDispatch {
|
||||||
commit({})
|
commit({})
|
||||||
@ -404,7 +405,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Logger.shared.log("MediaEditor", "Calling uploadStory for video, randomId \(randomId)")
|
||||||
let entities = generateChatInputTextEntities(caption)
|
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)
|
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 {
|
Queue.mainQueue().justDispatch {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user