mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Various fixes
This commit is contained in:
parent
ce3af41ebe
commit
7b5883c115
@ -1161,7 +1161,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
|
|
||||||
func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController
|
func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController
|
||||||
|
|
||||||
func makeStoryMediaEditorScreen(context: AccountContext, source: Any?, text: String?, link: (url: String, name: String?)?, completion: @escaping ([MediaEditorScreenResult], @escaping (@escaping () -> Void) -> Void) -> Void) -> ViewController
|
func makeStoryMediaEditorScreen(context: AccountContext, source: Any?, text: String?, link: (url: String, name: String?)?, remainingCount: Int32, completion: @escaping ([MediaEditorScreenResult], MediaEditorTransitionOutExternalState, @escaping (@escaping () -> Void) -> Void) -> Void) -> ViewController
|
||||||
|
|
||||||
func makeBotPreviewEditorScreen(context: AccountContext, source: Any?, target: Stories.PendingTarget, transitionArguments: (UIView, CGRect, UIImage?)?, transitionOut: @escaping () -> BotPreviewEditorTransitionOut?, externalState: MediaEditorTransitionOutExternalState, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController
|
func makeBotPreviewEditorScreen(context: AccountContext, source: Any?, target: Stories.PendingTarget, transitionArguments: (UIView, CGRect, UIImage?)?, transitionOut: @escaping () -> BotPreviewEditorTransitionOut?, externalState: MediaEditorTransitionOutExternalState, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ final class CameraSession {
|
|||||||
|
|
||||||
let hasMultiCam: Bool
|
let hasMultiCam: Bool
|
||||||
|
|
||||||
init() {
|
init(forRoundVideo: Bool) {
|
||||||
if #available(iOS 13.0, *), AVCaptureMultiCamSession.isMultiCamSupported {
|
if #available(iOS 13.0, *), Camera.isDualCameraSupported(forRoundVideo: forRoundVideo) {
|
||||||
self.multiSession = AVCaptureMultiCamSession()
|
self.multiSession = AVCaptureMultiCamSession()
|
||||||
self.singleSession = nil
|
self.singleSession = nil
|
||||||
self.hasMultiCam = true
|
self.hasMultiCam = true
|
||||||
@ -765,7 +765,7 @@ public final class Camera {
|
|||||||
|
|
||||||
self.metrics = Camera.Metrics(model: DeviceModel.current)
|
self.metrics = Camera.Metrics(model: DeviceModel.current)
|
||||||
|
|
||||||
let session = CameraSession()
|
let session = CameraSession(forRoundVideo: configuration.isRoundVideo)
|
||||||
session.session.automaticallyConfiguresApplicationAudioSession = false
|
session.session.automaticallyConfiguresApplicationAudioSession = false
|
||||||
session.session.automaticallyConfiguresCaptureDeviceForWideColor = false
|
session.session.automaticallyConfiguresCaptureDeviceForWideColor = false
|
||||||
session.session.usesApplicationAudioSession = true
|
session.session.usesApplicationAudioSession = true
|
||||||
|
168
submodules/Camera/Sources/CameraRoundLegacyVideoFilter.swift
Normal file
168
submodules/Camera/Sources/CameraRoundLegacyVideoFilter.swift
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import AVFoundation
|
||||||
|
import CoreImage
|
||||||
|
import CoreMedia
|
||||||
|
import CoreVideo
|
||||||
|
import Metal
|
||||||
|
import Display
|
||||||
|
import TelegramCore
|
||||||
|
|
||||||
|
final class CameraRoundLegacyVideoFilter {
|
||||||
|
private let ciContext: CIContext
|
||||||
|
private let colorSpace: CGColorSpace
|
||||||
|
private let simple: Bool
|
||||||
|
|
||||||
|
private var resizeFilter: CIFilter?
|
||||||
|
private var overlayFilter: CIFilter?
|
||||||
|
private var compositeFilter: CIFilter?
|
||||||
|
private var borderFilter: CIFilter?
|
||||||
|
|
||||||
|
private var outputColorSpace: CGColorSpace?
|
||||||
|
private var outputPixelBufferPool: CVPixelBufferPool?
|
||||||
|
private(set) var outputFormatDescription: CMFormatDescription?
|
||||||
|
private(set) var inputFormatDescription: CMFormatDescription?
|
||||||
|
|
||||||
|
private(set) var isPrepared = false
|
||||||
|
|
||||||
|
init(ciContext: CIContext, colorSpace: CGColorSpace, simple: Bool) {
|
||||||
|
self.ciContext = ciContext
|
||||||
|
self.colorSpace = colorSpace
|
||||||
|
self.simple = simple
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepare(with formatDescription: CMFormatDescription, outputRetainedBufferCountHint: Int) {
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
(self.outputPixelBufferPool, self.outputColorSpace, self.outputFormatDescription) = allocateOutputBufferPool(with: formatDescription, outputRetainedBufferCountHint: outputRetainedBufferCountHint)
|
||||||
|
if self.outputPixelBufferPool == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.inputFormatDescription = formatDescription
|
||||||
|
|
||||||
|
let circleImage = generateImage(videoMessageDimensions.cgSize, opaque: false, scale: 1.0, rotatedContext: { size, context in
|
||||||
|
let bounds = CGRect(origin: .zero, size: size)
|
||||||
|
context.clear(bounds)
|
||||||
|
context.setFillColor(UIColor.white.cgColor)
|
||||||
|
context.fill(bounds)
|
||||||
|
context.setBlendMode(.clear)
|
||||||
|
context.fillEllipse(in: bounds.insetBy(dx: -2.0, dy: -2.0))
|
||||||
|
})!
|
||||||
|
|
||||||
|
self.resizeFilter = CIFilter(name: "CILanczosScaleTransform")
|
||||||
|
self.overlayFilter = CIFilter(name: "CIColorMatrix")
|
||||||
|
self.compositeFilter = CIFilter(name: "CISourceOverCompositing")
|
||||||
|
|
||||||
|
self.borderFilter = CIFilter(name: "CISourceOverCompositing")
|
||||||
|
self.borderFilter?.setValue(CIImage(image: circleImage), forKey: kCIInputImageKey)
|
||||||
|
|
||||||
|
self.isPrepared = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
self.resizeFilter = nil
|
||||||
|
self.overlayFilter = nil
|
||||||
|
self.compositeFilter = nil
|
||||||
|
self.borderFilter = nil
|
||||||
|
self.outputColorSpace = nil
|
||||||
|
self.outputPixelBufferPool = nil
|
||||||
|
self.outputFormatDescription = nil
|
||||||
|
self.inputFormatDescription = nil
|
||||||
|
self.isPrepared = false
|
||||||
|
self.lastMainSourceImage = nil
|
||||||
|
self.lastAdditionalSourceImage = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private var lastMainSourceImage: CIImage?
|
||||||
|
private var lastAdditionalSourceImage: CIImage?
|
||||||
|
|
||||||
|
func render(pixelBuffer: CVPixelBuffer, additional: Bool, captureOrientation: AVCaptureVideoOrientation, transitionFactor: CGFloat) -> CVPixelBuffer? {
|
||||||
|
guard let resizeFilter = self.resizeFilter, let overlayFilter = self.overlayFilter, let compositeFilter = self.compositeFilter, let borderFilter = self.borderFilter, self.isPrepared else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceImage = CIImage(cvImageBuffer: pixelBuffer, options: [.colorSpace: self.colorSpace])
|
||||||
|
var sourceOrientation: CGImagePropertyOrientation
|
||||||
|
var sourceIsLandscape = false
|
||||||
|
switch captureOrientation {
|
||||||
|
case .portrait:
|
||||||
|
sourceOrientation = additional ? .leftMirrored : .right
|
||||||
|
case .landscapeLeft:
|
||||||
|
sourceOrientation = additional ? .upMirrored : .down
|
||||||
|
sourceIsLandscape = true
|
||||||
|
case .landscapeRight:
|
||||||
|
sourceOrientation = additional ? .downMirrored : .up
|
||||||
|
sourceIsLandscape = true
|
||||||
|
case .portraitUpsideDown:
|
||||||
|
sourceOrientation = additional ? .rightMirrored : .left
|
||||||
|
@unknown default:
|
||||||
|
sourceOrientation = additional ? .leftMirrored : .right
|
||||||
|
}
|
||||||
|
sourceImage = sourceImage.oriented(sourceOrientation)
|
||||||
|
let scale = CGFloat(videoMessageDimensions.width) / min(sourceImage.extent.width, sourceImage.extent.height)
|
||||||
|
|
||||||
|
if !self.simple {
|
||||||
|
resizeFilter.setValue(sourceImage, forKey: kCIInputImageKey)
|
||||||
|
resizeFilter.setValue(scale, forKey: kCIInputScaleKey)
|
||||||
|
|
||||||
|
if let resizedImage = resizeFilter.outputImage {
|
||||||
|
sourceImage = resizedImage
|
||||||
|
} else {
|
||||||
|
sourceImage = sourceImage.transformed(by: CGAffineTransformMakeScale(scale, scale), highQualityDownsample: true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sourceImage = sourceImage.transformed(by: CGAffineTransformMakeScale(scale, scale), highQualityDownsample: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceIsLandscape {
|
||||||
|
sourceImage = sourceImage.transformed(by: CGAffineTransformMakeTranslation(-(sourceImage.extent.width - sourceImage.extent.height) / 2.0, 0.0))
|
||||||
|
sourceImage = sourceImage.cropped(to: CGRect(x: 0.0, y: 0.0, width: sourceImage.extent.height, height: sourceImage.extent.height))
|
||||||
|
} else {
|
||||||
|
sourceImage = sourceImage.transformed(by: CGAffineTransformMakeTranslation(0.0, -(sourceImage.extent.height - sourceImage.extent.width) / 2.0))
|
||||||
|
sourceImage = sourceImage.cropped(to: CGRect(x: 0.0, y: 0.0, width: sourceImage.extent.width, height: sourceImage.extent.width))
|
||||||
|
}
|
||||||
|
|
||||||
|
if additional {
|
||||||
|
self.lastAdditionalSourceImage = sourceImage
|
||||||
|
} else {
|
||||||
|
self.lastMainSourceImage = sourceImage
|
||||||
|
}
|
||||||
|
|
||||||
|
var effectiveSourceImage: CIImage
|
||||||
|
if transitionFactor == 0.0 {
|
||||||
|
effectiveSourceImage = !additional ? sourceImage : (self.lastMainSourceImage ?? sourceImage)
|
||||||
|
} else if transitionFactor == 1.0 {
|
||||||
|
effectiveSourceImage = additional ? sourceImage : (self.lastAdditionalSourceImage ?? sourceImage)
|
||||||
|
} else {
|
||||||
|
if let mainSourceImage = self.lastMainSourceImage, let additionalSourceImage = self.lastAdditionalSourceImage {
|
||||||
|
let overlayRgba: [CGFloat] = [0, 0, 0, transitionFactor]
|
||||||
|
let alphaVector: CIVector = CIVector(values: overlayRgba, count: 4)
|
||||||
|
overlayFilter.setValue(additionalSourceImage, forKey: kCIInputImageKey)
|
||||||
|
overlayFilter.setValue(alphaVector, forKey: "inputAVector")
|
||||||
|
|
||||||
|
compositeFilter.setValue(mainSourceImage, forKey: kCIInputBackgroundImageKey)
|
||||||
|
compositeFilter.setValue(overlayFilter.outputImage, forKey: kCIInputImageKey)
|
||||||
|
effectiveSourceImage = compositeFilter.outputImage ?? sourceImage
|
||||||
|
} else {
|
||||||
|
effectiveSourceImage = sourceImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
borderFilter.setValue(effectiveSourceImage, forKey: kCIInputBackgroundImageKey)
|
||||||
|
|
||||||
|
let finalImage = borderFilter.outputImage
|
||||||
|
guard let finalImage else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var pbuf: CVPixelBuffer?
|
||||||
|
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, outputPixelBufferPool!, &pbuf)
|
||||||
|
guard let outputPixelBuffer = pbuf else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ciContext.render(finalImage, to: outputPixelBuffer, bounds: CGRect(origin: .zero, size: videoMessageDimensions.cgSize), colorSpace: outputColorSpace)
|
||||||
|
|
||||||
|
return outputPixelBuffer
|
||||||
|
}
|
||||||
|
}
|
@ -78,7 +78,7 @@ func allocateOutputBufferPool(with inputFormatDescription: CMFormatDescription,
|
|||||||
return (pixelBufferPool, cgColorSpace, outputFormatDescription)
|
return (pixelBufferPool, cgColorSpace, outputFormatDescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preallocateBuffers(pool: CVPixelBufferPool, allocationThreshold: Int) {
|
func preallocateBuffers(pool: CVPixelBufferPool, allocationThreshold: Int) {
|
||||||
var pixelBuffers = [CVPixelBuffer]()
|
var pixelBuffers = [CVPixelBuffer]()
|
||||||
var error: CVReturn = kCVReturnSuccess
|
var error: CVReturn = kCVReturnSuccess
|
||||||
let auxAttributes = [kCVPixelBufferPoolAllocationThresholdKey as String: allocationThreshold] as NSDictionary
|
let auxAttributes = [kCVPixelBufferPoolAllocationThresholdKey as String: allocationThreshold] as NSDictionary
|
||||||
|
@ -297,12 +297,13 @@ extension ChatControllerImpl {
|
|||||||
if data.duration < 0.5 {
|
if data.duration < 0.5 {
|
||||||
strongSelf.recorderFeedback?.error()
|
strongSelf.recorderFeedback?.error()
|
||||||
strongSelf.recorderFeedback = nil
|
strongSelf.recorderFeedback = nil
|
||||||
|
strongSelf.audioRecorder.set(.single(nil))
|
||||||
|
strongSelf.recorderDataDisposable.set(nil)
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||||
$0.updatedInputTextPanelState { panelState in
|
$0.updatedInputTextPanelState { panelState in
|
||||||
return panelState.withUpdatedMediaRecordingState(nil)
|
return panelState.withUpdatedMediaRecordingState(nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
strongSelf.recorderDataDisposable.set(nil)
|
|
||||||
} else if let waveform = data.waveform {
|
} else if let waveform = data.waveform {
|
||||||
if resource == nil {
|
if resource == nil {
|
||||||
resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max), size: Int64(data.compressedData.count))
|
resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max), size: Int64(data.compressedData.count))
|
||||||
@ -351,6 +352,7 @@ extension ChatControllerImpl {
|
|||||||
strongSelf.recorderFeedback?.error()
|
strongSelf.recorderFeedback?.error()
|
||||||
strongSelf.recorderFeedback = nil
|
strongSelf.recorderFeedback = nil
|
||||||
strongSelf.audioRecorder.set(.single(nil))
|
strongSelf.audioRecorder.set(.single(nil))
|
||||||
|
strongSelf.recorderDataDisposable.set(nil)
|
||||||
} else {
|
} else {
|
||||||
let randomId = Int64.random(in: Int64.min ... Int64.max)
|
let randomId = Int64.random(in: Int64.min ... Int64.max)
|
||||||
|
|
||||||
|
@ -926,24 +926,50 @@ func openResolvedUrlImpl(
|
|||||||
source = subject
|
source = subject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let externalState = MediaEditorTransitionOutExternalState(
|
let _ = (context.engine.messages.checkStoriesUploadAvailability(target: .myStories)
|
||||||
storyTarget: nil,
|
|> deliverOnMainQueue).start(next: { availability in
|
||||||
isForcedTarget: false,
|
if case let .available(remainingCount) = availability {
|
||||||
isPeerArchived: false,
|
let controller = context.sharedContext.makeStoryMediaEditorScreen(context: context, source: source, text: nil, link: nil, remainingCount: remainingCount, completion: { results, externalState, commit in
|
||||||
transitionOut: nil
|
let target: Stories.PendingTarget = results.first!.target
|
||||||
)
|
externalState.storyTarget = target
|
||||||
let controller = context.sharedContext.makeStoryMediaEditorScreen(context: context, source: source, text: nil, link: nil, completion: { results, commit in
|
|
||||||
let target: Stories.PendingTarget = results.first!.target
|
if let rootController = context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface {
|
||||||
externalState.storyTarget = target
|
rootController.popToRoot(animated: false)
|
||||||
|
rootController.proceedWithStoryUpload(target: target, results: results, existingMedia: nil, forwardInfo: nil, externalState: externalState, commit: commit)
|
||||||
if let rootController = context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface {
|
}
|
||||||
rootController.proceedWithStoryUpload(target: target, results: results, existingMedia: nil, forwardInfo: nil, externalState: externalState, commit: commit)
|
})
|
||||||
|
if let navigationController {
|
||||||
|
navigationController.pushViewController(controller)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let subject: PremiumLimitSubject
|
||||||
|
switch availability {
|
||||||
|
case .expiringLimit:
|
||||||
|
subject = .expiringStories
|
||||||
|
case .weeklyLimit:
|
||||||
|
subject = .storiesWeekly
|
||||||
|
case .monthlyLimit:
|
||||||
|
subject = .storiesMonthly
|
||||||
|
default:
|
||||||
|
subject = .expiringStories
|
||||||
|
}
|
||||||
|
var replaceImpl: ((ViewController) -> Void)?
|
||||||
|
let controller = context.sharedContext.makePremiumLimitController(context: context, subject: subject, count: 10, forceDark: false, cancel: {
|
||||||
|
}, action: {
|
||||||
|
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true, dismissed: {
|
||||||
|
})
|
||||||
|
replaceImpl?(controller)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
replaceImpl = { [weak controller] c in
|
||||||
|
controller?.replace(with: c)
|
||||||
|
}
|
||||||
|
if let navigationController {
|
||||||
|
navigationController.pushViewController(controller)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if let navigationController {
|
|
||||||
navigationController.pushViewController(controller)
|
|
||||||
}
|
|
||||||
case let .startAttach(peerId, payload, choose):
|
case let .startAttach(peerId, payload, choose):
|
||||||
let presentError: (String) -> Void = { errorText in
|
let presentError: (String) -> Void = { errorText in
|
||||||
present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: errorText, timeout: nil, customUndoText: nil), elevatedLayout: true, animateInAsReplacement: false, action: { _ in
|
present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: errorText, timeout: nil, customUndoText: nil), elevatedLayout: true, animateInAsReplacement: false, action: { _ in
|
||||||
|
@ -3552,31 +3552,49 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return editorController
|
return editorController
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeStoryMediaEditorScreen(context: AccountContext, source: Any?, text: String?, link: (url: String, name: String?)?, completion: @escaping ([MediaEditorScreenResult], @escaping (@escaping () -> Void) -> Void) -> Void) -> ViewController {
|
public func makeStoryMediaEditorScreen(context: AccountContext, source: Any?, text: String?, link: (url: String, name: String?)?, remainingCount: Int32, completion: @escaping ([MediaEditorScreenResult], MediaEditorTransitionOutExternalState, @escaping (@escaping () -> Void) -> Void) -> Void) -> ViewController {
|
||||||
let subject: Signal<MediaEditorScreenImpl.Subject?, NoError>
|
let editorSubject: Signal<MediaEditorScreenImpl.Subject?, NoError>
|
||||||
if let image = source as? UIImage {
|
if let image = source as? UIImage {
|
||||||
subject = .single(.image(image: image, dimensions: PixelDimensions(image.size), additionalImage: nil, additionalImagePosition: .bottomRight, fromCamera: false))
|
editorSubject = .single(.image(image: image, dimensions: PixelDimensions(image.size), additionalImage: nil, additionalImagePosition: .bottomRight, fromCamera: false))
|
||||||
} else if let path = source as? String {
|
} else if let path = source as? String {
|
||||||
subject = .single(.video(videoPath: path, thumbnail: nil, mirror: false, additionalVideoPath: nil, additionalThumbnail: nil, dimensions: PixelDimensions(width: 1080, height: 1920), duration: 0.0, videoPositionChanges: [], additionalVideoPosition: .bottomRight, fromCamera: false))
|
editorSubject = .single(.video(videoPath: path, thumbnail: nil, mirror: false, additionalVideoPath: nil, additionalThumbnail: nil, dimensions: PixelDimensions(width: 1080, height: 1920), duration: 0.0, videoPositionChanges: [], additionalVideoPosition: .bottomRight, fromCamera: false))
|
||||||
} else if let subjects = source as? [MediaEditorScreenImpl.Subject] {
|
} else if let subjects = source as? [MediaEditorScreenImpl.Subject] {
|
||||||
subject = .single(.multiple(subjects))
|
editorSubject = .single(.multiple(subjects))
|
||||||
} else if let subjectValue = source as? MediaEditorScreenImpl.Subject {
|
} else if let subjectValue = source as? MediaEditorScreenImpl.Subject {
|
||||||
subject = .single(subjectValue)
|
editorSubject = .single(subjectValue)
|
||||||
} else {
|
} else {
|
||||||
subject = .single(.empty(PixelDimensions(width: 1080, height: 1920)))
|
editorSubject = .single(.empty(PixelDimensions(width: 1080, height: 1920)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let externalState = MediaEditorTransitionOutExternalState(
|
||||||
|
storyTarget: nil,
|
||||||
|
isForcedTarget: false,
|
||||||
|
isPeerArchived: false,
|
||||||
|
transitionOut: nil
|
||||||
|
)
|
||||||
|
|
||||||
let editorController = MediaEditorScreenImpl(
|
let editorController = MediaEditorScreenImpl(
|
||||||
context: context,
|
context: context,
|
||||||
mode: .storyEditor(remainingCount: 1),
|
mode: .storyEditor(remainingCount: remainingCount),
|
||||||
subject: subject,
|
subject: editorSubject,
|
||||||
customTarget: nil,
|
customTarget: nil,
|
||||||
initialCaption: text.flatMap { NSAttributedString(string: $0) },
|
initialCaption: text.flatMap { NSAttributedString(string: $0) },
|
||||||
initialLink: link,
|
initialLink: link,
|
||||||
transitionIn: nil,
|
transitionIn: nil,
|
||||||
transitionOut: { finished, isNew in
|
transitionOut: { finished, isNew in
|
||||||
|
if let externalTransitionOut = externalState.transitionOut {
|
||||||
|
if finished, let transitionOut = externalTransitionOut(externalState.storyTarget, false), let destinationView = transitionOut.destinationView {
|
||||||
|
return MediaEditorScreenImpl.TransitionOut(
|
||||||
|
destinationView: destinationView,
|
||||||
|
destinationRect: transitionOut.destinationRect,
|
||||||
|
destinationCornerRadius: transitionOut.destinationCornerRadius,
|
||||||
|
completion: transitionOut.completion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}, completion: { results, commit in
|
}, completion: { results, commit in
|
||||||
completion(results, commit)
|
completion(results, externalState, commit)
|
||||||
} as ([MediaEditorScreenImpl.Result], @escaping (@escaping () -> Void) -> Void) -> Void
|
} as ([MediaEditorScreenImpl.Result], @escaping (@escaping () -> Void) -> Void) -> Void
|
||||||
)
|
)
|
||||||
return editorController
|
return editorController
|
||||||
|
@ -1487,13 +1487,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let source {
|
if let source {
|
||||||
let externalState = MediaEditorTransitionOutExternalState(
|
let controller = self.context.sharedContext.makeStoryMediaEditorScreen(context: self.context, source: source, text: text, link: linkUrl.flatMap { ($0, linkName) }, remainingCount: 1, completion: { results, externalState, commit in
|
||||||
storyTarget: nil,
|
|
||||||
isForcedTarget: false,
|
|
||||||
isPeerArchived: false,
|
|
||||||
transitionOut: nil
|
|
||||||
)
|
|
||||||
let controller = self.context.sharedContext.makeStoryMediaEditorScreen(context: self.context, source: source, text: text, link: linkUrl.flatMap { ($0, linkName) }, completion: { results, commit in
|
|
||||||
let target: Stories.PendingTarget = results.first!.target
|
let target: Stories.PendingTarget = results.first!.target
|
||||||
externalState.storyTarget = target
|
externalState.storyTarget = target
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user