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/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 9cc1fcd669..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() + } } })) ] @@ -2170,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 } } 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/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/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/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index d2c4f92f96..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) + } } } @@ -2959,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)) @@ -6320,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 { @@ -6382,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) @@ -6499,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 } @@ -6608,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, @@ -6724,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): 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)