Кросс-платформенное шифрование фото/аватаров, профиль собеседника, вложения в чате
This commit is contained in:
91
Rosetta/Features/Chats/ChatDetail/PendingAttachment.swift
Normal file
91
Rosetta/Features/Chats/ChatDetail/PendingAttachment.swift
Normal file
@@ -0,0 +1,91 @@
|
||||
import UIKit
|
||||
|
||||
// MARK: - PendingAttachment
|
||||
|
||||
/// Represents an attachment selected by the user but not yet sent.
|
||||
/// Used in the attachment preview strip above the compositor.
|
||||
///
|
||||
/// Desktop parity: the `attachments` state array in `DialogInput.tsx` before
|
||||
/// `prepareAttachmentsToSend()` processes and uploads them.
|
||||
struct PendingAttachment: Identifiable, Sendable {
|
||||
|
||||
/// Random 8-character alphanumeric ID (matches desktop's `generateRandomKey(8)`).
|
||||
let id: String
|
||||
|
||||
/// Attachment type — `.image` or `.file` for user-initiated sends.
|
||||
let type: AttachmentType
|
||||
|
||||
/// Raw image/file data (pre-compression for images).
|
||||
let data: Data
|
||||
|
||||
/// Thumbnail for preview (images only). `nil` for files.
|
||||
let thumbnail: UIImage?
|
||||
|
||||
/// Original file name (files only). `nil` for images.
|
||||
let fileName: String?
|
||||
|
||||
/// File size in bytes (files only). `nil` for images.
|
||||
let fileSize: Int?
|
||||
|
||||
// MARK: - Factory
|
||||
|
||||
/// Creates a PendingAttachment from a UIImage (compressed to JPEG).
|
||||
static func fromImage(_ image: UIImage) -> PendingAttachment {
|
||||
let id = generateRandomId()
|
||||
// Resize to max 1280px on longest side for mobile optimization
|
||||
let resized = resizeImage(image, maxDimension: 1280)
|
||||
let data = resized.jpegData(compressionQuality: 0.8) ?? Data()
|
||||
let thumbnail = resizeImage(image, maxDimension: 200)
|
||||
|
||||
return PendingAttachment(
|
||||
id: id,
|
||||
type: .image,
|
||||
data: data,
|
||||
thumbnail: thumbnail,
|
||||
fileName: nil,
|
||||
fileSize: nil
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a PendingAttachment from file data + metadata.
|
||||
static func fromFile(data: Data, fileName: String) -> PendingAttachment {
|
||||
return PendingAttachment(
|
||||
id: generateRandomId(),
|
||||
type: .file,
|
||||
data: data,
|
||||
thumbnail: nil,
|
||||
fileName: fileName,
|
||||
fileSize: data.count
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
/// Generates a random 8-character ID (desktop: `generateRandomKey(8)`).
|
||||
private static func generateRandomId() -> String {
|
||||
let chars = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
return String((0..<8).map { _ in chars.randomElement()! })
|
||||
}
|
||||
|
||||
/// Resizes image so longest side is at most `maxDimension`.
|
||||
private static func resizeImage(_ image: UIImage, maxDimension: CGFloat) -> UIImage {
|
||||
let size = image.size
|
||||
let maxSide = max(size.width, size.height)
|
||||
guard maxSide > maxDimension else { return image }
|
||||
|
||||
let scale = maxDimension / maxSide
|
||||
let newSize = CGSize(width: size.width * scale, height: size.height * scale)
|
||||
|
||||
let renderer = UIGraphicsImageRenderer(size: newSize)
|
||||
return renderer.image { _ in
|
||||
image.draw(in: CGRect(origin: .zero, size: newSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
extension PendingAttachment {
|
||||
/// Desktop parity: `MAX_ATTACHMENTS_IN_MESSAGE = 5`.
|
||||
static let maxAttachmentsPerMessage = 5
|
||||
}
|
||||
Reference in New Issue
Block a user