Files
Isaac f84e94f507 MediaResource → EngineMediaResource refactor: wave 2
Drive raw `MediaResource` out of the `TelegramEngine` public facade for
photo-upload APIs, and complete a first batch of consumer-side
migrations. Behavior-preserving. The `_internal_*` Postbox-facing
functions are untouched — only the facade signatures change, bridging
inside via `_asResource()` / `EngineMediaResource(_:)`.

TelegramEngine facades migrated (signatures now take EngineMediaResource):
- TelegramEngine.Peers.uploadedPeerPhoto / uploadedPeerVideo
- TelegramEngine.Peers.updatePeerPhoto (closure param too)
- TelegramEngine.AccountData.updateAccountPhoto / updateFallbackPhoto
- TelegramEngine.Contacts.updateContactPhoto
- TelegramEngine.Auth.uploadedPeerVideo

Consumer-side changes:
- MapResourceToAvatarSizes utility: signature is now
  (engine: TelegramEngine, resource: EngineMediaResource, ...). Uses
  engine.resources.data(id:) internally. The submodule drops
  `import Postbox` and the Bazel dep.
- AuthorizationUI: avatar-video signal retyped from
  Signal<TelegramMediaResource?> to Signal<EngineMediaResource?>.
- 27 `mapResourceToAvatarSizes(postbox:...)` call sites across 5
  TelegramUI/TelegramCallsUI files migrated to the new form.

Deferred:
- SaveToCameraRoll (planned Task 8) abandoned — module has three
  public functions taking `postbox: Postbox` (umbrella-type leak,
  banned by rule 2) and requires a full module-migration wave, not a
  type swap. Reason recorded in the wave-2 plan doc.
- Other TelegramEngine facades still leaking MediaResource
  (TelegramEngineStickers.uploadSticker; UploadSecureIdFile.* —
  additionally leaks Postbox) flagged for a future wave.

Docs:
- CLAUDE.md: new rule 7 (TelegramCore never imports UIKit/Display,
  shared with Telegram-Mac), and a new "MediaResource →
  EngineMediaResource consumer migration" section describing the
  wrap/unwrap helpers and the modify-in-place facade-bridging
  pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 09:07:54 +02:00

285 lines
14 KiB
Swift

