[WIP] Stickers editor

This commit is contained in:
Ilya Laktyushin 2024-04-09 07:23:03 +04:00
parent 70eef8ecac
commit a4accf138a
9 changed files with 186 additions and 94 deletions

View File

@ -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:

View File

@ -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] = []

View File

@ -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
}
}

View File

@ -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<UploadStickerStatus, UploadStickerError> {
func _internal_uploadSticker(account: Account, peer: Peer, resource: MediaResource, thumbnail: MediaResource? = nil, alt: String, dimensions: PixelDimensions, duration: Double?, mimeType: String) -> Signal<UploadStickerStatus, UploadStickerError> {
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<UploadedStickerData?, NoError>
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<UploadStickerStatus, UploadStickerError> in
|> mapToSignal { result, thumbnailResult -> Signal<UploadStickerStatus, UploadStickerError> 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<UploadStickerStatus, UploadStickerError> 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<UploadStickerStatus, UploadStickerError> 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
}

View File

@ -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<UploadStickerStatus, UploadStickerError> {
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<UploadStickerStatus, UploadStickerError> {
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<CreateStickerSetStatus, CreateStickerSetError> {

View File

@ -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<InputData, NoError> {
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<InputData, NoError> {
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

View File

@ -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)

View File

@ -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<UploadStickerStatus, UploadStickerError> in
switch status {
case let .progress(progress):

View File

@ -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)