mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-06-20 18:24:43 +00:00
86d1456552
31 waves of consumer-side migration from `import Postbox` to TelegramEngine typealiases. Net: 173 import drops + 39 BUILD-dep drops + 1 new typealias (`EngineStoryId = StoryId`, wave 113). Wave shapes used: - Orphan-import sweeps (107, 108, 128): drop `import Postbox` from files whose only Postbox-symbol reference was the import line itself, then resolve build failures. Methodology requires token-level (`grep -oE`) filtering, not line-level, to avoid masking real Postbox usage on lines that also contain `Namespaces.X` references. - Identifier-swap mini-waves (109-127, 129-134, 136-137): rename Postbox-typealiased identifiers to engine equivalents (PeerId -> EnginePeer.Id, MessageId -> EngineMessage.Id, MediaId -> EngineMedia.Id, MessageIndex -> EngineMessage.Index, StoryId -> EngineStoryId, ItemCollectionId -> EngineItemCollectionId, PreferencesEntry -> EnginePreferencesEntry, FetchResourceSourceType/Error -> EngineFetchResourceSourceType/Error, MemoryBuffer -> EngineMemoryBuffer, MessageTags -> EngineMessage.Tags, MessageAttribute -> EngineMessage.Attribute, TempBox -> EngineTempBox). - Asset-string FP-only orphans (124). - Typealias addition + drain (113): added `EngineStoryId` typealias to TelegramCore, then drained 3+11 consumer sites. Hard blockers identified during these waves (must restore `import Postbox` when present): MediaResource[A-Za-z]* (any suffix -- the literal `MediaResource` matches don't catch MediaResourceData/MediaResourceId/etc.), Postbox/MediaBox/MediaResource raw types, PostboxCoding/PostboxEncoder/ PostboxDecoder, TempBoxFile, ValueBoxKey, PostboxView, combinedView, HashFunctions, postboxLog, openPostbox, declareEncodable, PeerView, MessageHistoryView, MessageHistoryThreadData, CachedPeerData, RenderedPeer, SelectivePrivacyPeer, SimpleDictionary, ItemCollectionInfosView, ItemCollectionItem, ItemCollectionItemIndex, ItemCollectionViewEntryIndex, ChatListIndex, ChatListEntrySummaryComponents, CodableEntry, MessageHistoryThread, MessageHistoryAnchorIndex, MessageHistoryEntryLocation, PeerStoryStats, PeerNameIndex, PeerSummaryCounterTags, ChatListTotalUnreadStateCategory/Stats, arePeersEqual. Protocol-shape blockers: bare `Peer`/`Message`/`Media` in function signatures, generic args, enum-case payloads, or dict value types (e.g., `[PeerId: Peer]`, `case messages([Message])`, `Signal<(Peer?, ...), NoError>`). `replace_all PeerId -> EnginePeer.Id` is dangerous: mangles compound names like `failedPeerId`, `ContactListPeerId`, `nextRemoteMediaId`, `replyToMessageId`. Pre-flight grep `\b[a-z][a-zA-Z]*PeerId\b` and only replace_all if 0 matches. Also removes unneeded design/plan docs from a separate (link-highlighting) feature branch: - docs/superpowers/plans/2026-05-02-link-highlighting-modern-path-fixes.md - docs/superpowers/specs/2026-05-02-link-highlighting-modern-path-fixes-design.md Squashed commits: 6d82c2980d..e6de5d53a3 (59 commits, including per-wave content commits and per-wave CLAUDE.md bumps). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
113 lines
5.4 KiB
Swift
113 lines
5.4 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import SwiftSignalKit
|
|
import TelegramCore
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import AccountContext
|
|
import ChatControllerInteraction
|
|
import VisionKit
|
|
import PDFKit
|
|
import Pdf
|
|
|
|
extension ChatControllerImpl: VNDocumentCameraViewControllerDelegate {
|
|
func presentDocumentScanner() {
|
|
let documentScanner = VNDocumentCameraViewController()
|
|
documentScanner.delegate = self
|
|
if let rootViewController = self.context.sharedContext.mainWindow?.viewController?.view.window?.rootViewController {
|
|
rootViewController.present(documentScanner, animated: true)
|
|
}
|
|
}
|
|
|
|
func enqueueScan(scan: VNDocumentCameraScan, convertToPdf: Bool) {
|
|
struct Item {
|
|
let resource: TelegramMediaResource
|
|
let previewResource: TelegramMediaResource?
|
|
let fileName: String
|
|
let mimeType: String
|
|
let size: Int64
|
|
}
|
|
|
|
var items: [Item] = []
|
|
|
|
var title = scan.title.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
if title.isEmpty {
|
|
title = "Scan"
|
|
}
|
|
|
|
if convertToPdf {
|
|
let pdfDocument = PDFDocument()
|
|
for i in 0 ..< scan.pageCount {
|
|
let image = scan.imageOfPage(at: i)
|
|
if let pdfPage = PDFPage(image: image) {
|
|
pdfDocument.insert(pdfPage, at: i)
|
|
}
|
|
}
|
|
if let data = pdfDocument.dataRepresentation() {
|
|
var randomId: Int64 = 0
|
|
arc4random_buf(&randomId, 8)
|
|
|
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
|
self.context.engine.resources.storeResourceData(id: EngineMediaResource.Id(resource.id), data: data, synchronous: true)
|
|
|
|
var previewResource: LocalFileMediaResource?
|
|
if let image = generatePdfPreviewImage(data: data, size: CGSize(width: 256, height: 256.0)), let jpegData = image.jpegData(compressionQuality: 0.5) {
|
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
|
self.context.engine.resources.storeResourceData(id: EngineMediaResource.Id(resource.id), data: jpegData, synchronous: true)
|
|
previewResource = resource
|
|
}
|
|
|
|
items.append(Item(resource: resource, previewResource: previewResource, fileName: "\(title).pdf", mimeType: "application/pdf", size: Int64(data.count)))
|
|
}
|
|
} else {
|
|
for i in 0 ..< scan.pageCount {
|
|
let image = scan.imageOfPage(at: i)
|
|
if let data = image.jpegData(compressionQuality: 0.87) {
|
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
|
self.context.engine.resources.storeResourceData(id: EngineMediaResource.Id(resource.id), data: data, synchronous: true)
|
|
|
|
var fileTitle = title
|
|
if scan.pageCount > 1 {
|
|
fileTitle += "\(fileTitle)-\(i)"
|
|
}
|
|
|
|
items.append(Item(resource: resource, previewResource: nil, fileName: "\(fileTitle).jpg", mimeType: "image/jpeg", size: Int64(data.count)))
|
|
}
|
|
}
|
|
}
|
|
|
|
var messages: [EnqueueMessage] = []
|
|
var groupingKey: Int64?
|
|
if items.count > 1 {
|
|
groupingKey = Int64.random(in: Int64.min ... Int64.max)
|
|
}
|
|
|
|
for item in items {
|
|
let fileId = Int64.random(in: Int64.min ... Int64.max)
|
|
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
|
if let previewResource = item.previewResource {
|
|
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 320, height: 320), resource: previewResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
}
|
|
|
|
var attributes: [TelegramMediaFileAttribute] = []
|
|
attributes.append(.FileName(fileName: item.fileName))
|
|
|
|
let file = TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: item.resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: item.mimeType, size: item.size, attributes: attributes, alternativeRepresentations: [])
|
|
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), threadId: self.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: groupingKey, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
|
messages.append(message)
|
|
|
|
if let _ = groupingKey, messages.count % 10 == 0 {
|
|
groupingKey = Int64.random(in: Int64.min ... Int64.max)
|
|
}
|
|
}
|
|
|
|
let transformedMessages = self.transformEnqueueMessages(messages)
|
|
self.sendMessages(transformedMessages)
|
|
}
|
|
|
|
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
|
|
controller.dismiss(animated: true)
|
|
self.enqueueScan(scan: scan, convertToPdf: true)
|
|
}
|
|
}
|