diff --git a/submodules/DeviceAccess/Sources/DeviceAccess.swift b/submodules/DeviceAccess/Sources/DeviceAccess.swift index 4751691d08..eeab79c9d2 100644 --- a/submodules/DeviceAccess/Sources/DeviceAccess.swift +++ b/submodules/DeviceAccess/Sources/DeviceAccess.swift @@ -11,7 +11,6 @@ import AddressBook import UserNotifications import CoreTelephony import TelegramPresentationData -import LegacyComponents import AccountContext public enum DeviceAccessCameraSubject { @@ -88,7 +87,7 @@ public final class DeviceAccess { } public static func isCameraAccessAuthorized() -> Bool { - return PGCamera.cameraAuthorizationStatus() == PGCameraAuthorizationStatusAuthorized + return AVCaptureDevice.authorizationStatus(for: .video) == .authorized } public static func authorizationStatus(applicationInForeground: Signal? = nil, siriAuthorization: (() -> AccessType)? = nil, subject: DeviceAccessSubject) -> Signal { @@ -257,8 +256,8 @@ public final class DeviceAccess { public static func authorizeAccess(to subject: DeviceAccessSubject, onlyCheck: Bool = false, registerForNotifications: ((@escaping (Bool) -> Void) -> Void)? = nil, requestSiriAuthorization: ((@escaping (Bool) -> Void) -> Void)? = nil, locationManager: LocationManager? = nil, presentationData: PresentationData? = nil, present: @escaping (ViewController, Any?) -> Void = { _, _ in }, openSettings: @escaping () -> Void = { }, displayNotificationFromBackground: @escaping (String) -> Void = { _ in }, _ completion: @escaping (Bool) -> Void = { _ in }) { switch subject { case let .camera(cameraSubject): - let status = PGCamera.cameraAuthorizationStatus() - if status == PGCameraAuthorizationStatusNotDetermined { + let status = AVCaptureDevice.authorizationStatus(for: .video) + if case .notDetermined = status { if !onlyCheck { AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in Queue.mainQueue().async { @@ -282,9 +281,9 @@ public final class DeviceAccess { } else { completion(true) } - } else if status == PGCameraAuthorizationStatusRestricted || status == PGCameraAuthorizationStatusDenied, let presentationData = presentationData { + } else if [.restricted, .denied].contains(status), let presentationData = presentationData { let text: String - if status == PGCameraAuthorizationStatusRestricted { + if case .restricted = status { text = presentationData.strings.AccessDenied_CameraRestricted } else { switch cameraSubject { @@ -300,7 +299,7 @@ public final class DeviceAccess { present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { openSettings() })]), nil) - } else if status == PGCameraAuthorizationStatusAuthorized { + } else if case .authorized = status { completion(true) } else { assertionFailure() diff --git a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift index 7222906a27..5524e35bf6 100644 --- a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift @@ -611,3 +611,26 @@ extension Api.EncryptedMessage { } } } + +extension Api.InputMedia { + func withUpdatedStickers(_ stickers: [Api.InputDocument]?) -> Api.InputMedia { + switch self { + case let .inputMediaUploadedDocument(flags, file, thumb, mimeType, attributes, _, ttlSeconds): + var flags = flags + var attributes = attributes + if let _ = stickers { + flags |= (1 << 0) + attributes.append(.documentAttributeHasStickers) + } + return .inputMediaUploadedDocument(flags: flags, file: file, thumb: thumb, mimeType: mimeType, attributes: attributes, stickers: stickers, ttlSeconds: ttlSeconds) + case let .inputMediaUploadedPhoto(flags, file, _, ttlSeconds): + var flags = flags + if let _ = stickers { + flags |= (1 << 0) + } + return .inputMediaUploadedPhoto(flags: flags, file: file, stickers: stickers, ttlSeconds: ttlSeconds) + default: + return self + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift index c7cf01878f..445370d041 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PendingStoryManager.swift @@ -11,6 +11,7 @@ public extension Stories { case media case text case entities + case embeddedStickers case pin case privacy case isForwardingDisabled @@ -23,6 +24,7 @@ public extension Stories { public let media: Media public let text: String public let entities: [MessageTextEntity] + public let embeddedStickers: [TelegramMediaFile] public let pin: Bool public let privacy: EngineStoryPrivacy public let isForwardingDisabled: Bool @@ -35,6 +37,7 @@ public extension Stories { media: Media, text: String, entities: [MessageTextEntity], + embeddedStickers: [TelegramMediaFile], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, @@ -46,6 +49,7 @@ public extension Stories { self.media = media self.text = text self.entities = entities + self.embeddedStickers = embeddedStickers self.pin = pin self.privacy = privacy self.isForwardingDisabled = isForwardingDisabled @@ -64,6 +68,11 @@ public extension Stories { self.text = try container.decode(String.self, forKey: .text) self.entities = try container.decode([MessageTextEntity].self, forKey: .entities) + + let stickersData = try container.decode(Data.self, forKey: .embeddedStickers) + let stickersDecoder = PostboxDecoder(buffer: MemoryBuffer(data: stickersData)) + self.embeddedStickers = (try? stickersDecoder.decodeObjectArrayWithCustomDecoderForKey("stickers", decoder: { TelegramMediaFile(decoder: $0) })) ?? [] + self.pin = try container.decode(Bool.self, forKey: .pin) self.privacy = try container.decode(EngineStoryPrivacy.self, forKey: .privacy) self.isForwardingDisabled = try container.decodeIfPresent(Bool.self, forKey: .isForwardingDisabled) ?? false @@ -83,6 +92,11 @@ public extension Stories { try container.encode(self.text, forKey: .text) try container.encode(self.entities, forKey: .entities) + + let stickersEncoder = PostboxEncoder() + stickersEncoder.encodeObjectArray(self.embeddedStickers, forKey: "stickers") + try container.encode(stickersEncoder.makeData(), forKey: .embeddedStickers) + try container.encode(self.pin, forKey: .pin) try container.encode(self.privacy, forKey: .privacy) try container.encode(self.isForwardingDisabled, forKey: .isForwardingDisabled) @@ -270,7 +284,7 @@ final class PendingStoryManager { self.currentPendingItemContext = pendingItemContext let stableId = firstItem.stableId - 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, 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 guard let `self` = self else { return diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index 2fcb4eff13..a396de6e1c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -4,8 +4,15 @@ import Postbox import TelegramApi public enum EngineStoryInputMedia { - case image(dimensions: PixelDimensions, data: Data) - case video(dimensions: PixelDimensions, duration: Double, resource: TelegramMediaResource, firstFrameImageData: Data?) + case image(dimensions: PixelDimensions, data: Data, stickers: [TelegramMediaFile]) + case video(dimensions: PixelDimensions, duration: Double, resource: TelegramMediaResource, firstFrameImageData: Data?, stickers: [TelegramMediaFile]) + + var embeddedStickers: [TelegramMediaFile] { + switch self { + case let .image(_, _, stickers), let .video(_, _, _, _, stickers): + return stickers + } + } } public struct EngineStoryPrivacy: Codable, Equatable { @@ -567,7 +574,7 @@ public enum StoryUploadResult { private func prepareUploadStoryContent(account: Account, media: EngineStoryInputMedia) -> Media { switch media { - case let .image(dimensions, data): + case let .image(dimensions, data, _): let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) account.postbox.mediaBox.storeResourceData(resource.id, data: data) @@ -580,7 +587,7 @@ private func prepareUploadStoryContent(account: Account, media: EngineStoryInput flags: [] ) return imageMedia - case let .video(dimensions, duration, resource, firstFrameImageData): + case let .video(dimensions, duration, resource, firstFrameImageData, _): var previewRepresentations: [TelegramMediaImageRepresentation] = [] if let firstFrameImageData = firstFrameImageData { let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) @@ -709,6 +716,7 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text: media: inputMedia, text: text, entities: entities, + embeddedStickers: media.embeddedStickers, pin: pin, privacy: privacy, isForwardingDisabled: isForwardingDisabled, @@ -757,7 +765,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], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal { +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 { let passFetchProgress = media is TelegramMediaFile let (contentSignal, originalMedia) = uploadedStoryContent(postbox: postbox, network: network, media: media, accountPeerId: accountPeerId, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, auxiliaryMethods: auxiliaryMethods, passFetchProgress: passFetchProgress) return contentSignal @@ -802,6 +810,17 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId flags |= 1 << 4 } + var inputMedia = inputMedia + if !embeddedStickers.isEmpty { + var stickersValue: [Api.InputDocument] = [] + for file in embeddedStickers { + if let resource = file.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference { + stickersValue.append(Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference))) + } + } + inputMedia = inputMedia.withUpdatedStickers(stickersValue) + } + return network.request(Api.functions.stories.sendStory( flags: flags, media: inputMedia, diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 14fa0a0a75..17aeaab15e 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -98,7 +98,7 @@ private final class CameraScreenComponent: CombinedComponent { let hasAppeared: Bool let isVisible: Bool let panelWidth: CGFloat - let flipAnimationAction: ActionSlot + let animateFlipAction: ActionSlot let animateShutter: () -> Void let present: (ViewController) -> Void let push: (ViewController) -> Void @@ -112,7 +112,7 @@ private final class CameraScreenComponent: CombinedComponent { hasAppeared: Bool, isVisible: Bool, panelWidth: CGFloat, - flipAnimationAction: ActionSlot, + animateFlipAction: ActionSlot, animateShutter: @escaping () -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, @@ -125,7 +125,7 @@ private final class CameraScreenComponent: CombinedComponent { self.hasAppeared = hasAppeared self.isVisible = isVisible self.panelWidth = panelWidth - self.flipAnimationAction = flipAnimationAction + self.animateFlipAction = animateFlipAction self.animateShutter = animateShutter self.present = present self.push = push @@ -170,6 +170,10 @@ private final class CameraScreenComponent: CombinedComponent { } } + private var cameraAuthorizationStatus: AVAuthorizationStatus = .notDetermined + private var microphoneAuthorizationStatus: AVAuthorizationStatus = .notDetermined + private var galleryAuthorizationStatus: PHAuthorizationStatus = .notDetermined + private let context: AccountContext fileprivate let camera: Camera private let present: (ViewController) -> Void @@ -652,7 +656,7 @@ private final class CameraScreenComponent: CombinedComponent { } } - let flipAnimationAction = component.flipAnimationAction + let animateFlipAction = component.animateFlipAction let captureControlsAvailableSize: CGSize if isTablet { captureControlsAvailableSize = CGSize(width: panelWidth, height: availableSize.height) @@ -706,7 +710,7 @@ private final class CameraScreenComponent: CombinedComponent { guard let state else { return } - state.togglePosition(flipAnimationAction) + state.togglePosition(animateFlipAction) }, galleryTapped: { guard let controller = environment.controller() as? CameraScreen else { @@ -720,7 +724,7 @@ private final class CameraScreenComponent: CombinedComponent { zoomUpdated: { fraction in state.updateZoom(fraction: fraction) }, - flipAnimationAction: flipAnimationAction + flipAnimationAction: animateFlipAction ), availableSize: captureControlsAvailableSize, transition: context.transition @@ -743,14 +747,14 @@ private final class CameraScreenComponent: CombinedComponent { id: "flip", component: AnyComponent( FlipButtonContentComponent( - action: flipAnimationAction, + action: animateFlipAction, maskFrame: .zero ) ) ), minSize: CGSize(width: 44.0, height: 44.0), action: { - state.togglePosition(flipAnimationAction) + state.togglePosition(animateFlipAction) } ), availableSize: availableSize, @@ -1036,7 +1040,7 @@ public class CameraScreen: ViewController { private var pipPosition: PIPPosition = .bottomRight fileprivate var previewBlurPromise = ValuePromise(false) - private let flipAnimationAction = ActionSlot() + private let animateFlipAction = ActionSlot() fileprivate var cameraIsActive = true fileprivate var hasGallery = false @@ -1738,6 +1742,7 @@ public class CameraScreen: ViewController { self.hasAppeared = hasAppeared transition = transition.withUserData(CameraScreenTransition.finishedAnimateIn) + // self.presentCameraTooltip() // self.presentDualCameraTooltip() } @@ -1753,7 +1758,7 @@ public class CameraScreen: ViewController { hasAppeared: self.hasAppeared, isVisible: self.cameraIsActive && !self.hasGallery, panelWidth: panelWidth, - flipAnimationAction: self.flipAnimationAction, + animateFlipAction: self.animateFlipAction, animateShutter: { [weak self] in self?.mainPreviewContainerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) }, diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift index df6e5a93e7..e2ab97ef27 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift @@ -8,6 +8,7 @@ import LocalMediaResources import CameraButtonComponent enum ShutterButtonState: Equatable { + case disabled case generic case video case stopRecording @@ -162,7 +163,7 @@ private final class ShutterButtonContentComponent: Component { let ringWidth: CGFloat = 3.0 var recordingProgress: Float? switch component.shutterState { - case .generic: + case .generic, .disabled: innerColor = .white innerSize = CGSize(width: 60.0, height: 60.0) ringSize = CGSize(width: 68.0, height: 68.0) @@ -986,7 +987,7 @@ final class CaptureControlsComponent: Component { var blobState: ShutterBlobView.BlobState switch component.shutterState { - case .generic: + case .generic, .disabled: blobState = .generic case .video, .transition: blobState = .video diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift index 0e84964a99..cc6e774792 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift @@ -134,9 +134,9 @@ final class MediaEditorComposer { } private var filteredImage: CIImage? - func processImage(inputImage: UIImage, pool: CVPixelBufferPool?, time: CMTime, completion: @escaping (CVPixelBuffer?, CMTime) -> Void) { + func processImage(inputImage: UIImage, pool: CVPixelBufferPool?, time: CMTime, completion: @escaping (CVPixelBuffer?) -> Void) { guard let pool else { - completion(nil, time) + completion(nil) return } if self.filteredImage == nil, let device = self.device { @@ -161,15 +161,15 @@ final class MediaEditorComposer { compositedImage = compositedImage.samplingLinear().transformed(by: CGAffineTransform(scaleX: scale, y: scale)) self.ciContext?.render(compositedImage, to: pixelBuffer) - completion(pixelBuffer, time) + completion(pixelBuffer) } else { - completion(nil, time) + completion(nil) } }) return } } - completion(nil, time) + completion(nil) } func processImage(inputImage: CIImage, time: CMTime, completion: @escaping (CIImage?) -> Void) { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift index 4d8160470d..0b0ef2fc7d 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift @@ -569,18 +569,30 @@ public final class MediaEditorVideoExport { let progress = (position - .zero).seconds / duration self.statusValue = .progress(Float(progress)) - composer.processImage(inputImage: image, pool: writer.pixelBufferPool, time: position, completion: { pixelBuffer, timestamp in + composer.processImage(inputImage: image, pool: writer.pixelBufferPool, time: position, completion: { pixelBuffer in if let pixelBuffer { - if !writer.appendPixelBuffer(pixelBuffer, at: timestamp) { - Logger.shared.log("VideoExport", "Failed to append pixelbuffer") - writer.markVideoAsFinished() - appendFailed = true + if !writer.appendPixelBuffer(pixelBuffer, at: position) { + Logger.shared.log("VideoExport", "Failed to append pixelbuffer at \(position.seconds), trying to wait") + Queue.concurrentDefaultQueue().after(1.0, { + if !writer.appendPixelBuffer(pixelBuffer, at: position) { + Logger.shared.log("VideoExport", "Failed to append pixelbuffer at \(position.seconds), complete failure") + writer.markVideoAsFinished() + appendFailed = true + self.semaphore.signal() + } + }) + } else { + Logger.shared.log("VideoExport", "Appended pixelbuffer at \(position.seconds)") + + Thread.sleep(forTimeInterval: 0.01) + self.semaphore.signal() } } else { Logger.shared.log("VideoExport", "No pixelbuffer from composer") + + Thread.sleep(forTimeInterval: 0.01) + self.semaphore.signal() } - Thread.sleep(forTimeInterval: 0.001) - self.semaphore.signal() }) self.semaphore.wait() diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift index 480a78a79b..cd9c61ebf0 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift @@ -641,13 +641,13 @@ final class VideoInputScalePass: RenderPass { } func process(input: MTLTexture, secondInput: MTLTexture?, timestamp: CMTime, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? { -#if targetEnvironment(simulator) - -#else +//#if targetEnvironment(simulator) +// +//#else guard max(input.width, input.height) > 1920 || secondInput != nil else { return input } -#endif +//#endif let scaledSize = CGSize(width: input.width, height: input.height).fitted(CGSize(width: 1920.0, height: 1920.0)) let width: Int @@ -695,9 +695,9 @@ final class VideoInputScalePass: RenderPass { renderCommandEncoder.setRenderPipelineState(self.mainPipelineState!) -#if targetEnvironment(simulator) - let secondInput = input -#endif +//#if targetEnvironment(simulator) +// let secondInput = input +//#endif let (mainVideoState, additionalVideoState, transitionVideoState) = self.transitionState(for: timestamp, mainInput: input, additionalInput: secondInput) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index a186c5894e..6c8024f4d9 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3016,7 +3016,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate fileprivate let transitionOut: (Bool, Bool?) -> TransitionOut? public var cancelled: (Bool) -> Void = { _ in } - public var completion: (Int64, MediaEditorScreen.Result?, NSAttributedString, MediaEditorResultPrivacy , @escaping (@escaping () -> Void) -> Void) -> Void = { _, _, _, _, _ in } + public var completion: (Int64, MediaEditorScreen.Result?, NSAttributedString, MediaEditorResultPrivacy, [TelegramMediaFile], @escaping (@escaping () -> Void) -> Void) -> Void = { _, _, _, _, _, _ in } public var dismissed: () -> Void = { } public var willDismiss: () -> Void = { } @@ -3031,7 +3031,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate initialVideoPosition: Double? = nil, transitionIn: TransitionIn?, transitionOut: @escaping (Bool, Bool?) -> TransitionOut?, - completion: @escaping (Int64, MediaEditorScreen.Result?, NSAttributedString, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void + completion: @escaping (Int64, MediaEditorScreen.Result?, NSAttributedString, MediaEditorResultPrivacy, [TelegramMediaFile], @escaping (@escaping () -> Void) -> Void) -> Void ) { self.context = context self.subject = subject @@ -3484,8 +3484,22 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate randomId = Int64.random(in: .min ... .max) } + var stickers: [TelegramMediaFile] = [] + for entity in codableEntities { + if case let .sticker(stickerEntity) = entity, case let .file(file) = stickerEntity.content { + stickers.append(file) + if let subEntities = stickerEntity.renderSubEntities { + for entity in subEntities { + if let stickerEntity = entity as? DrawingStickerEntity, case let .file(file) = stickerEntity.content { + stickers.append(file) + } + } + } + } + } + if self.isEditingStory && !self.node.hasAnyChanges { - self.completion(randomId, nil, caption, self.state.privacy, { [weak self] finished in + self.completion(randomId, nil, caption, self.state.privacy, stickers, { [weak self] finished in self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in self?.dismiss() Queue.mainQueue().justDispatch { @@ -3616,7 +3630,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 { - self.completion(randomId, .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), caption, self.state.privacy, { [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?.dismiss() Queue.mainQueue().justDispatch { @@ -3638,7 +3652,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 { - self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), caption, self.state.privacy, { [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?.dismiss() Queue.mainQueue().justDispatch { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index e31aadb307..e9e10b54d3 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -2809,7 +2809,7 @@ public final class StoryItemSetContainerComponent: Component { initialVideoPosition: videoPlaybackPosition, transitionIn: nil, transitionOut: { _, _ in return nil }, - completion: { [weak self] _, mediaResult, caption, privacy, commit in + completion: { [weak self] _, mediaResult, caption, privacy, stickers, commit in guard let self else { return } @@ -2831,7 +2831,7 @@ public final class StoryItemSetContainerComponent: Component { updateProgressImpl?(0.0) if let imageData = compressImageToJPEG(image, quality: 0.7) { - let _ = (context.engine.messages.editStory(media: .image(dimensions: dimensions, data: imageData), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) + let _ = (context.engine.messages.editStory(media: .image(dimensions: dimensions, data: imageData, stickers: stickers), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) |> deliverOnMainQueue).start(next: { [weak self] result in guard let self else { return @@ -2870,7 +2870,7 @@ public final class StoryItemSetContainerComponent: Component { } let firstFrameImageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) } - let _ = (context.engine.messages.editStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: firstFrameImageData), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) + let _ = (context.engine.messages.editStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: firstFrameImageData, stickers: stickers), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) |> deliverOnMainQueue).start(next: { [weak self] result in guard let self else { return @@ -2990,6 +2990,14 @@ public final class StoryItemSetContainerComponent: Component { let additionalCount = component.slice.item.storyItem.privacy?.additionallyIncludePeers.count ?? 0 + var hasLinkedStickers = false + let media = component.slice.item.storyItem.media._asMedia() + if let image = media as? TelegramMediaImage { + hasLinkedStickers = image.flags.contains(.hasStickers) + } else if let file = media as? TelegramMediaFile { + hasLinkedStickers = file.hasLinkedStickers + } + let privacyText: String switch component.slice.item.storyItem.privacy?.base { case .closeFriends: @@ -3126,6 +3134,8 @@ public final class StoryItemSetContainerComponent: Component { self.sendMessageContext.performShareAction(view: self) }))) } + + let _ = hasLinkedStickers let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/Contents.json index ffc2f05f85..89b3ac9ee6 100644 --- a/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "arrow_left.pdf", + "filename" : "ic_next.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/arrow_left.pdf b/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/arrow_left.pdf deleted file mode 100644 index 7b20434672..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/arrow_left.pdf and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/ic_next.pdf b/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/ic_next.pdf new file mode 100644 index 0000000000..58537192a4 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/ic_next.pdf @@ -0,0 +1,92 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +0.000000 -1.000000 1.000000 0.000000 -6.195312 14.000000 cm +1.000000 1.000000 1.000000 scn +-0.707107 8.902419 m +-1.097631 8.511895 -1.097631 7.878730 -0.707107 7.488206 c +-0.316583 7.097682 0.316583 7.097682 0.707107 7.488206 c +-0.707107 8.902419 l +h +6.000000 14.195312 m +6.707107 14.902419 l +6.316583 15.292944 5.683417 15.292944 5.292893 14.902419 c +6.000000 14.195312 l +h +11.292893 7.488206 m +11.683417 7.097682 12.316583 7.097682 12.707107 7.488206 c +13.097631 7.878730 13.097631 8.511895 12.707107 8.902419 c +11.292893 7.488206 l +h +0.707107 7.488206 m +6.707107 13.488206 l +5.292893 14.902419 l +-0.707107 8.902419 l +0.707107 7.488206 l +h +5.292893 13.488206 m +11.292893 7.488206 l +12.707107 8.902419 l +6.707107 14.902419 l +5.292893 13.488206 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 785 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 10.000000 16.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000000875 00000 n +0000000897 00000 n +0000001070 00000 n +0000001144 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1203 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index b5d2010422..4e10eb5517 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -354,7 +354,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } else { return nil } - }, completion: { [weak self] randomId, mediaResult, caption, privacy, commit in + }, completion: { [weak self] randomId, mediaResult, caption, privacy, stickers, commit in guard let self, let mediaResult else { dismissCameraImpl?() commit({}) @@ -373,7 +373,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon case let .image(image, dimensions): if let imageData = compressImageToJPEG(image, quality: 0.7) { let entities = generateChatInputTextEntities(caption) - self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), 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 { commit({}) } @@ -396,7 +396,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) } let entities = generateChatInputTextEntities(caption) - self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: imageData), 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, firstFrameImageData: 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({}) }