Swiftgram/submodules/PassportUI/Sources/SecureIdLocalResource.swift
2022-05-10 13:56:38 +04:00

149 lines
5.8 KiB
Swift

import Foundation
import UIKit
import Postbox
import TelegramCore
import SwiftSignalKit
import Display
public struct SecureIdLocalImageResourceId {
public let id: Int64
public var uniqueId: String {
return "secure-id-local-\(self.id)"
}
public var hashValue: Int {
return self.id.hashValue
}
}
public class SecureIdLocalImageResource: TelegramMediaResource {
public let localId: Int64
public let source: TelegramMediaResource
public var size: Int64? {
return nil
}
public init(localId: Int64, source: TelegramMediaResource) {
self.localId = localId
self.source = source
}
public required init(decoder: PostboxDecoder) {
self.localId = decoder.decodeInt64ForKey("i", orElse: 0)
self.source = decoder.decodeObjectForKey("s") as! TelegramMediaResource
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt64(self.localId, forKey: "i")
encoder.encodeObject(self.source, forKey: "s")
}
public var id: MediaResourceId {
return MediaResourceId(SecureIdLocalImageResourceId(id: self.localId).uniqueId)
}
public func isEqual(to: MediaResource) -> Bool {
if let to = to as? SecureIdLocalImageResource {
return self.localId == to.localId && self.source.isEqual(to:to.source)
} else {
return false
}
}
}
private final class Buffer {
var data = Data()
}
public func fetchSecureIdLocalImageResource(postbox: Postbox, resource: SecureIdLocalImageResource) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
return Signal { subscriber in
guard let fetchResource = postbox.mediaBox.fetchResource else {
return EmptyDisposable
}
subscriber.putNext(.reset)
let fetch = fetchResource(resource.source, .single([(0 ..< Int64.max, .default)]), nil)
let buffer = Atomic<Buffer>(value: Buffer())
let disposable = fetch.start(next: { result in
switch result {
case .reset:
let _ = buffer.with { buffer in
buffer.data.count = 0
}
case .resourceSizeUpdated:
break
case .progressUpdated:
break
case let .moveLocalFile(path):
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
let _ = buffer.with { buffer in
buffer.data = data
}
let _ = try? FileManager.default.removeItem(atPath: path)
}
case let .moveTempFile(file):
if let data = try? Data(contentsOf: URL(fileURLWithPath: file.path)) {
let _ = buffer.with { buffer in
buffer.data = data
}
}
TempBox.shared.dispose(file)
case let .copyLocalItem(item):
let tempFile = TempBox.shared.tempFile(fileName: "file")
if item.copyTo(url: URL(fileURLWithPath: tempFile.path)) {
if let data = try? Data(contentsOf: URL(fileURLWithPath: tempFile.path)) {
let _ = buffer.with { buffer in
buffer.data = data
}
}
}
TempBox.shared.dispose(tempFile)
case let .replaceHeader(data, range):
let _ = buffer.with { buffer in
if buffer.data.count < range.count {
buffer.data.count = range.count
}
buffer.data.withUnsafeMutableBytes { buffer -> Void in
guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return
}
data.copyBytes(to: bytes, from: Int(range.lowerBound) ..< Int(range.upperBound))
}
}
case let .dataPart(resourceOffset, data, range, _):
let _ = buffer.with { buffer in
if buffer.data.count < Int(resourceOffset) + range.count {
buffer.data.count = Int(resourceOffset) + range.count
}
buffer.data.withUnsafeMutableBytes { buffer -> Void in
guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return
}
data.copyBytes(to: bytes.advanced(by: Int(resourceOffset)), from: Int(range.lowerBound) ..< Int(range.upperBound))
}
}
}
}, completed: {
let image = buffer.with { buffer -> UIImage? in
return UIImage(data: buffer.data)
}
if let image = image {
if let scaledImage = generateImage(image.size.fitted(CGSize(width: 2048.0, height: 2048.0)), contextGenerator: { size, context in
context.setBlendMode(.copy)
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size))
}, scale: 1.0), let scaledData = scaledImage.jpegData(compressionQuality: 0.6) {
subscriber.putNext(.dataPart(resourceOffset: 0, data: scaledData, range: 0 ..< Int64(scaledData.count), complete: true))
subscriber.putCompletion()
}
}
})
return ActionDisposable {
disposable.dispose()
}
}
}