import SwiftSignalKit
import Postbox
import TelegramApi
import MtProtoKit
public enum TelegramEngineAuthorizationState {
case unauthorized(UnauthorizedAccountState)
case authorized
}
public extension TelegramEngineUnauthorized {
final class Auth {
private let account: UnauthorizedAccount
init(account: UnauthorizedAccount) {
self.account = account
}
public func exportAuthTransferToken(accountManager: AccountManager<TelegramAccountManagerTypes>, otherAccountUserIds: [PeerId.Id], syncContacts: Bool) -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> {
return _internal_exportAuthTransferToken(accountManager: accountManager, account: self.account, otherAccountUserIds: otherAccountUserIds, syncContacts: syncContacts)
}
public func twoStepAuthData() -> Signal<TwoStepAuthData, MTRpcError> {
return _internal_twoStepAuthData(self.account.network)
}
public func test() -> Signal<Bool, String> {
return _internal_test(self.account.network)
}
public func updateTwoStepVerificationPassword(currentPassword: String?, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<UpdateTwoStepVerificationPasswordResult, UpdateTwoStepVerificationPasswordError> {
return _internal_updateTwoStepVerificationPassword(network: self.account.network, currentPassword: currentPassword, updatedPassword: updatedPassword)
}
public func requestTwoStepVerificationPasswordRecoveryCode() -> Signal<String, RequestTwoStepVerificationPasswordRecoveryCodeError> {
return _internal_requestTwoStepVerificationPasswordRecoveryCode(network: self.account.network)
}
public func checkPasswordRecoveryCode(code: String) -> Signal<Never, PasswordRecoveryError> {
return _internal_checkPasswordRecoveryCode(network: self.account.network, code: code)
}
public func performPasswordRecovery(code: String, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<RecoveredAccountData, PasswordRecoveryError> {
return _internal_performPasswordRecovery(network: self.account.network, code: code, updatedPassword: updatedPassword)
}
public func resendTwoStepRecoveryEmail() -> Signal<Never, ResendTwoStepRecoveryEmailError> {
return _internal_resendTwoStepRecoveryEmail(network: self.account.network)
}
public func uploadedPeerVideo(resource: EngineMediaResource) -> Signal<UploadedPeerPhotoData, NoError> {
return _internal_uploadedPeerVideo(postbox: self.account.postbox, network: self.account.network, messageMediaPreuploadManager: nil, resource: resource._asResource())
}
public func reportMissingCode(phoneNumber: String, phoneCodeHash: String, mnc: String) -> Signal<Never, ReportMissingCodeError> {
return _internal_reportMissingCode(network: self.account.network, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash, mnc: mnc)
}
public func requestPasskeyLoginData(apiId: Int32, apiHash: String) -> Signal<String?, NoError> {
return _internal_requestPasskeyLoginData(network: self.account.network, apiId: apiId, apiHash: apiHash)
}
public func state() -> Signal<TelegramEngineAuthorizationState?, NoError> {
return self.account.postbox.stateView()
|> map { view -> TelegramEngineAuthorizationState? in
if let state = view.state as? UnauthorizedAccountState {
return .unauthorized(state)
} else if let _ = view.state as? AuthorizedAccountState {
return .authorized
} else {
return nil
}
}
}
public func setState(state: UnauthorizedAccountState) -> Signal<Never, NoError> {
return self.account.postbox.transaction { transaction -> Void in
transaction.setState(state)
}
|> ignoreValues
}
}
}
public enum DeleteAccountError {
case generic
}
public extension TelegramEngine {
final class Auth {
private let account: Account
init(account: Account) {
self.account = account
}
public func twoStepAuthData() -> Signal<TwoStepAuthData, MTRpcError> {
return _internal_twoStepAuthData(self.account.network)
}
public func updateTwoStepVerificationPassword(currentPassword: String?, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<UpdateTwoStepVerificationPasswordResult, UpdateTwoStepVerificationPasswordError> {
return _internal_updateTwoStepVerificationPassword(network: self.account.network, currentPassword: currentPassword, updatedPassword: updatedPassword)
}
public func deleteAccount(reason: String, password: String?) -> Signal<Never, DeleteAccountError> {
let network = self.account.network
let passwordSignal: Signal<Api.InputCheckPasswordSRP?, DeleteAccountError>
if let password = password {
passwordSignal = _internal_twoStepAuthData(network)
|> mapError { _ -> DeleteAccountError in
return .generic
}
|> mapToSignal { authData -> Signal<Api.InputCheckPasswordSRP?, DeleteAccountError> in
if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData {
guard let kdfResult = passwordKDF(encryptionProvider: network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
return .fail(.generic)
}
return .single(.inputCheckPasswordSRP(.init(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1))))
} else {
return .single(nil)
}
}
} else {
passwordSignal = .single(nil)
}
return passwordSignal
|> mapToSignal { password -> Signal<Never, DeleteAccountError> in
var flags: Int32 = 0
if let _ = password {
flags |= (1 << 0)
}
return self.account.network.request(Api.functions.account.deleteAccount(flags: flags, reason: reason, password: password))
|> mapError { _ -> DeleteAccountError in
return .generic
}
|> ignoreValues
}
}
public func updateTwoStepVerificationEmail(currentPassword: String, updatedEmail: String) -> Signal<UpdateTwoStepVerificationPasswordResult, UpdateTwoStepVerificationPasswordError> {
return _internal_updateTwoStepVerificationEmail(network: self.account.network, currentPassword: currentPassword, updatedEmail: updatedEmail)
}
public func confirmTwoStepRecoveryEmail(code: String) -> Signal<Never, ConfirmTwoStepRecoveryEmailError> {
return _internal_confirmTwoStepRecoveryEmail(network: self.account.network, code: code)
}
public func resendTwoStepRecoveryEmail() -> Signal<Never, ResendTwoStepRecoveryEmailError> {
return _internal_resendTwoStepRecoveryEmail(network: self.account.network)
}
public func cancelTwoStepRecoveryEmail() -> Signal<Never, CancelTwoStepRecoveryEmailError> {
return _internal_cancelTwoStepRecoveryEmail(network: self.account.network)
}
public func twoStepVerificationConfiguration() -> Signal<TwoStepVerificationConfiguration, NoError> {
return _internal_twoStepVerificationConfiguration(account: self.account)
}
public func requestTwoStepVerifiationSettings(password: String) -> Signal<TwoStepVerificationSettings, AuthorizationPasswordVerificationError> {
return _internal_requestTwoStepVerifiationSettings(network: self.account.network, password: password)
}
public func requestTwoStepVerificationPasswordRecoveryCode() -> Signal<String, RequestTwoStepVerificationPasswordRecoveryCodeError> {
return _internal_requestTwoStepVerificationPasswordRecoveryCode(network: self.account.network)
}
public func performPasswordRecovery(code: String, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<RecoveredAccountData, PasswordRecoveryError> {
return _internal_performPasswordRecovery(network: self.account.network, code: code, updatedPassword: updatedPassword)
}
public func cachedTwoStepPasswordToken() -> Signal<TemporaryTwoStepPasswordToken?, NoError> {
return _internal_cachedTwoStepPasswordToken(postbox: self.account.postbox)
}
public func cacheTwoStepPasswordToken(token: TemporaryTwoStepPasswordToken?) -> Signal<Void, NoError> {
return _internal_cacheTwoStepPasswordToken(postbox: self.account.postbox, token: token)
}
public func requestTemporaryTwoStepPasswordToken(password: String, period: Int32, requiresBiometrics: Bool) -> Signal<TemporaryTwoStepPasswordToken, AuthorizationPasswordVerificationError> {
return _internal_requestTemporaryTwoStepPasswordToken(account: self.account, password: password, period: period, requiresBiometrics: requiresBiometrics)
}
public func checkPasswordRecoveryCode(code: String) -> Signal<Never, PasswordRecoveryError> {
return _internal_checkPasswordRecoveryCode(network: self.account.network, code: code)
}
public func requestTwoStepPasswordReset() -> Signal<RequestTwoStepPasswordResetResult, NoError> {
return _internal_requestTwoStepPasswordReset(network: self.account.network)
}
public func declineTwoStepPasswordReset() -> Signal<Never, NoError> {
return _internal_declineTwoStepPasswordReset(network: self.account.network)
}
public func requestCancelAccountResetData(hash: String) -> Signal<CancelAccountResetData, RequestCancelAccountResetDataError> {
return _internal_requestCancelAccountResetData(network: self.account.network, hash: hash)
}
public func requestNextCancelAccountResetOption(phoneNumber: String, phoneCodeHash: String) -> Signal<CancelAccountResetData, RequestCancelAccountResetDataError> {
return _internal_requestNextCancelAccountResetOption(network: self.account.network, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash)
}
public func requestCancelAccountReset(phoneCodeHash: String, phoneCode: String) -> Signal<Never, CancelAccountResetError> {
return _internal_requestCancelAccountReset(network: self.account.network, phoneCodeHash: phoneCodeHash, phoneCode: phoneCode)
}
public func invalidateLoginCodes(codes: [String]) -> Signal<Never, NoError> {
return _internal_invalidateLoginCodes(network: self.account.network, codes: codes)
}
public func reportMissingCode(phoneNumber: String, phoneCodeHash: String, mnc: String) -> Signal<Never, ReportMissingCodeError> {
return _internal_reportMissingCode(network: self.account.network, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash, mnc: mnc)
}
public func passkeysData() -> Signal<[TelegramPasskey], NoError> {
return _internal_passkeysData(network: self.account.network)
}
public func requestPasskeyRegistration() -> Signal<String?, NoError> {
return _internal_requestPasskeyRegistration(network: self.account.network)
}
public func requestCreatePasskey(id: String, clientData: String, attestationObject: Data) -> Signal<TelegramPasskey?, NoError> {
return _internal_requestCreatePasskey(network: self.account.network, id: id, clientData: clientData, attestationObject: attestationObject)
}
public func deletePasskey(id: String) -> Signal<Never, NoError> {
return _internal_deletePasskey(network: self.account.network, id: id)
}
}
}
public extension SomeTelegramEngine {
final class Auth {
private let engine: SomeTelegramEngine
init(engine: SomeTelegramEngine) {
self.engine = engine
}
public func twoStepAuthData() -> Signal<TwoStepAuthData, MTRpcError> {
switch self.engine {
case let .authorized(engine):
return engine.auth.twoStepAuthData()
case let .unauthorized(engine):
return engine.auth.twoStepAuthData()
}
}
public func updateTwoStepVerificationPassword(currentPassword: String?, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<UpdateTwoStepVerificationPasswordResult, UpdateTwoStepVerificationPasswordError> {
switch self.engine {
case let .authorized(engine):
return engine.auth.updateTwoStepVerificationPassword(currentPassword: currentPassword, updatedPassword: updatedPassword)
case let .unauthorized(engine):
return engine.auth.updateTwoStepVerificationPassword(currentPassword: currentPassword, updatedPassword: updatedPassword)
}
}
public func requestTwoStepVerificationPasswordRecoveryCode() -> Signal<String, RequestTwoStepVerificationPasswordRecoveryCodeError> {
switch self.engine {
case let .authorized(engine):
return engine.auth.requestTwoStepVerificationPasswordRecoveryCode()
case let .unauthorized(engine):
return engine.auth.requestTwoStepVerificationPasswordRecoveryCode()
}
}
public func checkPasswordRecoveryCode(code: String) -> Signal<Never, PasswordRecoveryError> {
switch self.engine {
case let .authorized(engine):
return engine.auth.checkPasswordRecoveryCode(code: code)
case let .unauthorized(engine):
return engine.auth.checkPasswordRecoveryCode(code: code)
}
}
}
var auth: Auth {
return Auth(engine: self)
}
}