diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 23516bfeb4..32a607fe0b 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -1126,6 +1126,9 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele guard let strongSelf = self, strongSelf.availableFilters.count > 1 || strongSelf.controller?.isStoryPostingAvailable == true else { return [] } + guard case .chatList(.root) = strongSelf.location else { + return [] + } switch strongSelf.currentItemNode.visibleContentOffset() { case let .known(value): if value < -strongSelf.currentItemNode.tempTopInset { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift index 5ef2bea493..f137bb7442 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift @@ -148,8 +148,8 @@ final class MediaEditorComposer { var pixelBuffer: CVPixelBuffer? CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool, &pixelBuffer) - if let pixelBuffer { - makeEditorImageFrameComposition(inputImage: image, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, values: self.values, entities: self.entities, time: time, completion: { compositedImage in + if let pixelBuffer, let context = self.ciContext { + makeEditorImageFrameComposition(context: context, inputImage: image, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, values: self.values, entities: self.entities, time: time, completion: { compositedImage in if var compositedImage { let scale = self.outputDimensions.width / self.dimensions.width compositedImage = compositedImage.samplingLinear().transformed(by: CGAffineTransform(scaleX: scale, y: scale)) @@ -167,11 +167,14 @@ final class MediaEditorComposer { } func processImage(inputImage: CIImage, time: CMTime, completion: @escaping (CIImage?) -> Void) { - makeEditorImageFrameComposition(inputImage: inputImage, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, values: self.values, entities: self.entities, time: time, completion: completion) + guard let context = self.ciContext else { + return + } + makeEditorImageFrameComposition(context: context, inputImage: inputImage, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, values: self.values, entities: self.entities, time: time, completion: completion) } } -public func makeEditorImageComposition(account: Account, inputImage: UIImage, dimensions: CGSize, values: MediaEditorValues, time: CMTime, completion: @escaping (UIImage?) -> Void) { +public func makeEditorImageComposition(context: CIContext, account: Account, inputImage: UIImage, dimensions: CGSize, values: MediaEditorValues, time: CMTime, completion: @escaping (UIImage?) -> Void) { let colorSpace = CGColorSpaceCreateDeviceRGB() let inputImage = CIImage(image: inputImage, options: [.colorSpace: colorSpace])! let gradientImage: CIImage @@ -191,9 +194,8 @@ public func makeEditorImageComposition(account: Account, inputImage: UIImage, di entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, entity: entity.entity, colorSpace: colorSpace)) } - makeEditorImageFrameComposition(inputImage: inputImage, gradientImage: gradientImage, drawingImage: drawingImage, dimensions: dimensions, values: values, entities: entities, time: time, completion: { ciImage in + makeEditorImageFrameComposition(context: context, inputImage: inputImage, gradientImage: gradientImage, drawingImage: drawingImage, dimensions: dimensions, values: values, entities: entities, time: time, completion: { ciImage in if let ciImage { - let context = CIContext(options: [.workingColorSpace : NSNull()]) if let cgImage = context.createCGImage(ciImage, from: CGRect(origin: .zero, size: ciImage.extent.size)) { Queue.mainQueue().async { completion(UIImage(cgImage: cgImage)) @@ -205,7 +207,7 @@ public func makeEditorImageComposition(account: Account, inputImage: UIImage, di }) } -private func makeEditorImageFrameComposition(inputImage: CIImage, gradientImage: CIImage, drawingImage: CIImage?, dimensions: CGSize, values: MediaEditorValues, entities: [MediaEditorComposerEntity], time: CMTime, completion: @escaping (CIImage?) -> Void) { +private func makeEditorImageFrameComposition(context: CIContext, inputImage: CIImage, gradientImage: CIImage, drawingImage: CIImage?, dimensions: CGSize, values: MediaEditorValues, entities: [MediaEditorComposerEntity], time: CMTime, completion: @escaping (CIImage?) -> Void) { var resultImage = CIImage(color: .black).cropped(to: CGRect(origin: .zero, size: dimensions)).transformed(by: CGAffineTransform(translationX: -dimensions.width / 2.0, y: -dimensions.height / 2.0)) resultImage = gradientImage.composited(over: resultImage) @@ -253,7 +255,7 @@ private func makeEditorImageFrameComposition(inputImage: CIImage, gradientImage: return current + 1 } let index = i - entity.image(for: time, frameRate: frameRate, completion: { image in + entity.image(for: time, frameRate: frameRate, context: context, completion: { image in if var image = image?.samplingLinear() { let resetTransform = CGAffineTransform(translationX: -image.extent.width / 2.0, y: -image.extent.height / 2.0) image = image.transformed(by: resetTransform) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift index 197b6f5390..260ba977cf 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift @@ -62,7 +62,7 @@ private class MediaEditorComposerStaticEntity: MediaEditorComposerEntity { self.mirrored = mirrored } - func image(for time: CMTime, frameRate: Float, completion: @escaping (CIImage?) -> Void) { + func image(for time: CMTime, frameRate: Float, context: CIContext, completion: @escaping (CIImage?) -> Void) { completion(self.image) } } @@ -215,7 +215,7 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity { } private var circleMaskFilter: CIFilter? - func image(for time: CMTime, frameRate: Float, completion: @escaping (CIImage?) -> Void) { + func image(for time: CMTime, frameRate: Float, context: CIContext, completion: @escaping (CIImage?) -> Void) { if case .video = self.content { if let videoOutput = self.videoOutput { if let sampleBuffer = videoOutput.copyNextSampleBuffer(), let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { @@ -264,7 +264,7 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity { var tintColor: UIColor? if let file = self.content.file, file.isCustomTemplateEmoji { - tintColor = .white + tintColor = UIColor(rgb: 0xffffff) } let processFrame: (Double?, Int?, Int?, (Int) -> AnimatedStickerFrame?) -> Void = { [weak self] duration, frameCount, frameRate, takeFrame in @@ -341,7 +341,7 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity { } if let imagePixelBuffer { - let image = render(width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, pixelBuffer: imagePixelBuffer, tintColor: tintColor) + let image = render(context: context, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, pixelBuffer: imagePixelBuffer, tintColor: tintColor) strongSelf.image = image } completion(strongSelf.image) @@ -426,10 +426,27 @@ protocol MediaEditorComposerEntity { var baseSize: CGSize? { get } var mirrored: Bool { get } - func image(for time: CMTime, frameRate: Float, completion: @escaping (CIImage?) -> Void) + func image(for time: CMTime, frameRate: Float, context: CIContext, completion: @escaping (CIImage?) -> Void) } -private func render(width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, pixelBuffer: CVPixelBuffer, tintColor: UIColor?) -> CIImage? { +extension CIImage { + func tinted(with color: UIColor) -> CIImage? { + guard let colorMatrix = CIFilter(name: "CIColorMatrix") else { + return self + } + var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0 + color.getRed(&r, green: &g, blue: &b, alpha: &a) + colorMatrix.setDefaults() + colorMatrix.setValue(self, forKey: "inputImage") + colorMatrix.setValue(CIVector(x: r, y: 0, z: 0, w: 0), forKey: "inputRVector") + colorMatrix.setValue(CIVector(x: 0, y: g, z: 0, w: 0), forKey: "inputGVector") + colorMatrix.setValue(CIVector(x: 0, y: 0, z: b, w: 0), forKey: "inputBVector") + colorMatrix.setValue(CIVector(x: 0, y: 0, z: 0, w: a), forKey: "inputAVector") + return colorMatrix.outputImage + } +} + +private func render(context: CIContext, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, pixelBuffer: CVPixelBuffer, tintColor: UIColor?) -> CIImage? { //let calculatedBytesPerRow = (4 * Int(width) + 31) & (~31) //assert(bytesPerRow == calculatedBytesPerRow) @@ -458,7 +475,15 @@ private func render(width: Int, height: Int, bytesPerRow: Int, data: Data, type: CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) - return CIImage(cvPixelBuffer: pixelBuffer, options: [.colorSpace: deviceColorSpace]) + let image = CIImage(cvPixelBuffer: pixelBuffer, options: [.colorSpace: deviceColorSpace]) + if let tintColor { + if let cgImage = context.createCGImage(image, from: CGRect(origin: .zero, size: image.extent.size)) { + if let tintedImage = generateTintedImage(image: UIImage(cgImage: cgImage), color: tintColor) { + return CIImage(image: tintedImage) + } + } + } + return image } private extension UIImage.Orientation { diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 6617388bf8..45afe71c51 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -1566,6 +1566,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate fileprivate var mediaEditor: MediaEditor? fileprivate var mediaEditorPromise = Promise() + fileprivate let ciContext = CIContext(options: [.workingColorSpace : NSNull()]) + private let stickerPickerInputData = Promise() private var dismissPanGestureRecognizer: UIPanGestureRecognizer? @@ -1730,19 +1732,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if case let .draft(draft, _) = subject, let privacy = draft.privacy { controller.state.privacy = privacy - } else if !controller.isEditingStory { - let _ = combineLatest( - queue: Queue.mainQueue(), - mediaEditorStoredState(engine: controller.context.engine), - controller.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: controller.context.account.peerId)) - ).start(next: { [weak controller] state, peer in - if let controller, var privacy = state?.privacy { - if case let .user(user) = peer, !user.isPremium && privacy.timeout != 86400 { - privacy = MediaEditorResultPrivacy(privacy: privacy.privacy, timeout: 86400, archive: false) - } - controller.state.privacy = privacy - } - }) } let isSavingAvailable: Bool @@ -2971,6 +2960,19 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let initialPrivacy { self.state.privacy = MediaEditorResultPrivacy(privacy: initialPrivacy, timeout: 86400, archive: false) } + } else { + let _ = combineLatest( + queue: Queue.mainQueue(), + mediaEditorStoredState(engine: self.context.engine), + self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + ).start(next: { [weak self] state, peer in + if let self, var privacy = state?.privacy { + if case let .user(user) = peer, !user.isPremium && privacy.timeout != 86400 { + privacy = MediaEditorResultPrivacy(privacy: privacy.privacy, timeout: 86400, archive: false) + } + self.state.privacy = privacy + } + }) } } @@ -3272,7 +3274,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let resultImage = mediaEditor.resultImage { mediaEditor.seek(0.0, andPlay: false) - makeEditorImageComposition(account: self.context.account, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, completion: { resultImage in + makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, completion: { resultImage in guard let resultImage else { return } @@ -3499,7 +3501,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let _ = (firstFrame |> deliverOnMainQueue).start(next: { [weak self] image in if let self { - makeEditorImageComposition(account: self.context.account, inputImage: image ?? UIImage(), dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] coverImage in + makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image ?? UIImage(), dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] coverImage in if let self { self.completion(randomId, .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), caption, self.state.privacy, { [weak self] finished in self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in @@ -3521,7 +3523,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let image = mediaEditor.resultImage { self.saveDraft(id: randomId) - makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] resultImage in + makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] resultImage in if let self, let resultImage { self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), caption, self.state.privacy, { [weak self] finished in self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in @@ -3668,7 +3670,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } else { if let image = mediaEditor.resultImage { Queue.concurrentDefaultQueue().async { - makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { resultImage in + makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { resultImage in if let data = resultImage?.jpegData(compressionQuality: 0.8) { let outputPath = NSTemporaryDirectory() + "\(Int64.random(in: 0 ..< .max)).jpg" try? data.write(to: URL(fileURLWithPath: outputPath)) diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 6b0c80d7bf..5df7cb1bb2 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -382,10 +382,10 @@ final class ShareWithPeersScreenComponent: Component { @objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { - guard let environment = self.environment, let controller = environment.controller() else { + guard let environment = self.environment, let controller = environment.controller() as? ShareWithPeersScreen else { return } - controller.dismiss() + controller.requestDismiss() } } @@ -1045,10 +1045,10 @@ final class ShareWithPeersScreenComponent: Component { component: AnyComponent(Button( content: AnyComponent(Text(text: "Cancel", font: Font.regular(17.0), color: environment.theme.rootController.navigationBar.accentTextColor)), action: { [weak self] in - guard let self, let environment = self.environment, let controller = environment.controller() else { + guard let self, let environment = self.environment, let controller = environment.controller() as? ShareWithPeersScreen else { return } - controller.dismiss() + controller.requestDismiss() } ).minSize(CGSize(width: navigationHeight, height: navigationHeight))), environment: {}, @@ -1428,6 +1428,8 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer { private var isDismissed: Bool = false + public var dismissed: () -> Void = {} + public init(context: AccountContext, initialPrivacy: EngineStoryPrivacy, timeout: Int, stateContext: StateContext, completion: @escaping (EngineStoryPrivacy) -> Void, editCategory: @escaping (EngineStoryPrivacy) -> Void, secondaryAction: @escaping () -> Void = {}) { self.context = context @@ -1521,6 +1523,11 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer { } } + func requestDismiss() { + self.dismissed() + self.dismiss() + } + override public func dismiss(completion: (() -> Void)? = nil) { if !self.isDismissed { self.isDismissed = true diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 492c227514..f45b9bbb3a 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -548,7 +548,7 @@ public final class StoryItemSetContainerComponent: Component { } if let navigationController = component.controller()?.navigationController as? NavigationController { let topViewController = navigationController.topViewController - if !(topViewController is StoryContainerScreen) && !(topViewController is MediaEditorScreen) { + if !(topViewController is StoryContainerScreen) && !(topViewController is MediaEditorScreen) && !(topViewController is ShareWithPeersScreen) { return true } } @@ -2336,6 +2336,7 @@ public final class StoryItemSetContainerComponent: Component { } let _ = component.context.engine.messages.editStoryPrivacy(id: component.slice.item.storyItem.id, privacy: privacy).start() + self.privacyController = nil self.updateIsProgressPaused() }, editCategory: { [weak self] privacy in @@ -2350,6 +2351,12 @@ public final class StoryItemSetContainerComponent: Component { }) } ) + controller.dismissed = { [weak self] in + if let self { + self.privacyController = nil + self.updateIsProgressPaused() + } + } self.component?.controller()?.push(controller) self.privacyController = controller @@ -2382,6 +2389,12 @@ public final class StoryItemSetContainerComponent: Component { }, editCategory: { _ in } ) + controller.dismissed = { [weak self] in + if let self { + self.privacyController = nil + self.updateIsProgressPaused() + } + } self.component?.controller()?.push(controller) self.privacyController = controller