mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
[WIP] Stickers editor
This commit is contained in:
parent
70eef8ecac
commit
a4accf138a
@ -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:
|
||||
|
@ -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] = []
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user