diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 47151fb452..3c593ac9f0 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -11903,3 +11903,6 @@ Sorry for the inconvenience."; "Conversation.StickerRemovedFromRecent" = "Sticker was removed from Recents."; "Conversation.ViewStickers" = "VIEW STICKERS"; +"Conversation.ViewEmojis" = "VIEW EMOJIS"; + +"MediaEditor.StickersTooMuch" = "Sorry, you've reached the maximum number of stickers in this set. Try a different one."; diff --git a/submodules/ImportStickerPackUI/Sources/ImportStickerPackController.swift b/submodules/ImportStickerPackUI/Sources/ImportStickerPackController.swift index dff3fadfcc..a3792b02b7 100644 --- a/submodules/ImportStickerPackUI/Sources/ImportStickerPackController.swift +++ b/submodules/ImportStickerPackUI/Sources/ImportStickerPackController.swift @@ -89,7 +89,7 @@ public final class ImportStickerPackController: ViewController, StandalonePresen var signals: [Signal<(UUID, StickerVerificationStatus, EngineMediaResource?), NoError>] = [] for sticker in strongSelf.stickerPack.stickers { if let resource = strongSelf.controllerNode.stickerResources[sticker.uuid] { - signals.append(strongSelf.context.engine.stickers.uploadSticker(peer: peer, resource: resource._asResource(), alt: sticker.emojis.first ?? "", dimensions: PixelDimensions(width: 512, height: 512), duration: nil, mimeType: sticker.mimeType) + signals.append(strongSelf.context.engine.stickers.uploadSticker(peer: peer, resource: resource._asResource(), thumbnail: nil, alt: sticker.emojis.first ?? "", dimensions: PixelDimensions(width: 512, height: 512), duration: nil, mimeType: sticker.mimeType) |> map { result -> (UUID, StickerVerificationStatus, EngineMediaResource?) in switch result { case .progress: diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 76a89b99b0..e6386b878a 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -2186,6 +2186,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } @objc private func searchOrMorePressed(node: ContextReferenceContentNode, gesture: ContextGesture?) { + guard self.moreButtonNode.iconNode.alpha > 0.0 else { + return + } let strings = self.presentationData.strings if case let .assets(_, mode) = self.subject, [.createSticker].contains(mode) { var items: [ContextMenuItem] = [] diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift index a981865d2b..e98d2366d8 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift @@ -314,7 +314,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode { if stickerItem.file.isAnimatedSticker || stickerItem.file.isVideoSticker { let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512) if stickerItem.file.isVideoSticker { - self.imageNode.setSignal(chatMessageSticker(account: context.account, userLocation: .other, file: stickerItem.file, small: true)) + self.imageNode.setSignal(chatMessageSticker(account: context.account, userLocation: .other, file: stickerItem.file, small: true, fetched: true)) } else { self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: context.account.postbox, userLocation: .other, file: stickerItem.file, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)))) } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 5d2a9aada7..a8d521cc7a 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -580,10 +580,16 @@ private final class StickerPackContainer: ASDisplayNode { if let self, let (info, items, installed) = self.currentStickerPack { let updatedItems = items.filter { $0.file.fileId != item.file.fileId } - self.currentStickerPack = (info, updatedItems, installed) - self.reorderAndUpdateEntries() - - let _ = self.context.engine.stickers.deleteStickerFromStickerSet(sticker: .stickerPack(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), media: item.file)).startStandalone() + if updatedItems.isEmpty { + let _ = (self.context.engine.stickers.deleteStickerSet(packReference: .id(id: info.id.id, accessHash: info.accessHash)) + |> deliverOnMainQueue).startStandalone() + + self.controller?.controllerNode.dismiss() + } else { + self.currentStickerPack = (info, updatedItems, installed) + self.reorderAndUpdateEntries() + let _ = self.context.engine.stickers.deleteStickerFromStickerSet(sticker: .stickerPack(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), media: item.file)).startStandalone() + } } })) ] @@ -1250,6 +1256,7 @@ private final class StickerPackContainer: ASDisplayNode { let presentationData = self.presentationData let updatedPresentationData = self.controller?.updatedPresentationData let navigationController = self.controller?.parentNavigationController as? NavigationController + let sendSticker = self.controller?.sendSticker var dismissImpl: (() -> Void)? let mainController = context.sharedContext.makeStickerMediaPickerScreen( @@ -1275,7 +1282,7 @@ private final class StickerPackContainer: ASDisplayNode { |> deliverOnMainQueue).start(completed: { commit() - let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) + let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) Queue.mainQueue().after(0.1) { @@ -1301,6 +1308,7 @@ private final class StickerPackContainer: ASDisplayNode { let presentationData = self.presentationData let updatedPresentationData = self.controller?.updatedPresentationData let navigationController = self.controller?.parentNavigationController as? NavigationController + let sendSticker = self.controller?.sendSticker let context = self.context let controller = self.context.sharedContext.makeStickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData, completion: { file in @@ -1323,7 +1331,7 @@ private final class StickerPackContainer: ASDisplayNode { let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) let _ = (context.engine.stickers.addStickerToStickerSet(packReference: packReference, sticker: sticker) |> deliverOnMainQueue).start(completed: { - let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) + let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) Queue.mainQueue().after(0.1) { @@ -1345,8 +1353,10 @@ private final class StickerPackContainer: ASDisplayNode { } let context = self.context + let presentationData = self.presentationData let updatedPresentationData = self.controller?.updatedPresentationData let navigationController = self.controller?.parentNavigationController as? NavigationController + let sendSticker = self.controller?.sendSticker self.controller?.dismiss() @@ -1369,8 +1379,12 @@ private final class StickerPackContainer: ASDisplayNode { |> deliverOnMainQueue).start(completed: { commit() - let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) + let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) + + Queue.mainQueue().after(0.1) { + packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: "Sticker updated.", undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) + } }) } ) @@ -2162,7 +2176,7 @@ private final class StickerPackContainer: ASDisplayNode { private func expandIfNeeded(force: Bool = false) { if self.currentEntries.count >= 15, force || (self.controller?.expandIfNeeded == true && !self.didAutomaticExpansion) { self.didAutomaticExpansion = true - self.gridNode.autoscroll(toOffset: CGPoint(x: 0.0, y: max(0.0, self.gridNode.scrollView.contentSize.height - self.gridNode.scrollView.contentInset.top - self.gridNode.scrollView.bounds.height)), duration: 0.4) + self.gridNode.autoscroll(toOffset: CGPoint(x: 0.0, y: max(0.0, self.gridNode.scrollView.contentSize.height + self.gridNode.scrollView.contentInset.bottom - self.gridNode.scrollView.bounds.height)), duration: 0.4) self.skipNextGridLayoutUpdate = true } } @@ -2645,7 +2659,7 @@ public final class StickerPackScreenImpl: ViewController, StickerPackScreen { private let initialSelectedStickerPackIndex: Int fileprivate weak var parentNavigationController: NavigationController? - private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? + fileprivate let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? private let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)? fileprivate var controllerNode: StickerPackScreenNode { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift index c107e8635e..b1539aa49b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift @@ -33,13 +33,23 @@ private func uploadedSticker(postbox: Postbox, network: Network, resource: Media } } -func _internal_uploadSticker(account: Account, peer: Peer, resource: MediaResource, alt: String, dimensions: PixelDimensions, duration: Double?, mimeType: String) -> Signal { +func _internal_uploadSticker(account: Account, peer: Peer, resource: MediaResource, thumbnail: MediaResource? = nil, alt: String, dimensions: PixelDimensions, duration: Double?, mimeType: String) -> Signal { guard let inputPeer = apiInputPeer(peer) else { return .fail(.generic) } - return uploadedSticker(postbox: account.postbox, network: account.network, resource: resource) + + let uploadSticker = uploadedSticker(postbox: account.postbox, network: account.network, resource: resource) + let uploadThumbnail: Signal + if let thumbnail { + uploadThumbnail = uploadedSticker(postbox: account.postbox, network: account.network, resource: thumbnail) + |> map(Optional.init) + } else { + uploadThumbnail = .single(nil) + } + + return combineLatest(uploadSticker, uploadThumbnail) |> mapError { _ -> UploadStickerError in } - |> mapToSignal { result -> Signal in + |> mapToSignal { result, thumbnailResult -> Signal in switch result.content { case .error: return .fail(.generic) @@ -48,25 +58,47 @@ func _internal_uploadSticker(account: Account, peer: Peer, resource: MediaResour case let .progress(progress): return .single(.progress(progress)) case let .inputFile(file): - let flags: Int32 = 0 - var attributes: [Api.DocumentAttribute] = [] - attributes.append(.documentAttributeSticker(flags: 0, alt: alt, stickerset: .inputStickerSetEmpty, maskCoords: nil)) - if let duration { - attributes.append(.documentAttributeVideo(flags: 0, duration: duration, w: dimensions.width, h: dimensions.height, preloadPrefixSize: nil)) + var ready = false + var thumbnailFile: Api.InputFile? + if thumbnailResult == nil { + ready = true + } else if let thumbnailResult = thumbnailResult { + if case let .result(thumbnailResultData) = thumbnailResult.content { + if case let .inputFile(file) = thumbnailResultData { + ready = true + thumbnailFile = file + } + } else { + ready = true + } } - attributes.append(.documentAttributeImageSize(w: dimensions.width, h: dimensions.height)) - return account.network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: file, thumb: nil, mimeType: mimeType, attributes: attributes, stickers: nil, ttlSeconds: nil))) - |> mapError { _ -> UploadStickerError in return .generic } - |> mapToSignal { media -> Signal in - switch media { + if ready { + var flags: Int32 = 0 + if let _ = thumbnailFile { + flags |= (1 << 2) + } + var attributes: [Api.DocumentAttribute] = [] + attributes.append(.documentAttributeSticker(flags: 0, alt: alt, stickerset: .inputStickerSetEmpty, maskCoords: nil)) + if let duration { + attributes.append(.documentAttributeVideo(flags: 0, duration: duration, w: dimensions.width, h: dimensions.height, preloadPrefixSize: nil)) + } + attributes.append(.documentAttributeImageSize(w: dimensions.width, h: dimensions.height)) + return account.network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: file, thumb: thumbnailFile, mimeType: mimeType, attributes: attributes, stickers: nil, ttlSeconds: nil))) + |> mapError { _ -> UploadStickerError in return .generic } + |> mapToSignal { media -> Signal in + switch media { case let .messageMediaDocument(_, document, _, _): - if let document = document, let file = telegramMediaFileFromApiDocument(document), let resource = file.resource as? CloudDocumentMediaResource { - return .single(.complete(resource, file.mimeType)) + if let document = document, let file = telegramMediaFileFromApiDocument(document), let uploadedResource = file.resource as? CloudDocumentMediaResource { + account.postbox.mediaBox.copyResourceData(from: resource.id, to: uploadedResource.id, synchronous: true) + return .single(.complete(uploadedResource, file.mimeType)) } default: break + } + return .fail(.generic) } - return .fail(.generic) + } else { + return .single(.progress(1.0)) } default: return .fail(.generic) @@ -81,14 +113,16 @@ public enum CreateStickerSetError { public struct ImportSticker { public let resource: MediaResourceReference + public let thumbnailResource: MediaResourceReference? let emojis: [String] public let dimensions: PixelDimensions public let duration: Double? public let mimeType: String public let keywords: String - public init(resource: MediaResourceReference, emojis: [String], dimensions: PixelDimensions, duration: Double?, mimeType: String, keywords: String) { + public init(resource: MediaResourceReference, thumbnailResource: MediaResourceReference? = nil, emojis: [String], dimensions: PixelDimensions, duration: Double?, mimeType: String, keywords: String) { self.resource = resource + self.thumbnailResource = thumbnailResource self.emojis = emojis self.dimensions = dimensions self.duration = duration @@ -116,7 +150,13 @@ public extension ImportSticker { fileAttributes.append(.FileName(fileName: "sticker.webp")) } fileAttributes.append(.ImageSize(size: self.dimensions)) - return StickerPackItem(index: ItemCollectionItemIndex(index: 0, id: 0), file: TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 0), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: self.mimeType, size: nil, attributes: fileAttributes), indexKeys: []) + + var previewRepresentations: [TelegramMediaImageRepresentation] = [] + if let thumbnailResource = self.thumbnailResource?.resource as? TelegramMediaResource { + previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 320, height: 320), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil)) + } + + return StickerPackItem(index: ItemCollectionItemIndex(index: 0, id: 0), file: TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 0), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: self.mimeType, size: nil, attributes: fileAttributes), indexKeys: []) } } @@ -159,7 +199,7 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri if let resource = sticker.resource.resource as? CloudDocumentMediaResource { uploadStickers.append(.single(.complete(resource, sticker.mimeType))) } else { - uploadStickers.append(_internal_uploadSticker(account: account, peer: peer, resource: sticker.resource.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, duration: sticker.duration, mimeType: sticker.mimeType) + uploadStickers.append(_internal_uploadSticker(account: account, peer: peer, resource: sticker.resource.resource, thumbnail: sticker.thumbnailResource?.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, duration: sticker.duration, mimeType: sticker.mimeType) |> mapError { _ -> CreateStickerSetError in return .generic }) @@ -306,7 +346,7 @@ func _internal_addStickerToStickerSet(account: Account, packReference: StickerPa uploadSticker = account.postbox.loadedPeerWithId(account.peerId) |> castError(AddStickerToSetError.self) |> mapToSignal { peer in - return _internal_uploadSticker(account: account, peer: peer, resource: sticker.resource.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, duration: sticker.duration, mimeType: sticker.mimeType) + return _internal_uploadSticker(account: account, peer: peer, resource: sticker.resource.resource, thumbnail: sticker.thumbnailResource?.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, duration: sticker.duration, mimeType: sticker.mimeType) |> mapError { _ -> AddStickerToSetError in return .generic } @@ -434,7 +474,7 @@ func _internal_replaceSticker(account: Account, previousSticker: FileMediaRefere uploadSticker = account.postbox.loadedPeerWithId(account.peerId) |> castError(ReplaceStickerError.self) |> mapToSignal { peer in - return _internal_uploadSticker(account: account, peer: peer, resource: sticker.resource.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, duration: sticker.duration, mimeType: sticker.mimeType) + return _internal_uploadSticker(account: account, peer: peer, resource: sticker.resource.resource, thumbnail: sticker.thumbnailResource?.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, duration: sticker.duration, mimeType: sticker.mimeType) |> mapError { _ -> ReplaceStickerError in return .generic } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift index 2f2005e228..a29f884883 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -78,8 +78,8 @@ public extension TelegramEngine { return _internal_stickerPacksAttachedToMedia(account: self.account, media: media) } - public func uploadSticker(peer: Peer, resource: MediaResource, alt: String, dimensions: PixelDimensions, duration: Double?, mimeType: String) -> Signal { - return _internal_uploadSticker(account: self.account, peer: peer, resource: resource, alt: alt, dimensions: dimensions, duration: duration, mimeType: mimeType) + public func uploadSticker(peer: Peer, resource: MediaResource, thumbnail: MediaResource?, alt: String, dimensions: PixelDimensions, duration: Double?, mimeType: String) -> Signal { + return _internal_uploadSticker(account: self.account, peer: peer, resource: resource, thumbnail: thumbnail, alt: alt, dimensions: dimensions, duration: duration, mimeType: mimeType) } public func createStickerSet(title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, type: CreateStickerSetType, software: String?) -> Signal { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 0039683647..376bd60be9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -162,7 +162,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { self.activateBadgeAction?() } - public typealias AsyncLayout = (_ presentationData: ChatPresentationData, _ automaticDownloadSettings: MediaAutoDownloadSettings, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ context: AccountContext, _ controllerInteraction: ChatControllerInteraction, _ message: Message, _ messageRead: Bool, _ chatLocation: ChatLocation, _ title: String?, _ titleBadge: String?, _ subtitle: NSAttributedString?, _ text: String?, _ entities: [MessageTextEntity]?, _ media: (Media, ChatMessageAttachedContentNodeMediaFlags)?, _ mediaBadge: String?, _ actionIcon: ChatMessageAttachedContentActionIcon?, _ actionTitle: String?, _ displayLine: Bool, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ constrainedSize: CGSize, _ animationCache: AnimationCache, _ animationRenderer: MultiAnimationRenderer) -> (CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) + public typealias AsyncLayout = (_ presentationData: ChatPresentationData, _ automaticDownloadSettings: MediaAutoDownloadSettings, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ context: AccountContext, _ controllerInteraction: ChatControllerInteraction, _ message: Message, _ messageRead: Bool, _ chatLocation: ChatLocation, _ title: String?, _ titleBadge: String?, _ subtitle: NSAttributedString?, _ text: String?, _ entities: [MessageTextEntity]?, _ media: ([Media], ChatMessageAttachedContentNodeMediaFlags)?, _ mediaBadge: String?, _ actionIcon: ChatMessageAttachedContentActionIcon?, _ actionTitle: String?, _ displayLine: Bool, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ constrainedSize: CGSize, _ animationCache: AnimationCache, _ animationRenderer: MultiAnimationRenderer) -> (CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) public func makeProgress() -> Promise { let progress = Promise() @@ -302,7 +302,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { var mediaAndFlags = mediaAndFlags if let mediaAndFlagsValue = mediaAndFlags { - if mediaAndFlagsValue.0 is TelegramMediaStory || mediaAndFlagsValue.0 is WallpaperPreviewMedia { + if mediaAndFlagsValue.0.first is TelegramMediaStory || mediaAndFlagsValue.0.first is WallpaperPreviewMedia { var flags = mediaAndFlagsValue.1 flags.remove(.preferMediaInline) mediaAndFlags = (mediaAndFlagsValue.0, flags) @@ -315,50 +315,52 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } var contentMediaInline = false - if let (media, flags) = mediaAndFlags { + if let (mediaArray, flags) = mediaAndFlags { contentMediaInline = flags.contains(.preferMediaInline) - if let file = media as? TelegramMediaFile { - if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 { - contentMediaValue = file - } else if file.isInstantVideo { - contentMediaValue = file - } else if file.isVideo { - contentMediaValue = file - } else if file.isSticker || file.isAnimatedSticker { - contentMediaValue = file - } else { - contentFileValue = file - } - - if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file) { - contentMediaAutomaticDownload = .full - } else if shouldPredownloadMedia(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file) { - contentMediaAutomaticDownload = .prefetch - } - - if file.isAnimated { - contentMediaAutomaticPlayback = context.sharedContext.energyUsageSettings.autoplayGif - } else if file.isVideo && context.sharedContext.energyUsageSettings.autoplayVideo { - var willDownloadOrLocal = false - if case .full = contentMediaAutomaticDownload { - willDownloadOrLocal = true + if let media = mediaArray.first { + if let file = media as? TelegramMediaFile { + if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 { + contentMediaValue = file + } else if file.isInstantVideo { + contentMediaValue = file + } else if file.isVideo { + contentMediaValue = file + } else if file.isSticker || file.isAnimatedSticker { + contentMediaValue = file } else { - willDownloadOrLocal = context.account.postbox.mediaBox.completedResourcePath(file.resource) != nil + contentFileValue = file } - if willDownloadOrLocal { - contentMediaAutomaticPlayback = true - contentMediaAspectFilled = true + + if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file) { + contentMediaAutomaticDownload = .full + } else if shouldPredownloadMedia(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file) { + contentMediaAutomaticDownload = .prefetch } + + if file.isAnimated { + contentMediaAutomaticPlayback = context.sharedContext.energyUsageSettings.autoplayGif + } else if file.isVideo && context.sharedContext.energyUsageSettings.autoplayVideo { + var willDownloadOrLocal = false + if case .full = contentMediaAutomaticDownload { + willDownloadOrLocal = true + } else { + willDownloadOrLocal = context.account.postbox.mediaBox.completedResourcePath(file.resource) != nil + } + if willDownloadOrLocal { + contentMediaAutomaticPlayback = true + contentMediaAspectFilled = true + } + } + } else if let _ = media as? TelegramMediaImage { + contentMediaValue = media + } else if let _ = media as? TelegramMediaWebFile { + contentMediaValue = media + } else if let _ = media as? WallpaperPreviewMedia { + contentMediaValue = media + } else if let _ = media as? TelegramMediaStory { + contentMediaValue = media } - } else if let _ = media as? TelegramMediaImage { - contentMediaValue = media - } else if let _ = media as? TelegramMediaWebFile { - contentMediaValue = media - } else if let _ = media as? WallpaperPreviewMedia { - contentMediaValue = media - } else if let _ = media as? TelegramMediaStory { - contentMediaValue = media } } @@ -939,7 +941,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { self.context = context self.message = message - self.media = mediaAndFlags?.0 + self.media = mediaAndFlags?.0.first self.theme = presentationData.theme self.mainColor = mainColor diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift index 7da7c1c32c..d17580bb8f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift @@ -50,7 +50,7 @@ public final class ChatMessageEventLogPreviousDescriptionContentNode: ChatMessag } else { text = item.message.text } - let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil + let mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? = nil let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize, item.controllerInteraction.presentationContext.animationCache, item.controllerInteraction.presentationContext.animationRenderer) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift index 7588af15ed..0b420f0c7f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift @@ -45,7 +45,7 @@ public final class ChatMessageEventLogPreviousLinkContentNode: ChatMessageBubble let title: String = item.message.text.contains("\n") ? item.presentationData.strings.Channel_AdminLog_MessagePreviousLinks : item.presentationData.strings.Channel_AdminLog_MessagePreviousLink let text: String = item.message.text - let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil + let mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? = nil let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize, item.controllerInteraction.presentationContext.animationCache, item.controllerInteraction.presentationContext.animationRenderer) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift index 79975870a3..d3ec9d1355 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift @@ -50,7 +50,7 @@ public final class ChatMessageEventLogPreviousMessageContentNode: ChatMessageBub } else { text = item.message.text } - let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil + let mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? = nil let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize, item.controllerInteraction.presentationContext.animationCache, item.controllerInteraction.presentationContext.animationRenderer) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift index b93e0ea81c..b4974c403a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift @@ -67,16 +67,16 @@ public final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNod var title: String? var text: String? - var mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? + var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? if let game = game { title = game.title text = game.description if let file = game.file { - mediaAndFlags = (file, [.preferMediaBeforeText]) + mediaAndFlags = ([file], [.preferMediaBeforeText]) } else if let image = game.image { - mediaAndFlags = (image, [.preferMediaBeforeText]) + mediaAndFlags = ([image], [.preferMediaBeforeText]) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift index c78beea3bc..b46926dc47 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift @@ -52,7 +52,7 @@ public final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContent var title: String? var subtitle: NSAttributedString? = nil var text: String? - var mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? + var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? var automaticDownloadSettings = item.controllerInteraction.automaticMediaDownloadSettings if let invoice = invoice { @@ -61,7 +61,7 @@ public final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContent if let image = invoice.photo { automaticDownloadSettings = MediaAutoDownloadSettings.defaultSettings - mediaAndFlags = (image, [.preferMediaBeforeText]) + mediaAndFlags = ([image], [.preferMediaBeforeText]) } else { let invoiceLabel = item.presentationData.strings.Message_InvoiceLabel var invoiceText = "\(formatCurrencyAmount(invoice.totalAmount, currency: invoice.currency)) " diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift index bbc8da69bb..d0c6cec2db 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -242,7 +242,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent var text: String? var entities: [MessageTextEntity]? var titleBadge: String? - var mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? + var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? var badge: String? var actionIcon: ChatMessageAttachedContentActionIcon? @@ -307,9 +307,9 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent if let file = mainMedia as? TelegramMediaFile, webpage.type != "telegram_theme" { if let embedUrl = webpage.embedUrl, !embedUrl.isEmpty { if automaticPlayback { - mediaAndFlags = (file, [.preferMediaBeforeText]) + mediaAndFlags = ([file], [.preferMediaBeforeText]) } else { - mediaAndFlags = (webpage.image ?? file, [.preferMediaBeforeText]) + mediaAndFlags = ([webpage.image ?? file], [.preferMediaBeforeText]) } } else if webpage.type == "telegram_background" { var colors: [UInt32] = [] @@ -321,12 +321,12 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent intensity = intensityValue } let media = WallpaperPreviewMedia(content: .file(file: file, colors: colors, rotation: rotation, intensity: intensity, false, false)) - mediaAndFlags = (media, [.preferMediaAspectFilled]) + mediaAndFlags = ([media], [.preferMediaAspectFilled]) if let fileSize = file.size { badge = dataSizeString(fileSize, formatting: DataSizeStringFormatting(chatPresentationData: item.presentationData)) } } else { - mediaAndFlags = (file, []) + mediaAndFlags = ([file], []) } } else if let image = mainMedia as? TelegramMediaImage { if let type = webpage.type, ["photo", "video", "embed", "gif", "document", "telegram_album"].contains(type) { @@ -338,13 +338,13 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } else if let embedUrl = webpage.embedUrl, !embedUrl.isEmpty { flags.insert(.preferMediaBeforeText) } - mediaAndFlags = (image, flags) + mediaAndFlags = ([image], flags) } else if let _ = largestImageRepresentation(image.representations)?.dimensions { let flags = ChatMessageAttachedContentNodeMediaFlags() - mediaAndFlags = (image, flags) + mediaAndFlags = ([image], flags) } } else if let story = mainMedia as? TelegramMediaStory { - mediaAndFlags = (story, [.preferMediaBeforeText, .titleBeforeMedia]) + mediaAndFlags = ([story], [.preferMediaBeforeText, .titleBeforeMedia]) if let storyItem = item.message.associatedStories[story.storyId]?.get(Stories.StoredItem.self), case let .item(itemValue) = storyItem { text = itemValue.text entities = itemValue.entities @@ -372,7 +372,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } if let content = content { let media = WallpaperPreviewMedia(content: content) - mediaAndFlags = (media, []) + mediaAndFlags = ([media], []) } } else if type == "telegram_theme" { var file: TelegramMediaFile? @@ -397,10 +397,10 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } if let file = file { let media = WallpaperPreviewMedia(content: .file(file: file, colors: [], rotation: nil, intensity: nil, true, isSupported)) - mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags()) + mediaAndFlags = ([media], ChatMessageAttachedContentNodeMediaFlags()) } else if let settings = settings { let media = WallpaperPreviewMedia(content: .themeSettings(settings)) - mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags()) + mediaAndFlags = ([media], ChatMessageAttachedContentNodeMediaFlags()) } } } @@ -467,11 +467,24 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent case "telegram_group_boost": actionTitle = item.presentationData.strings.Conversation_BoostChannel case "telegram_stickerset": - actionTitle = item.presentationData.strings.Conversation_ViewStickers + var isEmoji = false + for attribute in webpage.attributes { + if case let .stickerPack(stickerPack) = attribute { + isEmoji = stickerPack.flags.contains(.isEmoji) + break + } + } + actionTitle = isEmoji ? item.presentationData.strings.Conversation_ViewEmojis : item.presentationData.strings.Conversation_ViewStickers default: break } } + for attribute in webpage.attributes { + if case let .stickerPack(stickerPack) = attribute, !stickerPack.files.isEmpty { + mediaAndFlags = (stickerPack.files, .preferMediaInline) + break + } + } if defaultWebpageImageSizeIsSmall(webpage: webpage) { mediaAndFlags?.1.insert(.preferMediaInline) @@ -507,7 +520,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent for media in item.message.media { switch media { case _ as TelegramMediaImage, _ as TelegramMediaFile, _ as TelegramMediaStory: - mediaAndFlags = (media, [.preferMediaInline]) + mediaAndFlags = ([media], [.preferMediaInline]) default: break } diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 477b16b599..2aedcd6da1 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -160,7 +160,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { return hasPremium } - public static func inputData(context: AccountContext, chatPeerId: PeerId?, areCustomEmojiEnabled: Bool, hasTrending: Bool = true, hasSearch: Bool = true, hideBackground: Bool = false, sendGif: ((FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool)?) -> Signal { + public static func inputData(context: AccountContext, chatPeerId: PeerId?, areCustomEmojiEnabled: Bool, hasEdit: Bool = false, hasTrending: Bool = true, hasSearch: Bool = true, hideBackground: Bool = false, sendGif: ((FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool)?) -> Signal { let animationCache = context.animationCache let animationRenderer = context.animationRenderer @@ -184,7 +184,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings - let stickerItems = EmojiPagerContentComponent.stickerInputData(context: context, animationCache: animationCache, animationRenderer: animationRenderer, stickerNamespaces: stickerNamespaces, stickerOrderedItemListCollectionIds: stickerOrderedItemListCollectionIds, chatPeerId: chatPeerId, hasSearch: hasSearch, hasTrending: hasTrending, forceHasPremium: false, hasEdit: GlobalExperimentalSettings.enableWIPStickers, hideBackground: hideBackground) + let stickerItems = EmojiPagerContentComponent.stickerInputData(context: context, animationCache: animationCache, animationRenderer: animationRenderer, stickerNamespaces: stickerNamespaces, stickerOrderedItemListCollectionIds: stickerOrderedItemListCollectionIds, chatPeerId: chatPeerId, hasSearch: hasSearch, hasTrending: hasTrending, forceHasPremium: false, hasEdit: hasEdit, hideBackground: hideBackground) let reactions: Signal<[String], NoError> = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App()) |> map { appConfiguration -> [String] in diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift index 2a94a06a80..fd72005c91 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift @@ -441,6 +441,7 @@ public final class MediaEditor { additionalVideoVolume: nil, nightTheme: false, drawing: nil, + maskDrawing: nil, entities: [], toolValues: [:], audioTrack: nil, diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift index 328bdbcf4e..0976516301 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift @@ -121,7 +121,7 @@ final class MediaEditorComposer { } else { self.drawingImage = nil } - + var entities: [MediaEditorComposerEntity] = [] for entity in values.entities { entities.append(contentsOf: composerEntitiesForDrawingEntity(postbox: postbox, textScale: textScale, entity: entity.entity, colorSpace: colorSpace)) @@ -136,6 +136,10 @@ final class MediaEditorComposer { } else { self.ciContext = nil } + + if let maskDrawing = values.maskDrawing, let device = self.device, let maskTexture = loadTexture(image: maskDrawing, device: device) { + self.renderer.currentMainInputMask = maskTexture + } self.renderer.setupForComposer(composer: self) self.renderChain.update(values: self.values) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift index 5c738d60f8..417c9b2cf1 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift @@ -303,6 +303,9 @@ public final class MediaEditorValues: Codable, Equatable { if lhs.drawing !== rhs.drawing { return false } + if lhs.maskDrawing !== rhs.maskDrawing { + return false + } if lhs.entities != rhs.entities { return false } @@ -384,6 +387,7 @@ public final class MediaEditorValues: Codable, Equatable { case nightTheme case drawing + case maskDrawing case entities case toolValues case audioTrack @@ -424,6 +428,7 @@ public final class MediaEditorValues: Codable, Equatable { public let nightTheme: Bool public let drawing: UIImage? + public let maskDrawing: UIImage? public let entities: [CodableDrawingEntity] public let toolValues: [EditorToolKey: Any] @@ -469,6 +474,7 @@ public final class MediaEditorValues: Codable, Equatable { additionalVideoVolume: CGFloat?, nightTheme: Bool, drawing: UIImage?, + maskDrawing: UIImage?, entities: [CodableDrawingEntity], toolValues: [EditorToolKey: Any], audioTrack: MediaAudioTrack?, @@ -503,6 +509,7 @@ public final class MediaEditorValues: Codable, Equatable { self.additionalVideoVolume = additionalVideoVolume self.nightTheme = nightTheme self.drawing = drawing + self.maskDrawing = maskDrawing self.entities = entities self.toolValues = toolValues self.audioTrack = audioTrack @@ -557,6 +564,11 @@ public final class MediaEditorValues: Codable, Equatable { } else { self.drawing = nil } + if let drawingData = try container.decodeIfPresent(Data.self, forKey: .maskDrawing), let image = UIImage(data: drawingData) { + self.maskDrawing = image + } else { + self.maskDrawing = nil + } self.entities = try container.decode([CodableDrawingEntity].self, forKey: .entities) @@ -617,6 +629,9 @@ public final class MediaEditorValues: Codable, Equatable { if let drawing = self.drawing, let pngDrawingData = drawing.pngData() { try container.encode(pngDrawingData, forKey: .drawing) } + if let drawing = self.maskDrawing, let pngDrawingData = drawing.pngData() { + try container.encode(pngDrawingData, forKey: .maskDrawing) + } try container.encode(self.entities, forKey: .entities) @@ -637,101 +652,105 @@ public final class MediaEditorValues: Codable, Equatable { } public func makeCopy() -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: offset, cropRect: self.cropRect, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: offset, cropRect: self.cropRect, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } public func withUpdatedCropRect(cropRect: CGRect, rotation: CGFloat, mirroring: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: .zero, cropRect: cropRect, cropScale: 1.0, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: .zero, cropRect: cropRect, cropScale: 1.0, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedGradientColors(gradientColors: [UIColor]) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedVideoIsMuted(_ videoIsMuted: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedVideoIsFullHd(_ videoIsFullHd: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedVideoIsMirrored(_ videoIsMirrored: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedVideoVolume(_ videoVolume: CGFloat?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAdditionalVideo(path: String?, isDual: Bool, positionChanges: [VideoPositionChange]) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: path, additionalVideoIsDual: isDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: path, additionalVideoIsDual: isDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAdditionalVideo(position: CGPoint, scale: CGFloat, rotation: CGFloat) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAdditionalVideoTrimRange(_ additionalVideoTrimRange: Range?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAdditionalVideoOffset(_ additionalVideoOffset: Double?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAdditionalVideoVolume(_ additionalVideoVolume: CGFloat?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedVideoTrimRange(_ videoTrimRange: Range) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedDrawingAndEntities(drawing: UIImage?, entities: [CodableDrawingEntity]) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: drawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: drawing, maskDrawing: self.maskDrawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + } + + public func withUpdatedMaskDrawing(maskDrawing: UIImage?) -> MediaEditorValues { + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedToolValues(_ toolValues: [EditorToolKey: Any]) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrack(_ audioTrack: MediaAudioTrack?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrackTrimRange(_ audioTrackTrimRange: Range?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrackOffset(_ audioTrackOffset: Double?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrackVolume(_ audioTrackVolume: CGFloat?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedAudioTrackSamples(_ audioTrackSamples: MediaAudioTrackSamples?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: audioTrackSamples, qualityPreset: self.qualityPreset) } func withUpdatedNightTheme(_ nightTheme: Bool) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } public func withUpdatedEntities(_ entities: [CodableDrawingEntity]) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset) } public func withUpdatedQualityPreset(_ qualityPreset: MediaQualityPreset?) -> MediaEditorValues { - return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: qualityPreset) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: qualityPreset) } public var resultDimensions: PixelDimensions { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoFFMpegWriter.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoFFMpegWriter.swift index b42e99a435..a7c5fd134f 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoFFMpegWriter.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoFFMpegWriter.swift @@ -110,7 +110,6 @@ final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter { CVPixelBufferLockBaseAddress(buffer, CVPixelBufferLockFlags.readOnly) let src = CVPixelBufferGetBaseAddress(buffer) - var srcBuffer = vImage_Buffer(data: src, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow) var yBuffer = vImage_Buffer(data: frame.data[0], height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: width) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift index 838538984c..cf84864576 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift @@ -258,7 +258,7 @@ private final class MediaCutoutScreenComponent: Component { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let result = super.hitTest(point, with: event) if let controller = self.environment?.controller() as? MediaCutoutScreen, [.erase, .restore].contains(controller.mode), result == self.previewContainerView { - return nil//controller.previewView.superview + return nil } return result } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 75b78c450d..2152091942 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -52,6 +52,8 @@ private let playbackButtonTag = GenericComponentViewTag() private let muteButtonTag = GenericComponentViewTag() private let saveButtonTag = GenericComponentViewTag() private let switchCameraButtonTag = GenericComponentViewTag() +private let drawButtonTag = GenericComponentViewTag() +private let textButtonTag = GenericComponentViewTag() private let stickerButtonTag = GenericComponentViewTag() private let dayNightButtonTag = GenericComponentViewTag() @@ -861,12 +863,14 @@ final class MediaEditorScreenComponent: Component { let drawButtonSize = self.drawButton.update( transition: transition, - component: AnyComponent(Button( + component: AnyComponent(ContextReferenceButtonComponent( content: AnyComponent(Image( image: state.image(.draw), size: CGSize(width: 30.0, height: 30.0) )), - action: { [weak controller] in + tag: drawButtonTag, + minSize: CGSize(width: 30.0, height: 30.0), + action: { [weak controller] _, _ in guard let controller else { return } @@ -879,30 +883,21 @@ final class MediaEditorScreenComponent: Component { environment: {}, containerSize: CGSize(width: 40.0, height: 40.0) ) - let drawButtonFrame = CGRect( + var drawButtonFrame = CGRect( origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 5.0 - drawButtonSize.width / 2.0 - 3.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 1.0), size: drawButtonSize ) - if let drawButtonView = self.drawButton.view { - if drawButtonView.superview == nil { - self.addSubview(drawButtonView) - } - transition.setPosition(view: drawButtonView, position: drawButtonFrame.center) - transition.setBounds(view: drawButtonView, bounds: CGRect(origin: .zero, size: drawButtonFrame.size)) - if !self.animatingButtons { - transition.setAlpha(view: drawButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) - } - } - let textButtonSize = self.textButton.update( transition: transition, - component: AnyComponent(Button( + component: AnyComponent(ContextReferenceButtonComponent( content: AnyComponent(Image( image: state.image(.text), size: CGSize(width: 30.0, height: 30.0) )), - action: { [weak controller] in + tag: textButtonTag, + minSize: CGSize(width: 30.0, height: 30.0), + action: { [weak controller] _, _ in guard let controller else { return } @@ -915,20 +910,10 @@ final class MediaEditorScreenComponent: Component { environment: {}, containerSize: CGSize(width: 40.0, height: 40.0) ) - let textButtonFrame = CGRect( + var textButtonFrame = CGRect( origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 5.0 * 2.0 - textButtonSize.width / 2.0 - 1.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 2.0), size: textButtonSize ) - if let textButtonView = self.textButton.view { - if textButtonView.superview == nil { - self.addSubview(textButtonView) - } - transition.setPosition(view: textButtonView, position: textButtonFrame.center) - transition.setBounds(view: textButtonView, bounds: CGRect(origin: .zero, size: textButtonFrame.size)) - if !self.animatingButtons { - transition.setAlpha(view: textButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) - } - } let stickerButtonSize = self.stickerButton.update( transition: transition, @@ -956,10 +941,41 @@ final class MediaEditorScreenComponent: Component { environment: {}, containerSize: CGSize(width: 40.0, height: 40.0) ) - let stickerButtonFrame = CGRect( + var stickerButtonFrame = CGRect( origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 5.0 * 3.0 - stickerButtonSize.width / 2.0 + 1.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 2.0), size: stickerButtonSize ) + + if let subject = controller.node.subject, case .empty = subject { + let distance = floor((stickerButtonFrame.minX - textButtonFrame.minX) * 1.2) + textButtonFrame.origin.x = availableSize.width / 2.0 - textButtonFrame.width / 2.0 + drawButtonFrame.origin.x = textButtonFrame.origin.x - distance + stickerButtonFrame.origin.x = textButtonFrame.origin.x + distance + } + + + if let drawButtonView = self.drawButton.view { + if drawButtonView.superview == nil { + self.addSubview(drawButtonView) + } + transition.setPosition(view: drawButtonView, position: drawButtonFrame.center) + transition.setBounds(view: drawButtonView, bounds: CGRect(origin: .zero, size: drawButtonFrame.size)) + if !self.animatingButtons { + transition.setAlpha(view: drawButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) + } + } + + if let textButtonView = self.textButton.view { + if textButtonView.superview == nil { + self.addSubview(textButtonView) + } + transition.setPosition(view: textButtonView, position: textButtonFrame.center) + transition.setBounds(view: textButtonView, bounds: CGRect(origin: .zero, size: textButtonFrame.size)) + if !self.animatingButtons { + transition.setAlpha(view: textButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) + } + } + if let stickerButtonView = self.stickerButton.view { if stickerButtonView.superview == nil { self.addSubview(stickerButtonView) @@ -971,38 +987,44 @@ final class MediaEditorScreenComponent: Component { } } - let toolsButtonSize = self.toolsButton.update( - transition: transition, - component: AnyComponent(Button( - content: AnyComponent(Image( - image: state.image(.tools), - size: CGSize(width: 30.0, height: 30.0) - )), - action: { [weak controller] in - guard let controller else { - return - } - guard !controller.node.recording.isActive else { - return - } - openDrawing(.tools) - } - )), - environment: {}, - containerSize: CGSize(width: 40.0, height: 40.0) - ) - let toolsButtonFrame = CGRect( - origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 5.0 * 4.0 - toolsButtonSize.width / 2.0 + 3.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 1.0), - size: toolsButtonSize - ) - if let toolsButtonView = self.toolsButton.view { - if toolsButtonView.superview == nil { - self.addSubview(toolsButtonView) + if let subject = controller.node.subject, case .empty = subject { + if let toolsButtonView = self.toolsButton.view, toolsButtonView.superview != nil { + toolsButtonView.removeFromSuperview() } - transition.setPosition(view: toolsButtonView, position: toolsButtonFrame.center) - transition.setBounds(view: toolsButtonView, bounds: CGRect(origin: .zero, size: toolsButtonFrame.size)) - if !self.animatingButtons { - transition.setAlpha(view: toolsButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) + } else { + let toolsButtonSize = self.toolsButton.update( + transition: transition, + component: AnyComponent(Button( + content: AnyComponent(Image( + image: state.image(.tools), + size: CGSize(width: 30.0, height: 30.0) + )), + action: { [weak controller] in + guard let controller else { + return + } + guard !controller.node.recording.isActive else { + return + } + openDrawing(.tools) + } + )), + environment: {}, + containerSize: CGSize(width: 40.0, height: 40.0) + ) + let toolsButtonFrame = CGRect( + origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 5.0 * 4.0 - toolsButtonSize.width / 2.0 + 3.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 1.0), + size: toolsButtonSize + ) + if let toolsButtonView = self.toolsButton.view { + if toolsButtonView.superview == nil { + self.addSubview(toolsButtonView) + } + transition.setPosition(view: toolsButtonView, position: toolsButtonFrame.center) + transition.setBounds(view: toolsButtonView, bounds: CGRect(origin: .zero, size: toolsButtonFrame.size)) + if !self.animatingButtons { + transition.setAlpha(view: toolsButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) + } } } @@ -2418,7 +2440,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } - final class Node: ViewControllerTracingNode, ASGestureRecognizerDelegate { + final class Node: ViewControllerTracingNode, ASGestureRecognizerDelegate, UIScrollViewDelegate { private weak var controller: MediaEditorScreen? private let context: AccountContext fileprivate var interaction: DrawingToolsInteraction? @@ -2438,6 +2460,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate fileprivate let toolValue: ComponentView fileprivate let previewContainerView: UIView + fileprivate let previewScrollView: UIScrollView fileprivate let previewContentContainerView: PortalSourceView private var transitionInView: UIImageView? @@ -2514,7 +2537,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.componentHost = ComponentView() self.storyPreview = ComponentView() self.toolValue = ComponentView() - + self.previewContainerView = UIView() self.previewContainerView.alpha = 0.0 self.previewContainerView.clipsToBounds = true @@ -2523,6 +2546,14 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.previewContainerView.layer.cornerCurve = .continuous } + self.previewScrollView = UIScrollView() + self.previewScrollView.contentInsetAdjustmentBehavior = .never + self.previewScrollView.contentInset = .zero + self.previewScrollView.showsHorizontalScrollIndicator = false + self.previewScrollView.showsVerticalScrollIndicator = false + self.previewScrollView.panGestureRecognizer.minimumNumberOfTouches = 2 + self.previewScrollView.isScrollEnabled = false + self.previewContentContainerView = PortalSourceView() self.gradientView = UIImageView() @@ -2568,6 +2599,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.view.addSubview(self.backgroundDimView) self.view.addSubview(self.containerView) + + self.previewScrollView.delegate = self + self.containerView.addSubview(self.previewContainerView) if case .stickerEditor = controller.mode { @@ -2597,7 +2631,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.previewContainerView.addSubview(self.gradientView) } - self.previewContainerView.addSubview(self.previewContentContainerView) + self.previewContainerView.addSubview(self.previewScrollView) + self.previewScrollView.addSubview(self.previewContentContainerView) self.previewContentContainerView.addSubview(self.previewView) self.previewContentContainerView.addSubview(self.entitiesContainerView) @@ -2946,6 +2981,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.controller?.stickerRecommendedEmoji = emojiForClasses(classes.map { $0.0 }) } + if case .empty = effectiveSubject { + self.stickerMaskDrawingView?.emptyColor = .black + self.stickerMaskDrawingView?.clearWithEmptyColor() + } + if case .message = effectiveSubject { } else { self.readyValue.set(.single(true)) @@ -3050,7 +3090,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate controller.stickerSelectedEmoji = emoji let stickerEntity = DrawingStickerEntity(content: .file(.standalone(media: sticker), .sticker)) stickerEntity.referenceDrawingSize = storyDimensions - stickerEntity.scale = 4.0 + stickerEntity.scale = 4.0 * 0.97 stickerEntity.position = CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0) self.entitiesView.add(stickerEntity, announce: false) } @@ -3262,6 +3302,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } ) + + Queue.mainQueue().after(0.1) { + self.previewScrollView.pinchGestureRecognizer?.isEnabled = false + } } @objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { @@ -4436,6 +4480,53 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } + private func adjustPreviewZoom(updating: Bool = false) { + let minScale: CGFloat = 0.05 + let maxScale: CGFloat = 3.0 + + if self.previewScrollView.minimumZoomScale != minScale { + self.previewScrollView.minimumZoomScale = minScale + } + if self.previewScrollView.maximumZoomScale != maxScale { + self.previewScrollView.maximumZoomScale = maxScale + } + + let boundsSize = self.previewScrollView.frame.size + var contentFrame = self.previewContentContainerView.frame + if boundsSize.width > contentFrame.size.width { + contentFrame.origin.x = (boundsSize.width - contentFrame.size.width) / 2.0 + } else { + contentFrame.origin.x = 0.0 + } + + if boundsSize.height > contentFrame.size.height { + contentFrame.origin.y = (boundsSize.height - contentFrame.size.height) / 2.0 + } else { + contentFrame.origin.y = 0.0 + } + self.previewContentContainerView.frame = contentFrame + + if !updating { + self.stickerMaskDrawingView?.updateZoomScale(self.previewScrollView.zoomScale) + } + } + + func scrollViewDidZoom(_ scrollView: UIScrollView) { + self.adjustPreviewZoom() + } + + func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { + self.adjustPreviewZoom() + + if scrollView.zoomScale < 1.0 { + scrollView.setZoomScale(1.0, animated: true) + } + } + + func viewForZooming(in scrollView: UIScrollView) -> UIView? { + return self.previewContentContainerView + } + fileprivate var drawingScreen: DrawingScreen? fileprivate var stickerScreen: StickerPickerScreen? fileprivate weak var cutoutScreen: MediaCutoutScreen? @@ -4723,6 +4814,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate guard let mediaEditor = self.mediaEditor, let stickerMaskDrawingView = self.stickerMaskDrawingView, let stickerBackgroundView = self.stickerBackgroundView else { return } + + if [.cutoutErase, .cutoutRestore].contains(mode) { + self.previewScrollView.isScrollEnabled = true + self.previewScrollView.pinchGestureRecognizer?.isEnabled = true + } + let cutoutController = MediaCutoutScreen( context: self.context, mode: cutoutMode, @@ -4746,6 +4843,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } cutoutController.dismissed = { [weak self] in if let self { + self.previewScrollView.setZoomScale(1.0, animated: true) + self.previewScrollView.isScrollEnabled = false + self.previewScrollView.pinchGestureRecognizer?.isEnabled = false self.animateInFromTool(inPlace: true) } } @@ -4911,8 +5011,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let previewFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - previewSize.width) / 2.0), y: topInset - bottomInputOffset + self.dismissOffset), size: previewSize) transition.setFrame(view: self.previewContainerView, frame: previewFrame) + transition.setFrame(view: self.previewScrollView, frame: CGRect(origin: .zero, size: previewSize)) - transition.setFrame(view: self.previewContentContainerView, frame: CGRect(origin: .zero, size: previewSize)) + if self.previewScrollView.contentSize == .zero { + self.previewScrollView.zoomScale = 1.0 + self.previewScrollView.contentSize = previewSize + } + + if abs(self.previewContentContainerView.bounds.width - previewSize.width) > 1.0 { + transition.setFrame(view: self.previewContentContainerView, frame: CGRect(origin: .zero, size: previewSize)) + } + + self.adjustPreviewZoom(updating: true) transition.setFrame(view: self.previewView, frame: CGRect(origin: .zero, size: previewSize)) let entitiesViewScale = previewSize.width / storyDimensions.width @@ -5125,6 +5235,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate public var completion: (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _ in } public var dismissed: () -> Void = { } public var willDismiss: () -> Void = { } + public var sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? private var adminedChannels = Promise<[EnginePeer]>() private var closeFriends = Promise<[EnginePeer]>() @@ -6236,6 +6347,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate return } + if let subject = self.node.subject, case .empty = subject { + if !self.node.hasAnyChanges && !self.node.drawingView.internalState.canUndo { + self.hapticFeedback.error() + + self.node.componentHost.findTaggedView(tag: drawButtonTag)?.layer.addShakeAnimation() + self.node.componentHost.findTaggedView(tag: stickerButtonTag)?.layer.addShakeAnimation() + self.node.componentHost.findTaggedView(tag: textButtonTag)?.layer.addShakeAnimation() + + return + } + } + self.dismissAllTooltips() if let navigationController = self.navigationController as? NavigationController { @@ -6298,9 +6421,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate isVideo = true } Queue.concurrentDefaultQueue().async { - if let data = try? WebP.convert(toWebP: image, quality: 97.0) { + if !isVideo, let data = try? WebP.convert(toWebP: image, quality: 97.0) { self.context.account.postbox.mediaBox.storeResourceData(isVideo ? thumbnailResource.id : resource.id, data: data) } + if let thumbnailImage = generateScaledImage(image: image, size: CGSize(width: 320.0, height: 320.0), opaque: false, scale: 1.0), let data = try? WebP.convert(toWebP: thumbnailImage, quality: 90.0) { + self.context.account.postbox.mediaBox.storeResourceData(thumbnailResource.id, data: data) + } } var file = stickerFile(resource: resource, thumbnailResource: thumbnailResource, size: Int64(0), dimensions: PixelDimensions(image.size), duration: self.preferredStickerDuration(), isVideo: isVideo) @@ -6367,9 +6493,25 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate contextItems.append(.custom(StickerPackListContextItem(context: self.context, packs: self.myStickerPacks, packSelected: { [weak self] pack in guard let self else { - return + return true + } + if pack.count >= 120 { + let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.MediaEditor_StickersTooMuch, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { [weak self] action in + if case .info = action, let self { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true, dismissed: { + + }) + self.push(controller) + } + return false + }) + self.hapticFeedback.error() + self.present(controller, in: .window(.root)) + return false + } else { + self.uploadSticker(file, action: .addToStickerPack(pack: .id(id: pack.id.id, accessHash: pack.accessHash), title: pack.title)) + return true } - self.uploadSticker(file, action: .addToStickerPack(pack: .id(id: pack.id.id, accessHash: pack.accessHash), title: pack.title)) }), false)) let items = ContextController.Items( @@ -6399,7 +6541,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate f(.default) var action: StickerAction = .upload - if !self.node.hasAnyChanges, case let .sticker(sticker, _) = self.node.subject { + if !self.node.hasAnyChanges && !self.node.drawingView.internalState.canUndo, case let .sticker(sticker, _) = self.node.subject { file = sticker action = .update } @@ -6508,6 +6650,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate stickers: [ ImportSticker( resource: .standalone(resource: file.resource), + thumbnailResource: file.previewRepresentations.first.flatMap { .standalone(resource: $0.resource) }, emojis: self.effectiveStickerEmoji(), dimensions: file.dimensions ?? PixelDimensions(width: 512, height: 512), duration: file.duration, @@ -6552,7 +6695,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } self.present(controller, in: .window(.root)) } - + private let stickerUploadDisposable = MetaDisposable() private func uploadSticker(_ file: TelegramMediaFile, action: StickerAction) { let context = self.context @@ -6624,7 +6767,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let resource = resource as? CloudDocumentMediaResource { return .single(.progress(1.0)) |> then(.single(.complete(resource, mimeType))) } else { - return context.engine.stickers.uploadSticker(peer: peer._asPeer(), resource: resource, alt: "", dimensions: dimensions, duration: duration, mimeType: mimeType) + return context.engine.stickers.uploadSticker(peer: peer._asPeer(), resource: resource, thumbnail: file.previewRepresentations.first?.resource, alt: "", dimensions: dimensions, duration: duration, mimeType: mimeType) |> mapToSignal { status -> Signal in switch status { case let .progress(progress): @@ -6887,14 +7030,16 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate guard let self else { return } + var values = mediaEditor.values var duration: Double = 0.0 if case let .video(video, _) = exportSubject { duration = video.duration.seconds } if isSticker { duration = self.preferredStickerDuration() + values = values.withUpdatedMaskDrawing(maskDrawing: self.node.stickerMaskDrawingView?.drawingImage) } - let configuration = recommendedVideoExportConfiguration(values: mediaEditor.values, duration: duration, forceFullHd: true, frameRate: 60.0, isSticker: isSticker) + let configuration = recommendedVideoExportConfiguration(values: values, duration: duration, forceFullHd: true, frameRate: 60.0, isSticker: isSticker) let outputPath = NSTemporaryDirectory() + "\(Int64.random(in: 0 ..< .max)).\(fileExtension)" let videoExport = MediaEditorVideoExport(postbox: self.context.account.postbox, subject: exportSubject, configuration: configuration, outputPath: outputPath, textScale: 2.0) self.videoExport = videoExport diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift index 05f167e9f5..67da5b8fe1 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift @@ -13,9 +13,9 @@ import ContextUI final class StickerPackListContextItem: ContextMenuCustomItem { let context: AccountContext let packs: [(StickerPackCollectionInfo, StickerPackItem?)] - let packSelected: (StickerPackCollectionInfo) -> Void + let packSelected: (StickerPackCollectionInfo) -> Bool - init(context: AccountContext, packs: [(StickerPackCollectionInfo, StickerPackItem?)], packSelected: @escaping (StickerPackCollectionInfo) -> Void) { + init(context: AccountContext, packs: [(StickerPackCollectionInfo, StickerPackItem?)], packSelected: @escaping (StickerPackCollectionInfo) -> Bool) { self.context = context self.packs = packs self.packSelected = packSelected @@ -75,9 +75,9 @@ private final class StickerPackListContextItemNode: ASDisplayNode, ContextMenuCu } let action = ContextMenuActionItem(text: pack.title, textLayout: .singleLine, icon: { _ in nil }, iconSource: thumbnailIconSource, iconPosition: .left, action: { _, f in - f(.dismissWithoutContent) - - item.packSelected(pack) + if item.packSelected(pack) { + f(.dismissWithoutContent) + } }) let actionNode = ContextControllerActionsListActionItemNode(getController: getController, requestDismiss: actionSelected, requestUpdateAction: { _, _ in }, item: action) actionNodes.append(actionNode) diff --git a/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift b/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift index 42b71c4316..fd926aaa0b 100644 --- a/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift +++ b/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift @@ -902,6 +902,7 @@ private extension MediaEditorValues { additionalVideoVolume: nil, nightTheme: false, drawing: nil, + maskDrawing: nil, entities: [], toolValues: [:], audioTrack: nil, @@ -1044,6 +1045,7 @@ private extension MediaEditorValues { additionalVideoVolume: nil, nightTheme: false, drawing: drawing, + maskDrawing: nil, entities: entities, toolValues: toolValues, audioTrack: nil, diff --git a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift index 70892cf4c4..207e788b94 100644 --- a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift +++ b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift @@ -1565,7 +1565,7 @@ public class VideoMessageCameraScreen: ViewController { guard let self else { return } - let values = MediaEditorValues(peerId: self.context.account.peerId, originalDimensions: dimensions, cropOffset: .zero, cropRect: CGRect(origin: .zero, size: dimensions.cgSize), cropScale: 1.0, cropRotation: 0.0, cropMirroring: false, cropOrientation: nil, gradientColors: nil, videoTrimRange: self.node.previewState?.trimRange, videoIsMuted: false, videoIsFullHd: false, videoIsMirrored: false, videoVolume: nil, additionalVideoPath: nil, additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], additionalVideoTrimRange: nil, additionalVideoOffset: nil, additionalVideoVolume: nil, nightTheme: false, drawing: nil, entities: [], toolValues: [:], audioTrack: nil, audioTrackTrimRange: nil, audioTrackOffset: nil, audioTrackVolume: nil, audioTrackSamples: nil, qualityPreset: .videoMessage) + let values = MediaEditorValues(peerId: self.context.account.peerId, originalDimensions: dimensions, cropOffset: .zero, cropRect: CGRect(origin: .zero, size: dimensions.cgSize), cropScale: 1.0, cropRotation: 0.0, cropMirroring: false, cropOrientation: nil, gradientColors: nil, videoTrimRange: self.node.previewState?.trimRange, videoIsMuted: false, videoIsFullHd: false, videoIsMirrored: false, videoVolume: nil, additionalVideoPath: nil, additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], additionalVideoTrimRange: nil, additionalVideoOffset: nil, additionalVideoVolume: nil, nightTheme: false, drawing: nil, maskDrawing: nil, entities: [], toolValues: [:], audioTrack: nil, audioTrackTrimRange: nil, audioTrackOffset: nil, audioTrackVolume: nil, audioTrackSamples: nil, qualityPreset: .videoMessage) var resourceAdjustments: VideoMediaResourceAdjustments? = nil if let valuesData = try? JSONEncoder().encode(values) { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index b2304b60c9..34f0852b8a 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -7642,6 +7642,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let editMessage = interfaceState.editMessage, let message = combinedInitialData.initialData?.associatedMessages[editMessage.messageId] { let (updatedState, updatedPreviewQueryState) = updatedChatEditInterfaceMessageState(context: strongSelf.context, state: updated, message: message) updated = updatedState + strongSelf.editingUrlPreviewQueryState?.1.dispose() strongSelf.editingUrlPreviewQueryState = updatedPreviewQueryState } updated = updated.updatedSlowmodeState(slowmodeState) @@ -8979,6 +8980,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let (updatedState, updatedPreviewQueryState) = updatedChatEditInterfaceMessageState(context: strongSelf.context, state: updated, message: message) updated = updatedState + strongSelf.editingUrlPreviewQueryState?.1.dispose() strongSelf.editingUrlPreviewQueryState = updatedPreviewQueryState updated = updated.updatedInputMode({ _ in diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 95c149e8c9..38ae349803 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -3314,6 +3314,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { context: self.context, chatPeerId: self.chatLocation.peerId, areCustomEmojiEnabled: self.chatPresentationInterfaceState.customEmojiAvailable, + hasEdit: true, sendGif: { [weak self] fileReference, sourceView, sourceRect, silentPosting, schedule in if let self { return self.controllerInteraction.sendGif(fileReference, sourceView, sourceRect, silentPosting, schedule) diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index 7f9aa96657..965451a3ec 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -1772,6 +1772,9 @@ extension ChatControllerImpl { } } as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void ) + editorController.sendSticker = { [weak self] file, sourceView, sourceRect in + return self?.interfaceInteraction?.sendSticker(file, true, sourceView, sourceRect, nil, []) ?? false + } self.push(editorController) }, dismissed: {} diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 80944b6120..78386b6264 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -761,7 +761,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { if file.isAnimatedSticker { thumbnailItem = .animated(EngineMediaResource(file.resource)) resourceReference = MediaResourceReference.media(media: .standalone(media: file), resource: file.resource) - } else if let dimensions = file.dimensions, let resource = chatMessageStickerResource(file: file, small: false) as? TelegramMediaResource { + } else if let dimensions = file.dimensions, let resource = chatMessageStickerResource(file: file, small: true) as? TelegramMediaResource { thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)) resourceReference = MediaResourceReference.media(media: .standalone(media: file), resource: resource) }