import Foundation import UIKit import LegacyComponents import Postbox import TelegramCore import SyncCore import SwiftSignalKit import Display import StickerResources public func stickerFromLegacyDocument(_ documentAttachment: TGDocumentMediaAttachment) -> TelegramMediaFile? { if documentAttachment.isSticker() { for case let sticker as TGDocumentAttributeSticker in documentAttachment.attributes { var attributes: [TelegramMediaFileAttribute] = [] var packReference: StickerPackReference? if let legacyPackReference = sticker.packReference as? TGStickerPackIdReference { packReference = .id(id: legacyPackReference.packId, accessHash: legacyPackReference.packAccessHash) } else if let legacyPackReference = sticker.packReference as? TGStickerPackShortnameReference { packReference = .name(legacyPackReference.shortName) } attributes.append(.Sticker(displayText: sticker.alt, packReference: packReference, maskData: nil)) var fileReference: Data? if let originInfo = documentAttachment.originInfo, let data = originInfo.fileReference { fileReference = data } return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentAttachment.documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(documentAttachment.datacenterId), fileId: documentAttachment.documentId, accessHash: documentAttachment.accessHash, size: Int(documentAttachment.size), fileReference: fileReference, fileName: documentAttachment.fileName()), previewRepresentations: [], immediateThumbnailData: nil, mimeType: documentAttachment.mimeType, size: Int(documentAttachment.size), attributes: attributes) } } return nil } func legacyComponentsStickers(postbox: Postbox, namespace: Int32) -> SSignal { return SSignal { subscriber in let disposable = (postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [namespace], aroundIndex: nil, count: 200 * 200)).start(next: { view in var stickerPackDocuments: [ItemCollectionId: [Any]] = [:] for entry in view.entries { if let item = entry.item as? StickerPackItem { if item.file.isAnimatedSticker { continue } let document = TGDocumentMediaAttachment() document.documentId = item.file.fileId.id if let resource = item.file.resource as? CloudDocumentMediaResource { document.accessHash = resource.accessHash document.datacenterId = Int32(resource.datacenterId) var stickerPackId: Int64 = 0 var accessHash: Int64 = 0 for case let .Sticker(sticker) in item.file.attributes { if let packReference = sticker.packReference, case let .id(id, h) = packReference { stickerPackId = id accessHash = h } break } document.originInfo = TGMediaOriginInfo(fileReference: resource.fileReference ?? Data(), fileReferences: [:], stickerPackId: stickerPackId, accessHash: accessHash) } document.mimeType = item.file.mimeType if let size = item.file.size { document.size = Int32(size) } if let thumbnail = item.file.previewRepresentations.first { let imageInfo = TGImageInfo() let encoder = PostboxEncoder() encoder.encodeRootObject(thumbnail.resource) let dataString = encoder.makeData().base64EncodedString(options: []) imageInfo.addImage(with: thumbnail.dimensions.cgSize, url: dataString) document.thumbnailInfo = imageInfo } var attributes: [Any] = [] for attribute in item.file.attributes { switch attribute { case let .Sticker(displayText, _, maskData): attributes.append(TGDocumentAttributeSticker(alt: displayText, packReference: nil, mask: maskData.flatMap { return TGStickerMaskDescription(n: $0.n, point: CGPoint(x: CGFloat($0.x), y: CGFloat($0.y)), zoom: CGFloat($0.zoom)) })) case let .ImageSize(size): attributes.append(TGDocumentAttributeImageSize(size: size.cgSize)) default: break } } document.attributes = attributes if stickerPackDocuments[entry.index.collectionId] == nil { stickerPackDocuments[entry.index.collectionId] = [] } stickerPackDocuments[entry.index.collectionId]!.append(document) } } let stickerPacks = NSMutableArray() for (id, info, _) in view.collectionInfos { if let info = info as? StickerPackCollectionInfo, !info.flags.contains(.isAnimated) { let pack = TGStickerPack(packReference: TGStickerPackIdReference(), title: info.title, stickerAssociations: [], documents: stickerPackDocuments[id] ?? [], packHash: info.hash, hidden: false, isMask: true, isFeatured: false, installedDate: 0)! stickerPacks.add(pack) } } var dict: [AnyHashable: Any] = [:] dict["packs"] = stickerPacks subscriber?.putNext(dict) }) return SBlockDisposable { disposable.dispose() } } } private final class LegacyStickerImageDataTask: NSObject { private let disposable = DisposableSet() init(account: Account, file: TelegramMediaFile, small: Bool, fitSize: CGSize, completion: @escaping (UIImage?) -> Void) { super.init() self.disposable.add(chatMessageLegacySticker(account: account, file: file, small: small, fitSize: fitSize, fetched: true, onlyFullSize: true).start(next: { generator in if let image = generator(TransformImageArguments(corners: ImageCorners(), imageSize: fitSize, boundingSize: fitSize, intrinsicInsets: UIEdgeInsets()))?.generateImage() { completion(image) } })) } deinit { self.disposable.dispose() } func cancel() { self.disposable.dispose() } } private let sharedImageCache = TGMemoryImageCache(softMemoryLimit: 2 * 1024 * 1024, hardMemoryLimit: 3 * 1024 * 1024)! final class LegacyStickerImageDataSource: TGImageDataSource { private let account: () -> Account? init(account: @escaping () -> Account?) { self.account = account super.init() } override func canHandleUri(_ uri: String!) -> Bool { if let uri = uri { if uri.hasPrefix("sticker-preview://") { return true } else if uri.hasPrefix("sticker://") { return true } } return false } override func loadDataSync(withUri uri: String!, canWait: Bool, acceptPartialData: Bool, asyncTaskId: AutoreleasingUnsafeMutablePointer!, progress: ((Float) -> Void)!, partialCompletion: ((TGDataResource?) -> Void)!, completion: ((TGDataResource?) -> Void)!) -> TGDataResource! { if let image = sharedImageCache.image(forKey: uri, attributes: nil) { return TGDataResource(image: image, decoded: true) } return nil } override func loadDataAsync(withUri uri: String!, progress: ((Float) -> Void)!, partialCompletion: ((TGDataResource?) -> Void)!, completion: ((TGDataResource?) -> Void)!) -> Any! { if let account = self.account() { let args: [AnyHashable : Any] var highQuality: Bool if uri.hasPrefix("sticker-preview://") { let argumentsString = String(uri[uri.index(uri.startIndex, offsetBy: "sticker-preview://?".count)...]) args = TGStringUtils.argumentDictionary(inUrlString: argumentsString)! highQuality = Int((args["highQuality"] as! String))! != 0 } else if uri.hasPrefix("sticker://") { let argumentsString = String(uri[uri.index(uri.startIndex, offsetBy: "sticker://?".count)...]) args = TGStringUtils.argumentDictionary(inUrlString: argumentsString)! highQuality = true } else { return nil } let documentId = Int64((args["documentId"] as! String))! let datacenterId = Int((args["datacenterId"] as! String))! let accessHash = Int64((args["accessHash"] as! String))! let size: Int? = nil let width = Int((args["width"] as! String))! let height = Int((args["height"] as! String))! if width < 128 { highQuality = false } let fitSize = CGSize(width: CGFloat(width), height: CGFloat(height)) var attributes: [TelegramMediaFileAttribute] = [] if let originInfoString = args["origin_info"] as? String, let originInfo = TGMediaOriginInfo(stringRepresentation: originInfoString), let stickerPackId = originInfo.stickerPackId?.int64Value, let stickerPackAccessHash = originInfo.stickerPackAccessHash?.int64Value { attributes.append(.Sticker(displayText: "", packReference: .id(id: stickerPackId, accessHash: stickerPackAccessHash), maskData: nil)) } var previewRepresentations: [TelegramMediaImageRepresentation] = [] if let legacyThumbnailUri = args["legacyThumbnailUri"] as? String, let data = Data(base64Encoded: legacyThumbnailUri, options: []) { if let resource = PostboxDecoder(buffer: MemoryBuffer(data: data)).decodeRootObject() as? TelegramMediaResource { previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 140, height: 140), resource: resource)) } } return LegacyStickerImageDataTask(account: account, file: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: datacenterId, fileId: documentId, accessHash: accessHash, size: size, fileReference: nil, fileName: fileNameFromFileAttributes(attributes)), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: "image/webp", size: size, attributes: attributes), small: !highQuality, fitSize: fitSize, completion: { image in if let image = image { sharedImageCache.setImage(image, forKey: uri, attributes: nil) completion?(TGDataResource(image: image, decoded: true)) } }) } else { return nil } } override func cancelTask(byId taskId: Any!) { if let task = taskId as? LegacyStickerImageDataTask { task.cancel() } } }