mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-25 01:22:41 +00:00
no message
This commit is contained in:
parent
c192cd39e4
commit
cbe8943bc7
@ -167,6 +167,8 @@
|
||||
D03121021DA57E93006A2A60 /* TelegramPeerNotificationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03121011DA57E93006A2A60 /* TelegramPeerNotificationSettings.swift */; };
|
||||
D03229F41E6B39700000AF9C /* ImportAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03229F31E6B39700000AF9C /* ImportAccount.swift */; };
|
||||
D03229F51E6B39700000AF9C /* ImportAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03229F31E6B39700000AF9C /* ImportAccount.swift */; };
|
||||
D032F5BC20EF84FD00037B6C /* FetchedMediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032F5BB20EF84FD00037B6C /* FetchedMediaResource.swift */; };
|
||||
D032F5BD20EF84FD00037B6C /* FetchedMediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032F5BB20EF84FD00037B6C /* FetchedMediaResource.swift */; };
|
||||
D033FEB01E61EB0200644997 /* PeerReportStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEAF1E61EB0200644997 /* PeerReportStatus.swift */; };
|
||||
D033FEB11E61EB0200644997 /* PeerReportStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEAF1E61EB0200644997 /* PeerReportStatus.swift */; };
|
||||
D033FEB31E61F3C000644997 /* ReportPeer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB21E61F3C000644997 /* ReportPeer.swift */; };
|
||||
@ -811,6 +813,7 @@
|
||||
D02D60AA206BA64100FEFE1E /* VerifySecureIdValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifySecureIdValue.swift; sourceTree = "<group>"; };
|
||||
D03121011DA57E93006A2A60 /* TelegramPeerNotificationSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramPeerNotificationSettings.swift; sourceTree = "<group>"; };
|
||||
D03229F31E6B39700000AF9C /* ImportAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportAccount.swift; sourceTree = "<group>"; };
|
||||
D032F5BB20EF84FD00037B6C /* FetchedMediaResource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchedMediaResource.swift; sourceTree = "<group>"; };
|
||||
D033FEAF1E61EB0200644997 /* PeerReportStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerReportStatus.swift; sourceTree = "<group>"; };
|
||||
D033FEB21E61F3C000644997 /* ReportPeer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportPeer.swift; sourceTree = "<group>"; };
|
||||
D033FEB51E61F3F900644997 /* BlockedPeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockedPeers.swift; sourceTree = "<group>"; };
|
||||
@ -1448,6 +1451,7 @@
|
||||
D03B0D121D62257600955575 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D032F5BB20EF84FD00037B6C /* FetchedMediaResource.swift */,
|
||||
D0223A9A1EA5654D00211D94 /* TelegramMediaResource.swift */,
|
||||
D03B0D431D6319F900955575 /* CloudFileMediaResource.swift */,
|
||||
D0223A971EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift */,
|
||||
@ -2263,6 +2267,7 @@
|
||||
D0E35A121DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */,
|
||||
D093D806206539D000BC3599 /* SaveSecureIdValue.swift in Sources */,
|
||||
C239BE9C1E630CA700C2C453 /* UpdatePinnedMessage.swift in Sources */,
|
||||
D032F5BC20EF84FD00037B6C /* FetchedMediaResource.swift in Sources */,
|
||||
D08CAA7D1ED77EE90000FDA8 /* LocalizationSettings.swift in Sources */,
|
||||
D0B844531DAC0773005F29E1 /* TelegramUserPresence.swift in Sources */,
|
||||
D08F4A661E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift in Sources */,
|
||||
@ -2657,6 +2662,7 @@
|
||||
D01A21AA1F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift in Sources */,
|
||||
D0546495207386D7002ECC1E /* SecureIdUtilityBillValue.swift in Sources */,
|
||||
D03C53741DAD5CA9004C17B3 /* CachedChannelData.swift in Sources */,
|
||||
D032F5BD20EF84FD00037B6C /* FetchedMediaResource.swift in Sources */,
|
||||
D0B418861D7E056D004562A4 /* Namespaces.swift in Sources */,
|
||||
D05A32E51E6F0B2E002760B4 /* RecentAccountSessions.swift in Sources */,
|
||||
D02395D71F8D09A50070F5C2 /* ChannelHistoryAvailabilitySettings.swift in Sources */,
|
||||
|
||||
@ -452,10 +452,10 @@ public enum AccountNetworkState: Equatable {
|
||||
|
||||
public final class AccountAuxiliaryMethods {
|
||||
public let updatePeerChatInputState: (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?
|
||||
public let fetchResource: (Account, MediaResource, Signal<IndexSet, NoError>, MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError>?
|
||||
public let fetchResource: (Account, MediaResource, Signal<IndexSet, NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, NoError>?
|
||||
public let fetchResourceMediaReferenceHash: (MediaResource) -> Signal<Data?, NoError>
|
||||
|
||||
public init(updatePeerChatInputState: @escaping (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?, fetchResource: @escaping (Account, MediaResource, Signal<IndexSet, NoError>, MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError>?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal<Data?, NoError>) {
|
||||
public init(updatePeerChatInputState: @escaping (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?, fetchResource: @escaping (Account, MediaResource, Signal<IndexSet, NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, NoError>?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal<Data?, NoError>) {
|
||||
self.updatePeerChatInputState = updatePeerChatInputState
|
||||
self.fetchResource = fetchResource
|
||||
self.fetchResourceMediaReferenceHash = fetchResourceMediaReferenceHash
|
||||
@ -566,6 +566,7 @@ public class Account {
|
||||
public private(set) var viewTracker: AccountViewTracker!
|
||||
public private(set) var pendingMessageManager: PendingMessageManager!
|
||||
public private(set) var messageMediaPreuploadManager: MessageMediaPreuploadManager!
|
||||
private(set) var mediaReferenceRevalidationContext: MediaReferenceRevalidationContext!
|
||||
private var peerInputActivityManager: PeerInputActivityManager!
|
||||
private var localInputActivityManager: PeerInputActivityManager!
|
||||
fileprivate let managedContactsDisposable = MetaDisposable()
|
||||
@ -628,7 +629,8 @@ public class Account {
|
||||
self.localInputActivityManager = PeerInputActivityManager()
|
||||
self.viewTracker = AccountViewTracker(account: self)
|
||||
self.messageMediaPreuploadManager = MessageMediaPreuploadManager()
|
||||
self.pendingMessageManager = PendingMessageManager(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, stateManager: self.stateManager, messageMediaPreuploadManager: self.messageMediaPreuploadManager)
|
||||
self.mediaReferenceRevalidationContext = MediaReferenceRevalidationContext()
|
||||
self.pendingMessageManager = PendingMessageManager(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, stateManager: self.stateManager, messageMediaPreuploadManager: self.messageMediaPreuploadManager, revalidationContext: self.mediaReferenceRevalidationContext)
|
||||
|
||||
self.network.loggedOut = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
@ -775,8 +777,8 @@ public class Account {
|
||||
self.managedOperationsDisposable.add(managedSynchronizeInstalledStickerPacksOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager, namespace: .masks).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeMarkFeaturedStickerPacksAsSeenOperations(postbox: self.postbox, network: self.network).start())
|
||||
self.managedOperationsDisposable.add(managedRecentStickers(postbox: self.postbox, network: self.network).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeSavedGifsOperations(postbox: self.postbox, network: self.network).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeSavedStickersOperations(postbox: self.postbox, network: self.network).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeSavedGifsOperations(postbox: self.postbox, network: self.network, revalidationContext: self.mediaReferenceRevalidationContext).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeSavedStickersOperations(postbox: self.postbox, network: self.network, revalidationContext: self.mediaReferenceRevalidationContext).start())
|
||||
self.managedOperationsDisposable.add(managedRecentlyUsedInlineBots(postbox: self.postbox, network: self.network).start())
|
||||
self.managedOperationsDisposable.add(managedLocalTypingActivities(activities: self.localInputActivityManager.allActivities(), postbox: self.postbox, network: self.network, accountPeerId: self.peerId).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeConsumeMessageContentOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
@ -874,14 +876,14 @@ public func accountNetworkUsageStats(account: Account, reset: ResetNetworkUsageS
|
||||
}
|
||||
|
||||
public typealias FetchCachedResourceRepresentation = (_ account: Account, _ resource: MediaResource, _ resourceData: MediaResourceData, _ representation: CachedMediaResourceRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError>
|
||||
public typealias TransformOutgoingMessageMedia = (_ postbox: Postbox, _ network: Network, _ media: Media, _ userInteractive: Bool) -> Signal<Media?, NoError>
|
||||
public typealias TransformOutgoingMessageMedia = (_ postbox: Postbox, _ network: Network, _ media: AnyMediaReference, _ userInteractive: Bool) -> Signal<AnyMediaReference?, NoError>
|
||||
|
||||
public func setupAccount(_ account: Account, fetchCachedResourceRepresentation: FetchCachedResourceRepresentation? = nil, transformOutgoingMessageMedia: TransformOutgoingMessageMedia? = nil) {
|
||||
account.postbox.mediaBox.fetchResource = { [weak account] resource, ranges, tag -> Signal<MediaResourceDataFetchResult, NoError> in
|
||||
account.postbox.mediaBox.fetchResource = { [weak account] resource, ranges, parameters -> Signal<MediaResourceDataFetchResult, NoError> in
|
||||
if let strongAccount = account {
|
||||
if let result = fetchResource(account: strongAccount, resource: resource, ranges: ranges, tag: tag) {
|
||||
if let result = fetchResource(account: strongAccount, resource: resource, ranges: ranges, parameters: parameters) {
|
||||
return result
|
||||
} else if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, ranges, tag) {
|
||||
} else if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, ranges, parameters) {
|
||||
return result
|
||||
} else {
|
||||
return .never()
|
||||
|
||||
@ -137,25 +137,30 @@ public func updateAddressName(account: Account, domain: AddressNameDomain, name:
|
||||
|
||||
public func adminedPublicChannels(account: Account) -> Signal<[Peer], NoError> {
|
||||
return account.network.request(Api.functions.channels.getAdminedPublicChannels())
|
||||
|> retryRequest
|
||||
|> map { result -> [Peer] in
|
||||
var peers: [Peer] = []
|
||||
switch result {
|
||||
case let .chats(apiChats):
|
||||
for chat in apiChats {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<[Peer], NoError> in
|
||||
var peers: [Peer] = []
|
||||
switch result {
|
||||
case let .chats(apiChats):
|
||||
for chat in apiChats {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
case let .chatsSlice(_, apiChats):
|
||||
for chat in apiChats {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
case let .chatsSlice(_, apiChats):
|
||||
for chat in apiChats {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return account.postbox.transaction { transaction -> [Peer] in
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
return peers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChannelAddressNameAssignmentAvailability {
|
||||
@ -176,12 +181,12 @@ public func channelAddressNameAssignmentAvailability(account: Account, peerId: P
|
||||
}
|
||||
if let inputChannel = inputChannel {
|
||||
return account.network.request(Api.functions.channels.checkUsername(channel: inputChannel, username: "username"))
|
||||
|> map { _ -> ChannelAddressNameAssignmentAvailability in
|
||||
return .available
|
||||
}
|
||||
|> `catch` { error -> Signal<ChannelAddressNameAssignmentAvailability, NoError> in
|
||||
return .single(.addressNameLimitReached)
|
||||
}
|
||||
|> map { _ -> ChannelAddressNameAssignmentAvailability in
|
||||
return .available
|
||||
}
|
||||
|> `catch` { error -> Signal<ChannelAddressNameAssignmentAvailability, NoError> in
|
||||
return .single(.addressNameLimitReached)
|
||||
}
|
||||
} else {
|
||||
return .single(.unknown)
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[834148991] = { return Api.PageBlock.parse_pageBlockAudio($0) }
|
||||
dict[-614138572] = { return Api.account.TmpPassword.parse_tmpPassword($0) }
|
||||
dict[590459437] = { return Api.Photo.parse_photoEmpty($0) }
|
||||
dict[-1836524247] = { return Api.Photo.parse_photo($0) }
|
||||
dict[-1673036328] = { return Api.Photo.parse_photo($0) }
|
||||
dict[-1683826688] = { return Api.Chat.parse_chatEmpty($0) }
|
||||
dict[-652419756] = { return Api.Chat.parse_chat($0) }
|
||||
dict[120753115] = { return Api.Chat.parse_chatForbidden($0) }
|
||||
@ -227,7 +227,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1359533640] = { return Api.messages.FoundStickerSets.parse_foundStickerSets($0) }
|
||||
dict[1158290442] = { return Api.messages.FoundGifs.parse_foundGifs($0) }
|
||||
dict[2086234950] = { return Api.FileLocation.parse_fileLocationUnavailable($0) }
|
||||
dict[1406570614] = { return Api.FileLocation.parse_fileLocation($0) }
|
||||
dict[152900075] = { return Api.FileLocation.parse_fileLocation($0) }
|
||||
dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) }
|
||||
dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) }
|
||||
dict[1251338318] = { return Api.InputNotifyPeer.parse_inputNotifyChats($0) }
|
||||
@ -266,7 +266,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[326715557] = { return Api.auth.PasswordRecovery.parse_passwordRecovery($0) }
|
||||
dict[-1803769784] = { return Api.messages.BotResults.parse_botResults($0) }
|
||||
dict[1928391342] = { return Api.InputDocument.parse_inputDocumentEmpty($0) }
|
||||
dict[410618194] = { return Api.InputDocument.parse_inputDocument($0) }
|
||||
dict[448771445] = { return Api.InputDocument.parse_inputDocument($0) }
|
||||
dict[2131196633] = { return Api.contacts.ResolvedPeer.parse_resolvedPeer($0) }
|
||||
dict[-1964327229] = { return Api.SecureData.parse_secureData($0) }
|
||||
dict[-1771768449] = { return Api.InputMedia.parse_inputMediaEmpty($0) }
|
||||
@ -462,10 +462,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1157215293] = { return Api.Message.parse_message($0) }
|
||||
dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) }
|
||||
dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) }
|
||||
dict[342061462] = { return Api.InputFileLocation.parse_inputFileLocation($0) }
|
||||
dict[-182231723] = { return Api.InputFileLocation.parse_inputEncryptedFileLocation($0) }
|
||||
dict[1125058340] = { return Api.InputFileLocation.parse_inputDocumentFileLocation($0) }
|
||||
dict[-876089816] = { return Api.InputFileLocation.parse_inputSecureFileLocation($0) }
|
||||
dict[426148825] = { return Api.InputFileLocation.parse_inputDocumentFileLocation($0) }
|
||||
dict[-539317279] = { return Api.InputFileLocation.parse_inputFileLocation($0) }
|
||||
dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) }
|
||||
dict[43446532] = { return Api.GeoPoint.parse_geoPoint($0) }
|
||||
dict[506920429] = { return Api.InputPhoneCall.parse_inputPhoneCall($0) }
|
||||
@ -640,7 +640,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1687559349] = { return Api.MessageEntity.parse_messageEntityPhone($0) }
|
||||
dict[1280209983] = { return Api.MessageEntity.parse_messageEntityCashtag($0) }
|
||||
dict[483901197] = { return Api.InputPhoto.parse_inputPhotoEmpty($0) }
|
||||
dict[-74070332] = { return Api.InputPhoto.parse_inputPhoto($0) }
|
||||
dict[1001634122] = { return Api.InputPhoto.parse_inputPhoto($0) }
|
||||
dict[-567906571] = { return Api.contacts.TopPeers.parse_topPeersNotModified($0) }
|
||||
dict[1891070632] = { return Api.contacts.TopPeers.parse_topPeers($0) }
|
||||
dict[-1255369827] = { return Api.contacts.TopPeers.parse_topPeersDisabled($0) }
|
||||
@ -654,7 +654,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-94974410] = { return Api.EncryptedChat.parse_encryptedChat($0) }
|
||||
dict[332848423] = { return Api.EncryptedChat.parse_encryptedChatDiscarded($0) }
|
||||
dict[922273905] = { return Api.Document.parse_documentEmpty($0) }
|
||||
dict[-2027738169] = { return Api.Document.parse_document($0) }
|
||||
dict[1498631756] = { return Api.Document.parse_document($0) }
|
||||
dict[-1707344487] = { return Api.messages.HighScores.parse_highScores($0) }
|
||||
dict[-892779534] = { return Api.WebAuthorization.parse_webAuthorization($0) }
|
||||
dict[-805141448] = { return Api.ImportedContact.parse_importedContact($0) }
|
||||
|
||||
@ -973,7 +973,7 @@ extension Api {
|
||||
}
|
||||
enum Photo: TypeConstructorDescription {
|
||||
case photoEmpty(id: Int64)
|
||||
case photo(flags: Int32, id: Int64, accessHash: Int64, date: Int32, sizes: [Api.PhotoSize])
|
||||
case photo(flags: Int32, id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, sizes: [Api.PhotoSize])
|
||||
|
||||
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -983,13 +983,14 @@ extension Api {
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .photo(let flags, let id, let accessHash, let date, let sizes):
|
||||
case .photo(let flags, let id, let accessHash, let fileReference, let date, let sizes):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1836524247)
|
||||
buffer.appendInt32(-1673036328)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(sizes.count))
|
||||
@ -1004,8 +1005,8 @@ extension Api {
|
||||
switch self {
|
||||
case .photoEmpty(let id):
|
||||
return ("photoEmpty", [("id", id)])
|
||||
case .photo(let flags, let id, let accessHash, let date, let sizes):
|
||||
return ("photo", [("flags", flags), ("id", id), ("accessHash", accessHash), ("date", date), ("sizes", sizes)])
|
||||
case .photo(let flags, let id, let accessHash, let fileReference, let date, let sizes):
|
||||
return ("photo", [("flags", flags), ("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("date", date), ("sizes", sizes)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -1027,19 +1028,22 @@ extension Api {
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: [Api.PhotoSize]?
|
||||
var _4: Buffer?
|
||||
_4 = parseBytes(reader)
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: [Api.PhotoSize]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self)
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.Photo.photo(flags: _1!, id: _2!, accessHash: _3!, date: _4!, sizes: _5!)
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.Photo.photo(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, sizes: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -5439,7 +5443,7 @@ extension Api {
|
||||
}
|
||||
enum FileLocation: TypeConstructorDescription {
|
||||
case fileLocationUnavailable(volumeId: Int64, localId: Int32, secret: Int64)
|
||||
case fileLocation(dcId: Int32, volumeId: Int64, localId: Int32, secret: Int64)
|
||||
case fileLocation(dcId: Int32, volumeId: Int64, localId: Int32, secret: Int64, fileReference: Buffer)
|
||||
|
||||
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -5451,14 +5455,15 @@ extension Api {
|
||||
serializeInt32(localId, buffer: buffer, boxed: false)
|
||||
serializeInt64(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .fileLocation(let dcId, let volumeId, let localId, let secret):
|
||||
case .fileLocation(let dcId, let volumeId, let localId, let secret, let fileReference):
|
||||
if boxed {
|
||||
buffer.appendInt32(1406570614)
|
||||
buffer.appendInt32(152900075)
|
||||
}
|
||||
serializeInt32(dcId, buffer: buffer, boxed: false)
|
||||
serializeInt64(volumeId, buffer: buffer, boxed: false)
|
||||
serializeInt32(localId, buffer: buffer, boxed: false)
|
||||
serializeInt64(secret, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -5467,8 +5472,8 @@ extension Api {
|
||||
switch self {
|
||||
case .fileLocationUnavailable(let volumeId, let localId, let secret):
|
||||
return ("fileLocationUnavailable", [("volumeId", volumeId), ("localId", localId), ("secret", secret)])
|
||||
case .fileLocation(let dcId, let volumeId, let localId, let secret):
|
||||
return ("fileLocation", [("dcId", dcId), ("volumeId", volumeId), ("localId", localId), ("secret", secret)])
|
||||
case .fileLocation(let dcId, let volumeId, let localId, let secret, let fileReference):
|
||||
return ("fileLocation", [("dcId", dcId), ("volumeId", volumeId), ("localId", localId), ("secret", secret), ("fileReference", fileReference)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -5498,12 +5503,15 @@ extension Api {
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _5: Buffer?
|
||||
_5 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.FileLocation.fileLocation(dcId: _1!, volumeId: _2!, localId: _3!, secret: _4!)
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.FileLocation.fileLocation(dcId: _1!, volumeId: _2!, localId: _3!, secret: _4!, fileReference: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -6467,7 +6475,7 @@ extension Api {
|
||||
}
|
||||
enum InputDocument: TypeConstructorDescription {
|
||||
case inputDocumentEmpty
|
||||
case inputDocument(id: Int64, accessHash: Int64)
|
||||
case inputDocument(id: Int64, accessHash: Int64, fileReference: Buffer)
|
||||
|
||||
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -6477,12 +6485,13 @@ extension Api {
|
||||
}
|
||||
|
||||
break
|
||||
case .inputDocument(let id, let accessHash):
|
||||
case .inputDocument(let id, let accessHash, let fileReference):
|
||||
if boxed {
|
||||
buffer.appendInt32(410618194)
|
||||
buffer.appendInt32(448771445)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -6491,8 +6500,8 @@ extension Api {
|
||||
switch self {
|
||||
case .inputDocumentEmpty:
|
||||
return ("inputDocumentEmpty", [])
|
||||
case .inputDocument(let id, let accessHash):
|
||||
return ("inputDocument", [("id", id), ("accessHash", accessHash)])
|
||||
case .inputDocument(let id, let accessHash, let fileReference):
|
||||
return ("inputDocument", [("id", id), ("accessHash", accessHash), ("fileReference", fileReference)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -6504,10 +6513,13 @@ extension Api {
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputDocument.inputDocument(id: _1!, accessHash: _2!)
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputDocument.inputDocument(id: _1!, accessHash: _2!, fileReference: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -11616,21 +11628,13 @@ extension Api {
|
||||
|
||||
}
|
||||
enum InputFileLocation: TypeConstructorDescription {
|
||||
case inputFileLocation(volumeId: Int64, localId: Int32, secret: Int64)
|
||||
case inputEncryptedFileLocation(id: Int64, accessHash: Int64)
|
||||
case inputDocumentFileLocation(id: Int64, accessHash: Int64, version: Int32)
|
||||
case inputSecureFileLocation(id: Int64, accessHash: Int64)
|
||||
case inputDocumentFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer)
|
||||
case inputFileLocation(volumeId: Int64, localId: Int32, secret: Int64, fileReference: Buffer)
|
||||
|
||||
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputFileLocation(let volumeId, let localId, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(342061462)
|
||||
}
|
||||
serializeInt64(volumeId, buffer: buffer, boxed: false)
|
||||
serializeInt32(localId, buffer: buffer, boxed: false)
|
||||
serializeInt64(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputEncryptedFileLocation(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-182231723)
|
||||
@ -11638,14 +11642,6 @@ extension Api {
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputDocumentFileLocation(let id, let accessHash, let version):
|
||||
if boxed {
|
||||
buffer.appendInt32(1125058340)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeInt32(version, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputSecureFileLocation(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-876089816)
|
||||
@ -11653,39 +11649,39 @@ extension Api {
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputDocumentFileLocation(let id, let accessHash, let fileReference):
|
||||
if boxed {
|
||||
buffer.appendInt32(426148825)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputFileLocation(let volumeId, let localId, let secret, let fileReference):
|
||||
if boxed {
|
||||
buffer.appendInt32(-539317279)
|
||||
}
|
||||
serializeInt64(volumeId, buffer: buffer, boxed: false)
|
||||
serializeInt32(localId, buffer: buffer, boxed: false)
|
||||
serializeInt64(secret, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputFileLocation(let volumeId, let localId, let secret):
|
||||
return ("inputFileLocation", [("volumeId", volumeId), ("localId", localId), ("secret", secret)])
|
||||
case .inputEncryptedFileLocation(let id, let accessHash):
|
||||
return ("inputEncryptedFileLocation", [("id", id), ("accessHash", accessHash)])
|
||||
case .inputDocumentFileLocation(let id, let accessHash, let version):
|
||||
return ("inputDocumentFileLocation", [("id", id), ("accessHash", accessHash), ("version", version)])
|
||||
case .inputSecureFileLocation(let id, let accessHash):
|
||||
return ("inputSecureFileLocation", [("id", id), ("accessHash", accessHash)])
|
||||
case .inputDocumentFileLocation(let id, let accessHash, let fileReference):
|
||||
return ("inputDocumentFileLocation", [("id", id), ("accessHash", accessHash), ("fileReference", fileReference)])
|
||||
case .inputFileLocation(let volumeId, let localId, let secret, let fileReference):
|
||||
return ("inputFileLocation", [("volumeId", volumeId), ("localId", localId), ("secret", secret), ("fileReference", fileReference)])
|
||||
}
|
||||
}
|
||||
|
||||
static func parse_inputFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputFileLocation.inputFileLocation(volumeId: _1!, localId: _2!, secret: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
static func parse_inputEncryptedFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
@ -11700,23 +11696,6 @@ extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
static func parse_inputDocumentFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputFileLocation.inputDocumentFileLocation(id: _1!, accessHash: _2!, version: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
static func parse_inputSecureFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
@ -11731,6 +11710,43 @@ extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
static func parse_inputDocumentFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputFileLocation.inputDocumentFileLocation(id: _1!, accessHash: _2!, fileReference: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
static func parse_inputFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Buffer?
|
||||
_4 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.InputFileLocation.inputFileLocation(volumeId: _1!, localId: _2!, secret: _3!, fileReference: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
enum GeoPoint: TypeConstructorDescription {
|
||||
@ -15755,7 +15771,7 @@ extension Api {
|
||||
}
|
||||
enum InputPhoto: TypeConstructorDescription {
|
||||
case inputPhotoEmpty
|
||||
case inputPhoto(id: Int64, accessHash: Int64)
|
||||
case inputPhoto(id: Int64, accessHash: Int64, fileReference: Buffer)
|
||||
|
||||
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -15765,12 +15781,13 @@ extension Api {
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPhoto(let id, let accessHash):
|
||||
case .inputPhoto(let id, let accessHash, let fileReference):
|
||||
if boxed {
|
||||
buffer.appendInt32(-74070332)
|
||||
buffer.appendInt32(1001634122)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -15779,8 +15796,8 @@ extension Api {
|
||||
switch self {
|
||||
case .inputPhotoEmpty:
|
||||
return ("inputPhotoEmpty", [])
|
||||
case .inputPhoto(let id, let accessHash):
|
||||
return ("inputPhoto", [("id", id), ("accessHash", accessHash)])
|
||||
case .inputPhoto(let id, let accessHash, let fileReference):
|
||||
return ("inputPhoto", [("id", id), ("accessHash", accessHash), ("fileReference", fileReference)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -15792,10 +15809,13 @@ extension Api {
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPhoto.inputPhoto(id: _1!, accessHash: _2!)
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputPhoto.inputPhoto(id: _1!, accessHash: _2!, fileReference: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -15979,7 +15999,7 @@ extension Api {
|
||||
}
|
||||
enum Document: TypeConstructorDescription {
|
||||
case documentEmpty(id: Int64)
|
||||
case document(id: Int64, accessHash: Int64, date: Int32, mimeType: String, size: Int32, thumb: Api.PhotoSize, dcId: Int32, version: Int32, attributes: [Api.DocumentAttribute])
|
||||
case document(id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, mimeType: String, size: Int32, thumb: Api.PhotoSize, dcId: Int32, attributes: [Api.DocumentAttribute])
|
||||
|
||||
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -15989,18 +16009,18 @@ extension Api {
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .document(let id, let accessHash, let date, let mimeType, let size, let thumb, let dcId, let version, let attributes):
|
||||
case .document(let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumb, let dcId, let attributes):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2027738169)
|
||||
buffer.appendInt32(1498631756)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeString(mimeType, buffer: buffer, boxed: false)
|
||||
serializeInt32(size, buffer: buffer, boxed: false)
|
||||
thumb.serialize(buffer, true)
|
||||
serializeInt32(dcId, buffer: buffer, boxed: false)
|
||||
serializeInt32(version, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(attributes.count))
|
||||
for item in attributes {
|
||||
@ -16014,8 +16034,8 @@ extension Api {
|
||||
switch self {
|
||||
case .documentEmpty(let id):
|
||||
return ("documentEmpty", [("id", id)])
|
||||
case .document(let id, let accessHash, let date, let mimeType, let size, let thumb, let dcId, let version, let attributes):
|
||||
return ("document", [("id", id), ("accessHash", accessHash), ("date", date), ("mimeType", mimeType), ("size", size), ("thumb", thumb), ("dcId", dcId), ("version", version), ("attributes", attributes)])
|
||||
case .document(let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumb, let dcId, let attributes):
|
||||
return ("document", [("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("date", date), ("mimeType", mimeType), ("size", size), ("thumb", thumb), ("dcId", dcId), ("attributes", attributes)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -16035,18 +16055,18 @@ extension Api {
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: Api.PhotoSize?
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: Api.PhotoSize?
|
||||
if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.PhotoSize
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.PhotoSize
|
||||
}
|
||||
var _7: Int32?
|
||||
_7 = reader.readInt32()
|
||||
var _8: Int32?
|
||||
_8 = reader.readInt32()
|
||||
var _9: [Api.DocumentAttribute]?
|
||||
@ -16063,7 +16083,7 @@ extension Api {
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.Document.document(id: _1!, accessHash: _2!, date: _3!, mimeType: _4!, size: _5!, thumb: _6!, dcId: _7!, version: _8!, attributes: _9!)
|
||||
return Api.Document.document(id: _1!, accessHash: _2!, fileReference: _3!, date: _4!, mimeType: _5!, size: _6!, thumb: _7!, dcId: _8!, attributes: _9!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
||||
@ -5,6 +5,101 @@ import Foundation
|
||||
import Postbox
|
||||
#endif
|
||||
|
||||
public enum PeerReference: PostboxCoding, Hashable, Equatable {
|
||||
case user(id: Int32, accessHash: Int64)
|
||||
case group(id: Int32)
|
||||
case channel(id: Int32, accessHash: Int64)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("_r", orElse: 0) {
|
||||
case 0:
|
||||
self = .user(id: decoder.decodeInt32ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0))
|
||||
case 1:
|
||||
self = .group(id: decoder.decodeInt32ForKey("i", orElse: 0))
|
||||
case 2:
|
||||
self = .channel(id: decoder.decodeInt32ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0))
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .user(id: 0, accessHash: 0)
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .user(id, accessHash):
|
||||
encoder.encodeInt32(0, forKey: "_r")
|
||||
encoder.encodeInt32(id, forKey: "i")
|
||||
encoder.encodeInt64(accessHash, forKey: "h")
|
||||
case let .group(id):
|
||||
encoder.encodeInt32(1, forKey: "_r")
|
||||
encoder.encodeInt32(id, forKey: "i")
|
||||
case let .channel(id, accessHash):
|
||||
encoder.encodeInt32(2, forKey: "_r")
|
||||
encoder.encodeInt32(id, forKey: "i")
|
||||
encoder.encodeInt64(accessHash, forKey: "h")
|
||||
}
|
||||
}
|
||||
|
||||
var id: PeerId {
|
||||
switch self {
|
||||
case let .user(id, _):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudUser, id: id)
|
||||
case let .group(id):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudGroup, id: id)
|
||||
case let .channel(id, _):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudChannel, id: id)
|
||||
}
|
||||
}
|
||||
|
||||
public init?(_ peer: Peer) {
|
||||
switch peer {
|
||||
case let user as TelegramUser:
|
||||
if let accessHash = user.accessHash {
|
||||
self = .user(id: user.id.id, accessHash: accessHash)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case let group as TelegramGroup:
|
||||
self = .group(id: group.id.id)
|
||||
case let channel as TelegramChannel:
|
||||
if let accessHash = channel.accessHash {
|
||||
self = .channel(id: channel.id.id, accessHash: accessHash)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var inputPeer: Api.InputPeer {
|
||||
switch self {
|
||||
case let .user(id, accessHash):
|
||||
return .inputPeerUser(userId: id, accessHash: accessHash)
|
||||
case let .group(id):
|
||||
return .inputPeerChat(chatId: id)
|
||||
case let .channel(id, accessHash):
|
||||
return .inputPeerChannel(channelId: id, accessHash: accessHash)
|
||||
}
|
||||
}
|
||||
|
||||
var inputUser: Api.InputUser? {
|
||||
if case let .user(id, accessHash) = self {
|
||||
return .inputUser(userId: id, accessHash: accessHash)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var inputChannel: Api.InputChannel? {
|
||||
if case let .channel(id, accessHash) = self {
|
||||
return .inputChannel(channelId: id, accessHash: accessHash)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func apiInputPeer(_ peer: Peer) -> Api.InputPeer? {
|
||||
switch peer {
|
||||
case let user as TelegramUser where user.accessHash != nil:
|
||||
|
||||
@ -130,7 +130,7 @@ public func togglePeerUnreadMarkInteractively(postbox: Postbox, viewTracker: Acc
|
||||
let _ = transaction.applyInteractiveReadMaxIndex(index)
|
||||
}
|
||||
viewTracker.updateMarkAllMentionsSeen(peerId: peerId)
|
||||
} else {
|
||||
} else if namespace == Namespaces.Message.Cloud || namespace == Namespaces.Message.SecretIncoming {
|
||||
transaction.applyMarkUnread(peerId: peerId, namespace: namespace, value: true, interactive: true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ func applyMediaResourceChanges(from: Media, to: Media, postbox: Postbox) {
|
||||
if let fromPreview = smallestImageRepresentation(fromFile.previewRepresentations), let toPreview = smallestImageRepresentation(toFile.previewRepresentations) {
|
||||
postbox.mediaBox.moveResourceData(from: fromPreview.resource.id, to: toPreview.resource.id)
|
||||
}
|
||||
if fromFile.size == toFile.size && fromFile.mimeType == toFile.mimeType {
|
||||
if (fromFile.size == toFile.size || fromFile.resource.size == toFile.resource.size) && fromFile.mimeType == toFile.mimeType {
|
||||
postbox.mediaBox.moveResourceData(from: fromFile.resource.id, to: toFile.resource.id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +50,10 @@ public enum CachedStickerPackResult {
|
||||
case result(StickerPackCollectionInfo, [ItemCollectionItem], Bool)
|
||||
}
|
||||
|
||||
func cacheStickerPack(transaction: Transaction, info: StickerPackCollectionInfo, items: [ItemCollectionItem]) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(info.id)), entry: CachedStickerPack(info: info, items: items.map { $0 as! StickerPackItem }, hash: info.hash), collectionSpec: collectionSpec)
|
||||
}
|
||||
|
||||
public func cachedStickerPack(postbox: Postbox, network: Network, reference: StickerPackReference) -> Signal<CachedStickerPackResult, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<CachedStickerPackResult, NoError> in
|
||||
let namespace = Namespaces.ItemCollection.CloudStickerPacks
|
||||
@ -72,25 +76,27 @@ public func cachedStickerPack(postbox: Postbox, network: Network, reference: Sti
|
||||
|
||||
var signal = current
|
||||
if loadRemote {
|
||||
let appliedRemote = remoteStickerPack(network: network, reference: reference)
|
||||
|> mapToSignal { result -> Signal<CachedStickerPackResult, NoError> in
|
||||
return postbox.transaction { transaction -> CachedStickerPackResult in
|
||||
if let result = result {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(result.0.id)), entry: CachedStickerPack(info: result.0, items: result.1.map { $0 as! StickerPackItem }, hash: result.0.hash), collectionSpec: collectionSpec)
|
||||
|
||||
let currentInfo = transaction.getItemCollectionInfo(collectionId: result.0.id) as? StickerPackCollectionInfo
|
||||
|
||||
return .result(result.0, result.1, currentInfo != nil)
|
||||
} else {
|
||||
return .none
|
||||
}
|
||||
let appliedRemote = updatedRemoteStickerPack(postbox: postbox, network: network, reference: reference)
|
||||
|> mapToSignal { result -> Signal<CachedStickerPackResult, NoError> in
|
||||
return postbox.transaction { transaction -> CachedStickerPackResult in
|
||||
if let result = result {
|
||||
cacheStickerPack(transaction: transaction, info: result.0, items: result.1)
|
||||
|
||||
let currentInfo = transaction.getItemCollectionInfo(collectionId: result.0.id) as? StickerPackCollectionInfo
|
||||
|
||||
return .result(result.0, result.1, currentInfo != nil)
|
||||
} else {
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal = signal |> then(appliedRemote)
|
||||
signal = signal
|
||||
|> then(appliedRemote)
|
||||
}
|
||||
|
||||
return signal
|
||||
}
|
||||
} |> switchToLatest
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@ import Foundation
|
||||
|
||||
public func checkPeerChatServiceActions(postbox: Postbox, peerId: PeerId) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
transaction.applyMarkUnread(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, value: false, interactive: true)
|
||||
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
if let state = transaction.getPeerChatState(peerId) as? SecretChatState {
|
||||
let updatedState = secretChatCheckLayerNegotiationIfNeeded(transaction: transaction, peerId: peerId, state: state)
|
||||
|
||||
@ -6,7 +6,7 @@ import Foundation
|
||||
#endif
|
||||
|
||||
protocol TelegramCloudMediaResource: TelegramMediaResource {
|
||||
var apiInputLocation: Api.InputFileLocation { get }
|
||||
func apiInputLocation(fileReference: Data?) -> Api.InputFileLocation?
|
||||
}
|
||||
|
||||
protocol TelegramMultipartFetchableResource: TelegramMediaResource {
|
||||
@ -49,21 +49,27 @@ public class CloudFileMediaResource: TelegramCloudMediaResource, TelegramMultipa
|
||||
public let localId: Int32
|
||||
public let secret: Int64
|
||||
public let size: Int?
|
||||
public let fileReference: Data?
|
||||
|
||||
public var id: MediaResourceId {
|
||||
return CloudFileMediaResourceId(datacenterId: self.datacenterId, volumeId: self.volumeId, localId: self.localId, secret: self.secret)
|
||||
}
|
||||
|
||||
var apiInputLocation: Api.InputFileLocation {
|
||||
return Api.InputFileLocation.inputFileLocation(volumeId: self.volumeId, localId: self.localId, secret: self.secret)
|
||||
func apiInputLocation(fileReference: Data?) -> Api.InputFileLocation? {
|
||||
if let fileReference = fileReference {
|
||||
return Api.InputFileLocation.inputFileLocation(volumeId: self.volumeId, localId: self.localId, secret: self.secret, fileReference: Buffer(data: fileReference))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public init(datacenterId: Int, volumeId: Int64, localId: Int32, secret: Int64, size: Int?) {
|
||||
public init(datacenterId: Int, volumeId: Int64, localId: Int32, secret: Int64, size: Int?, fileReference: Data?) {
|
||||
self.datacenterId = datacenterId
|
||||
self.volumeId = volumeId
|
||||
self.localId = localId
|
||||
self.secret = secret
|
||||
self.size = size
|
||||
self.fileReference = fileReference
|
||||
}
|
||||
|
||||
public required init(decoder: PostboxDecoder) {
|
||||
@ -76,6 +82,7 @@ public class CloudFileMediaResource: TelegramCloudMediaResource, TelegramMultipa
|
||||
} else {
|
||||
self.size = nil
|
||||
}
|
||||
self.fileReference = decoder.decodeBytesForKey("fr")?.makeData()
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -88,11 +95,16 @@ public class CloudFileMediaResource: TelegramCloudMediaResource, TelegramMultipa
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "n")
|
||||
}
|
||||
if let fileReference = self.fileReference {
|
||||
encoder.encodeBytes(MemoryBuffer(data: fileReference), forKey: "fr")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "fr")
|
||||
}
|
||||
}
|
||||
|
||||
public func isEqual(to: TelegramMediaResource) -> Bool {
|
||||
if let to = to as? CloudFileMediaResource {
|
||||
return self.datacenterId == to.datacenterId && self.volumeId == to.volumeId && self.localId == to.localId && self.secret == to.secret && self.size == to.size
|
||||
return self.datacenterId == to.datacenterId && self.volumeId == to.volumeId && self.localId == to.localId && self.secret == to.secret && self.size == to.size && self.fileReference == to.fileReference
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
@ -102,16 +114,14 @@ public class CloudFileMediaResource: TelegramCloudMediaResource, TelegramMultipa
|
||||
public struct CloudDocumentMediaResourceId: MediaResourceId {
|
||||
let datacenterId: Int
|
||||
let fileId: Int64
|
||||
let accessHash: Int64
|
||||
|
||||
init(datacenterId: Int, fileId: Int64, accessHash: Int64) {
|
||||
init(datacenterId: Int, fileId: Int64) {
|
||||
self.datacenterId = datacenterId
|
||||
self.fileId = fileId
|
||||
self.accessHash = accessHash
|
||||
}
|
||||
|
||||
public var uniqueId: String {
|
||||
return "telegram-cloud-document-\(self.datacenterId)-\(self.fileId)-\(self.accessHash)"
|
||||
return "telegram-cloud-document-\(self.datacenterId)-\(self.fileId)"
|
||||
}
|
||||
|
||||
public var hashValue: Int {
|
||||
@ -120,7 +130,7 @@ public struct CloudDocumentMediaResourceId: MediaResourceId {
|
||||
|
||||
public func isEqual(to: MediaResourceId) -> Bool {
|
||||
if let to = to as? CloudDocumentMediaResourceId {
|
||||
return self.datacenterId == to.datacenterId && self.fileId == to.fileId && self.accessHash == to.accessHash
|
||||
return self.datacenterId == to.datacenterId && self.fileId == to.fileId
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
@ -132,20 +142,26 @@ public class CloudDocumentMediaResource: TelegramCloudMediaResource, TelegramMul
|
||||
let fileId: Int64
|
||||
public let accessHash: Int64
|
||||
public let size: Int?
|
||||
public let fileReference: Data?
|
||||
|
||||
public var id: MediaResourceId {
|
||||
return CloudDocumentMediaResourceId(datacenterId: self.datacenterId, fileId: self.fileId, accessHash: self.accessHash)
|
||||
return CloudDocumentMediaResourceId(datacenterId: self.datacenterId, fileId: self.fileId)
|
||||
}
|
||||
|
||||
var apiInputLocation: Api.InputFileLocation {
|
||||
return Api.InputFileLocation.inputDocumentFileLocation(id: self.fileId, accessHash: self.accessHash, version: 0)
|
||||
func apiInputLocation(fileReference: Data?) -> Api.InputFileLocation? {
|
||||
if let fileReference = fileReference {
|
||||
return Api.InputFileLocation.inputDocumentFileLocation(id: self.fileId, accessHash: self.accessHash, fileReference: Buffer(data: fileReference))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public init(datacenterId: Int, fileId: Int64, accessHash: Int64, size: Int?) {
|
||||
public init(datacenterId: Int, fileId: Int64, accessHash: Int64, size: Int?, fileReference: Data?) {
|
||||
self.datacenterId = datacenterId
|
||||
self.fileId = fileId
|
||||
self.accessHash = accessHash
|
||||
self.size = size
|
||||
self.fileReference = fileReference
|
||||
}
|
||||
|
||||
public required init(decoder: PostboxDecoder) {
|
||||
@ -157,6 +173,7 @@ public class CloudDocumentMediaResource: TelegramCloudMediaResource, TelegramMul
|
||||
} else {
|
||||
self.size = nil
|
||||
}
|
||||
self.fileReference = decoder.decodeBytesForKey("fr")?.makeData()
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -168,11 +185,16 @@ public class CloudDocumentMediaResource: TelegramCloudMediaResource, TelegramMul
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "n")
|
||||
}
|
||||
if let fileReference = self.fileReference {
|
||||
encoder.encodeBytes(MemoryBuffer(data: fileReference), forKey: "fr")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "fr")
|
||||
}
|
||||
}
|
||||
|
||||
public func isEqual(to: TelegramMediaResource) -> Bool {
|
||||
if let to = to as? CloudDocumentMediaResource {
|
||||
return self.datacenterId == to.datacenterId && self.fileId == to.fileId && self.accessHash == to.accessHash && self.size == to.size
|
||||
return self.datacenterId == to.datacenterId && self.fileId == to.fileId && self.accessHash == to.accessHash && self.size == to.size && self.fileReference == to.fileReference
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
@ -463,7 +485,7 @@ public struct SecretFileMediaResource: TelegramCloudMediaResource, TelegramMulti
|
||||
public let datacenterId: Int
|
||||
public let key: SecretFileEncryptionKey
|
||||
|
||||
var apiInputLocation: Api.InputFileLocation {
|
||||
func apiInputLocation(fileReference: Data?) -> Api.InputFileLocation? {
|
||||
return .inputEncryptedFileLocation(id: self.fileId, accessHash: self.accessHash)
|
||||
}
|
||||
|
||||
@ -560,8 +582,8 @@ public final class EmptyMediaResource: TelegramMediaResource {
|
||||
|
||||
func mediaResourceFromApiFileLocation(_ fileLocation: Api.FileLocation, size: Int?) -> TelegramMediaResource? {
|
||||
switch fileLocation {
|
||||
case let .fileLocation(dcId, volumeId, localId, secret):
|
||||
return CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size)
|
||||
case let .fileLocation(dcId, volumeId, localId, secret, fileReference):
|
||||
return CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size, fileReference: fileReference.makeData())
|
||||
case .fileLocationUnavailable:
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -73,9 +73,9 @@ private func filterMessageAttributesForForwardedMessage(_ attributes: [MessageAt
|
||||
}
|
||||
}
|
||||
|
||||
func opportunisticallyTransformMessageWithMedia(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia, media: Media, userInteractive: Bool) -> Signal<Media?, NoError> {
|
||||
return transformOutgoingMessageMedia(postbox, network, media, userInteractive)
|
||||
|> timeout(2.0, queue: Queue.concurrentDefaultQueue(), alternate: .single(nil))
|
||||
func opportunisticallyTransformMessageWithMedia(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia, mediaReference: AnyMediaReference, userInteractive: Bool) -> Signal<AnyMediaReference?, NoError> {
|
||||
return transformOutgoingMessageMedia(postbox, network, mediaReference, userInteractive)
|
||||
|> timeout(2.0, queue: Queue.concurrentDefaultQueue(), alternate: .single(nil))
|
||||
}
|
||||
|
||||
private func opportunisticallyTransformOutgoingMedia(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia, messages: [EnqueueMessage], userInteractive: Bool) -> Signal<[(Bool, EnqueueMessage)], NoError> {
|
||||
@ -101,9 +101,9 @@ private func opportunisticallyTransformOutgoingMedia(network: Network, postbox:
|
||||
switch message {
|
||||
case let .message(text, attributes, media, replyToMessageId, localGroupingKey):
|
||||
if let media = media {
|
||||
signals.append(opportunisticallyTransformMessageWithMedia(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, media: media, userInteractive: userInteractive) |> map { result -> (Bool, EnqueueMessage) in
|
||||
signals.append(opportunisticallyTransformMessageWithMedia(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, mediaReference: .standalone(media: media), userInteractive: userInteractive) |> map { result -> (Bool, EnqueueMessage) in
|
||||
if let result = result {
|
||||
return (true, .message(text: text, attributes: attributes, media: result, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey))
|
||||
return (true, .message(text: text, attributes: attributes, media: result.media, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey))
|
||||
} else {
|
||||
return (false, .message(text: text, attributes: attributes, media: media, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey))
|
||||
}
|
||||
@ -187,6 +187,9 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
||||
if case let .message(desc) = message, let replyToMessageId = desc.replyToMessageId, replyToMessageId.peerId != peerId {
|
||||
if let replyMessage = transaction.getMessage(replyToMessageId) {
|
||||
var canBeForwarded = true
|
||||
if replyMessage.id.namespace != Namespaces.Message.Cloud {
|
||||
canBeForwarded = false
|
||||
}
|
||||
inner: for media in replyMessage.media {
|
||||
if media is TelegramMediaAction {
|
||||
canBeForwarded = false
|
||||
@ -337,7 +340,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
||||
|
||||
var forwardInfo: StoreMessageForwardInfo?
|
||||
|
||||
if peerId.namespace != Namespaces.Peer.SecretChat {
|
||||
if sourceMessage.id.namespace == Namespaces.Message.Cloud && peerId.namespace != Namespaces.Peer.SecretChat {
|
||||
attributes.append(ForwardSourceInfoAttribute(messageId: sourceMessage.id))
|
||||
|
||||
if peerId == account.peerId {
|
||||
|
||||
@ -9,8 +9,8 @@ import SwiftSignalKit
|
||||
import Photos
|
||||
#endif
|
||||
|
||||
private func fetchCloudMediaLocation(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int?, ranges: Signal<IndexSet, NoError>, tag: MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
return multipartFetch(account: account, resource: resource, datacenterId: datacenterId, size: size, ranges: ranges, tag: tag)
|
||||
private func fetchCloudMediaLocation(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int?, ranges: Signal<IndexSet, NoError>, parameters: MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
return multipartFetch(account: account, resource: resource, datacenterId: datacenterId, size: size, ranges: ranges, parameters: parameters)
|
||||
}
|
||||
|
||||
private func fetchLocalFileResource(path: String, move: Bool) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
@ -30,17 +30,17 @@ private func fetchLocalFileResource(path: String, move: Bool) -> Signal<MediaRes
|
||||
}
|
||||
}
|
||||
|
||||
func fetchResource(account: Account, resource: MediaResource, ranges: Signal<IndexSet, NoError>, tag: MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError>? {
|
||||
func fetchResource(account: Account, resource: MediaResource, ranges: Signal<IndexSet, NoError>, parameters: MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, NoError>? {
|
||||
if let _ = resource as? EmptyMediaResource {
|
||||
return .single(.reset) |> then(.never())
|
||||
} else if let secretFileResource = resource as? SecretFileMediaResource {
|
||||
return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchSecretFileResource(account: account, resource: secretFileResource, ranges: ranges, tag: tag))
|
||||
return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchSecretFileResource(account: account, resource: secretFileResource, ranges: ranges, parameters: parameters))
|
||||
} else if let cloudResource = resource as? TelegramMultipartFetchableResource {
|
||||
return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, datacenterId: cloudResource.datacenterId, size: resource.size == 0 ? nil : resource.size, ranges: ranges, tag: tag))
|
||||
return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, datacenterId: cloudResource.datacenterId, size: resource.size == 0 ? nil : resource.size, ranges: ranges, parameters: parameters))
|
||||
} else if let webFileResource = resource as? WebFileReferenceMediaResource {
|
||||
return currentWebDocumentsHostDatacenterId(postbox: account.postbox, isTestingEnvironment: account.testingEnvironment)
|
||||
|> mapToSignal { datacenterId -> Signal<MediaResourceDataFetchResult, NoError> in
|
||||
return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: webFileResource, datacenterId: Int(datacenterId), size: resource.size == 0 ? nil : resource.size, ranges: ranges, tag: tag))
|
||||
return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: webFileResource, datacenterId: Int(datacenterId), size: resource.size == 0 ? nil : resource.size, ranges: ranges, parameters: parameters))
|
||||
}
|
||||
} else if let localFileResource = resource as? LocalFileReferenceMediaResource {
|
||||
return fetchLocalFileResource(path: localFileResource.localFilePath, move: localFileResource.isUniquelyReferencedTemporaryFile)
|
||||
|
||||
@ -9,6 +9,6 @@ import Foundation
|
||||
import MtProtoKitDynamic
|
||||
#endif
|
||||
|
||||
func fetchSecretFileResource(account: Account, resource: SecretFileMediaResource, ranges: Signal<IndexSet, NoError>, tag: MediaResourceFetchTag?) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
return multipartFetch(account: account, resource: resource, datacenterId: resource.datacenterId, size: resource.size, ranges: ranges, tag: tag, encryptionKey: resource.key, decryptedSize: resource.decryptedSize)
|
||||
func fetchSecretFileResource(account: Account, resource: SecretFileMediaResource, ranges: Signal<IndexSet, NoError>, parameters: MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
return multipartFetch(account: account, resource: resource, datacenterId: resource.datacenterId, size: resource.size, ranges: ranges, parameters: parameters, encryptionKey: resource.key, decryptedSize: resource.decryptedSize)
|
||||
}
|
||||
|
||||
774
TelegramCore/FetchedMediaResource.swift
Normal file
774
TelegramCore/FetchedMediaResource.swift
Normal file
@ -0,0 +1,774 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
#endif
|
||||
|
||||
public struct MessageReference: PostboxCoding, Hashable, Equatable {
|
||||
let content: MessageReferenceContent
|
||||
|
||||
public init(_ message: Message) {
|
||||
if let peer = message.peers[message.id.peerId], let inputPeer = PeerReference(peer) {
|
||||
self.content = .message(peer: inputPeer, id: message.id)
|
||||
} else {
|
||||
self.content = .none
|
||||
}
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.content = decoder.decodeObjectForKey("c", decoder: { MessageReferenceContent(decoder: $0) }) as! MessageReferenceContent
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeObject(self.content, forKey: "c")
|
||||
}
|
||||
}
|
||||
|
||||
enum MessageReferenceContent: PostboxCoding, Hashable, Equatable {
|
||||
case none
|
||||
case message(peer: PeerReference, id: MessageId)
|
||||
|
||||
init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("_r", orElse: 0) {
|
||||
case 0:
|
||||
self = .none
|
||||
case 1:
|
||||
self = .message(peer: decoder.decodeObjectForKey("p", decoder: { PeerReference(decoder: $0) }) as! PeerReference, id: MessageId(peerId: PeerId(decoder.decodeInt64ForKey("i.p", orElse: 0)), namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt32ForKey("i.i", orElse: 0)))
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .none
|
||||
}
|
||||
}
|
||||
|
||||
func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case .none:
|
||||
encoder.encodeInt32(0, forKey: "_r")
|
||||
case let .message(peer, id):
|
||||
encoder.encodeInt32(1, forKey: "_r")
|
||||
encoder.encodeObject(peer, forKey: "p")
|
||||
encoder.encodeInt64(id.peerId.toInt64(), forKey: "i.p")
|
||||
encoder.encodeInt32(id.namespace, forKey: "i.n")
|
||||
encoder.encodeInt32(id.id, forKey: "i.i")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct WebpageReference: PostboxCoding, Hashable, Equatable {
|
||||
let content: WebpageReferenceContent
|
||||
|
||||
public init(_ webPage: TelegramMediaWebpage) {
|
||||
if case let .Loaded(content) = webPage.content {
|
||||
self.content = .webPage(id: webPage.webpageId.id, url: content.url)
|
||||
} else {
|
||||
self.content = .none
|
||||
}
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.content = decoder.decodeObjectForKey("c", decoder: { WebpageReferenceContent(decoder: $0) }) as! WebpageReferenceContent
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeObject(self.content, forKey: "c")
|
||||
}
|
||||
}
|
||||
|
||||
enum WebpageReferenceContent: PostboxCoding, Hashable, Equatable {
|
||||
case none
|
||||
case webPage(id: Int64, url: String)
|
||||
|
||||
init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("_r", orElse: 0) {
|
||||
case 0:
|
||||
self = .none
|
||||
case 1:
|
||||
self = .webPage(id: decoder.decodeInt64ForKey("i", orElse: 0), url: decoder.decodeStringForKey("u", orElse: ""))
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .none
|
||||
}
|
||||
}
|
||||
|
||||
func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case .none:
|
||||
encoder.encodeInt32(0, forKey: "_r")
|
||||
case let .webPage(id, url):
|
||||
encoder.encodeInt32(1, forKey: "_r")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
encoder.encodeString(url, forKey: "u")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum AnyMediaReference: Equatable {
|
||||
case standalone(media: Media)
|
||||
case message(message: MessageReference, media: Media)
|
||||
case webPage(webPage: WebpageReference, media: Media)
|
||||
case stickerPack(stickerPack: StickerPackReference, media: Media)
|
||||
case savedGif(media: Media)
|
||||
|
||||
public static func ==(lhs: AnyMediaReference, rhs: AnyMediaReference) -> Bool {
|
||||
switch lhs {
|
||||
case let .standalone(lhsMedia):
|
||||
if case let .standalone(rhsMedia) = rhs, lhsMedia.isEqual(rhsMedia) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .message(lhsMessage, lhsMedia):
|
||||
if case let .message(rhsMessage, rhsMedia) = rhs, lhsMessage == rhsMessage, lhsMedia.isEqual(rhsMedia) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .webPage(lhsWebPage, lhsMedia):
|
||||
if case let .webPage(rhsWebPage, rhsMedia) = rhs, lhsWebPage == rhsWebPage, lhsMedia.isEqual(rhsMedia) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .stickerPack(lhsStickerPack, lhsMedia):
|
||||
if case let .stickerPack(rhsStickerPack, rhsMedia) = rhs, lhsStickerPack == rhsStickerPack, lhsMedia.isEqual(rhsMedia) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .savedGif(lhsMedia):
|
||||
if case let .savedGif(rhsMedia) = rhs, lhsMedia.isEqual(rhsMedia) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func concrete<T: Media>(_ type: T.Type) -> MediaReference<T>? {
|
||||
switch self {
|
||||
case let .standalone(media):
|
||||
if let media = media as? T {
|
||||
return .standalone(media: media)
|
||||
}
|
||||
case let .message(message, media):
|
||||
if let media = media as? T {
|
||||
return .message(message: message, media: media)
|
||||
}
|
||||
case let .webPage(webPage, media):
|
||||
if let media = media as? T {
|
||||
return .webPage(webPage: webPage, media: media)
|
||||
}
|
||||
case let .stickerPack(stickerPack, media):
|
||||
if let media = media as? T {
|
||||
return .stickerPack(stickerPack: stickerPack, media: media)
|
||||
}
|
||||
case let .savedGif(media):
|
||||
if let media = media as? T {
|
||||
return .savedGif(media: media)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public var media: Media {
|
||||
switch self {
|
||||
case let .standalone(media):
|
||||
return media
|
||||
case let .message(_, media):
|
||||
return media
|
||||
case let .webPage(_, media):
|
||||
return media
|
||||
case let .stickerPack(_, media):
|
||||
return media
|
||||
case let .savedGif(media):
|
||||
return media
|
||||
}
|
||||
}
|
||||
|
||||
public func resourceReference(_ resource: MediaResource) -> MediaResourceReference {
|
||||
return .media(media: self, resource: resource)
|
||||
}
|
||||
}
|
||||
|
||||
public enum MediaReference<T: Media> {
|
||||
private enum CodingCase: Int32 {
|
||||
case standalone
|
||||
case message
|
||||
case webPage
|
||||
case stickerPack
|
||||
case savedGif
|
||||
}
|
||||
|
||||
case standalone(media: T)
|
||||
case message(message: MessageReference, media: T)
|
||||
case webPage(webPage: WebpageReference, media: T)
|
||||
case stickerPack(stickerPack: StickerPackReference, media: T)
|
||||
case savedGif(media: T)
|
||||
|
||||
init?(decoder: PostboxDecoder) {
|
||||
guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else {
|
||||
return nil
|
||||
}
|
||||
switch caseId {
|
||||
case .standalone:
|
||||
guard let media = decoder.decodeObjectForKey("m") as? T else {
|
||||
return nil
|
||||
}
|
||||
self = .standalone(media: media)
|
||||
case .message:
|
||||
let message = decoder.decodeObjectForKey("msg", decoder: { MessageReference(decoder: $0) }) as! MessageReference
|
||||
guard let media = decoder.decodeObjectForKey("m") as? T else {
|
||||
return nil
|
||||
}
|
||||
self = .message(message: message, media: media)
|
||||
case .webPage:
|
||||
let webPage = decoder.decodeObjectForKey("wpg", decoder: { WebpageReference(decoder: $0) }) as! WebpageReference
|
||||
guard let media = decoder.decodeObjectForKey("m") as? T else {
|
||||
return nil
|
||||
}
|
||||
self = .webPage(webPage: webPage, media: media)
|
||||
case .stickerPack:
|
||||
let stickerPack = decoder.decodeObjectForKey("spk", decoder: { StickerPackReference(decoder: $0) }) as! StickerPackReference
|
||||
guard let media = decoder.decodeObjectForKey("m") as? T else {
|
||||
return nil
|
||||
}
|
||||
self = .stickerPack(stickerPack: stickerPack, media: media)
|
||||
case .savedGif:
|
||||
guard let media = decoder.decodeObjectForKey("m") as? T else {
|
||||
return nil
|
||||
}
|
||||
self = .savedGif(media: media)
|
||||
}
|
||||
}
|
||||
|
||||
func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .standalone(media):
|
||||
encoder.encodeInt32(CodingCase.standalone.rawValue, forKey: "_r")
|
||||
encoder.encodeObject(media, forKey: "m")
|
||||
case let .message(message, media):
|
||||
encoder.encodeInt32(CodingCase.message.rawValue, forKey: "_r")
|
||||
encoder.encodeObject(message, forKey: "msg")
|
||||
encoder.encodeObject(media, forKey: "m")
|
||||
case let .webPage(webPage, media):
|
||||
encoder.encodeInt32(CodingCase.webPage.rawValue, forKey: "_r")
|
||||
encoder.encodeObject(webPage, forKey: "wpg")
|
||||
encoder.encodeObject(media, forKey: "m")
|
||||
case let .stickerPack(stickerPack, media):
|
||||
encoder.encodeInt32(CodingCase.stickerPack.rawValue, forKey: "_r")
|
||||
encoder.encodeObject(stickerPack, forKey: "spk")
|
||||
encoder.encodeObject(media, forKey: "m")
|
||||
case let .savedGif(media):
|
||||
encoder.encodeObject(media, forKey: "m")
|
||||
}
|
||||
}
|
||||
|
||||
public var abstract: AnyMediaReference {
|
||||
switch self {
|
||||
case let .standalone(media):
|
||||
return .standalone(media: media)
|
||||
case let .message(message, media):
|
||||
return .message(message: message, media: media)
|
||||
case let .webPage(webPage, media):
|
||||
return .webPage(webPage: webPage, media: media)
|
||||
case let .stickerPack(stickerPack, media):
|
||||
return .stickerPack(stickerPack: stickerPack, media: media)
|
||||
case let .savedGif(media):
|
||||
return .savedGif(media: media)
|
||||
}
|
||||
}
|
||||
|
||||
public var media: T {
|
||||
switch self {
|
||||
case let .standalone(media):
|
||||
return media
|
||||
case let .message(_, media):
|
||||
return media
|
||||
case let .webPage(_, media):
|
||||
return media
|
||||
case let .stickerPack(_, media):
|
||||
return media
|
||||
case let .savedGif(media):
|
||||
return media
|
||||
}
|
||||
}
|
||||
|
||||
public func resourceReference(_ resource: MediaResource) -> MediaResourceReference {
|
||||
return .media(media: self.abstract, resource: resource)
|
||||
}
|
||||
}
|
||||
|
||||
public typealias FileMediaReference = MediaReference<TelegramMediaFile>
|
||||
public typealias ImageMediaReference = MediaReference<TelegramMediaImage>
|
||||
|
||||
public enum MediaResourceReference {
|
||||
case media(media: AnyMediaReference, resource: MediaResource)
|
||||
case standalone(resource: MediaResource)
|
||||
case avatar(peer: PeerReference, resource: MediaResource)
|
||||
case wallpaper(resource: MediaResource)
|
||||
|
||||
public var resource: MediaResource {
|
||||
switch self {
|
||||
case let .media(_, resource):
|
||||
return resource
|
||||
case let .standalone(resource):
|
||||
return resource
|
||||
case let .avatar(_, resource):
|
||||
return resource
|
||||
case let .wallpaper(resource):
|
||||
return resource
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MediaResourceReference {
|
||||
var apiFileReference: Data? {
|
||||
if let resource = self.resource as? CloudFileMediaResource {
|
||||
return resource.fileReference
|
||||
} else if let resource = self.resource as? CloudDocumentMediaResource {
|
||||
return resource.fileReference
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class TelegramCloudMediaResourceFetchInfo: MediaResourceFetchInfo {
|
||||
let reference: MediaResourceReference
|
||||
|
||||
init(reference: MediaResourceReference) {
|
||||
self.reference = reference
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchedMediaResource(postbox: Postbox, reference: MediaResourceReference, range: Range<Int>? = nil, statsCategory: MediaResourceStatsCategory = .generic, reportResultStatus: Bool = false) -> Signal<FetchResourceSourceType, NoError> {
|
||||
if let range = range {
|
||||
return postbox.mediaBox.fetchedResourceData(reference.resource, in: range, parameters: MediaResourceFetchParameters(tag: TelegramMediaResourceFetchTag(statsCategory: statsCategory), info: TelegramCloudMediaResourceFetchInfo(reference: reference)))
|
||||
|> map { _ in .local }
|
||||
} else {
|
||||
return postbox.mediaBox.fetchedResource(reference.resource, parameters: MediaResourceFetchParameters(tag: TelegramMediaResourceFetchTag(statsCategory: statsCategory), info: TelegramCloudMediaResourceFetchInfo(reference: reference)), implNext: reportResultStatus)
|
||||
}
|
||||
}
|
||||
|
||||
enum RevalidateMediaReferenceError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func stickerPackFileReference(_ file: TelegramMediaFile) -> FileMediaReference {
|
||||
for attribute in file.attributes {
|
||||
if case let .Sticker(sticker) = attribute, let stickerPack = sticker.packReference {
|
||||
return .stickerPack(stickerPack: stickerPack, media: file)
|
||||
}
|
||||
}
|
||||
return .standalone(media: file)
|
||||
}
|
||||
|
||||
private func findMediaResource(media: Media, resource: MediaResource) -> MediaResource? {
|
||||
if let image = media as? TelegramMediaImage {
|
||||
for representation in image.representations {
|
||||
if representation.resource.id.isEqual(to: resource.id) {
|
||||
return representation.resource
|
||||
}
|
||||
}
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
if file.resource.id.isEqual(to: resource.id) {
|
||||
return file.resource
|
||||
} else {
|
||||
for representation in file.previewRepresentations {
|
||||
if representation.resource.id.isEqual(to: resource.id) {
|
||||
return representation.resource
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let webPage = media as? TelegramMediaWebpage, case let .Loaded(content) = webPage.content {
|
||||
if let image = content.image, let result = findMediaResource(media: image, resource: resource) {
|
||||
return result
|
||||
}
|
||||
if let file = content.file, let result = findMediaResource(media: file, resource: resource) {
|
||||
return result
|
||||
}
|
||||
if let instantPage = content.instantPage {
|
||||
for pageMedia in instantPage.media.values {
|
||||
if let result = findMediaResource(media: pageMedia, resource: resource) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func findMediaResourceReference(media: Media, resource: MediaResource) -> Data? {
|
||||
if let foundResource = findMediaResource(media: media, resource: resource) {
|
||||
if let foundResource = foundResource as? CloudFileMediaResource {
|
||||
return foundResource.fileReference
|
||||
} else if let foundResource = foundResource as? CloudDocumentMediaResource {
|
||||
return foundResource.fileReference
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private enum MediaReferenceRevalidationKey: Hashable {
|
||||
case message(message: MessageReference)
|
||||
case webPage(webPage: WebpageReference)
|
||||
case stickerPack(stickerPack: StickerPackReference)
|
||||
case savedGifs
|
||||
case peer(peer: PeerReference)
|
||||
case wallpapers
|
||||
}
|
||||
|
||||
private final class MediaReferenceRevalidationItemContext {
|
||||
let subscribers = Bag<(Any) -> Void>()
|
||||
let disposable: Disposable
|
||||
|
||||
init(disposable: Disposable) {
|
||||
self.disposable = disposable
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable.dispose()
|
||||
}
|
||||
|
||||
var isEmpty: Bool {
|
||||
return self.subscribers.isEmpty
|
||||
}
|
||||
|
||||
func addSubscriber(_ f: @escaping (Any) -> Void) -> Int {
|
||||
return self.subscribers.add(f)
|
||||
}
|
||||
|
||||
func removeSubscriber(_ index: Int) {
|
||||
self.subscribers.remove(index)
|
||||
}
|
||||
}
|
||||
|
||||
private final class MediaReferenceRevalidationContextImpl {
|
||||
let queue: Queue
|
||||
|
||||
var itemContexts: [MediaReferenceRevalidationKey: MediaReferenceRevalidationItemContext] = [:]
|
||||
|
||||
init(queue: Queue) {
|
||||
self.queue = queue
|
||||
}
|
||||
|
||||
func genericItem(key: MediaReferenceRevalidationKey, request: @escaping (@escaping (Any) -> Void, @escaping (RevalidateMediaReferenceError) -> Void) -> Disposable, _ f: @escaping (Any) -> Void) -> Disposable {
|
||||
let queue = self.queue
|
||||
|
||||
let context: MediaReferenceRevalidationItemContext
|
||||
if let current = self.itemContexts[key] {
|
||||
context = current
|
||||
} else {
|
||||
let disposable = MetaDisposable()
|
||||
context = MediaReferenceRevalidationItemContext(disposable: disposable)
|
||||
self.itemContexts[key] = context
|
||||
disposable.set(request({ [weak self] result in
|
||||
queue.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let current = strongSelf.itemContexts[key], current === context {
|
||||
strongSelf.itemContexts.removeValue(forKey: key)
|
||||
for subscriber in current.subscribers.copyItems() {
|
||||
subscriber(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, { [weak self] _ in
|
||||
queue.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
let index = context.addSubscriber(f)
|
||||
|
||||
return ActionDisposable { [weak self, weak context] in
|
||||
queue.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let current = strongSelf.itemContexts[key], current === context {
|
||||
current.removeSubscriber(index)
|
||||
if current.isEmpty {
|
||||
current.disposable.dispose()
|
||||
strongSelf.itemContexts.removeValue(forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class MediaReferenceRevalidationContext {
|
||||
private let queue: Queue
|
||||
private let impl: QueueLocalObject<MediaReferenceRevalidationContextImpl>
|
||||
|
||||
init() {
|
||||
self.queue = Queue()
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: self.queue, generate: {
|
||||
return MediaReferenceRevalidationContextImpl(queue: queue)
|
||||
})
|
||||
}
|
||||
|
||||
private func genericItem(key: MediaReferenceRevalidationKey, request: @escaping (@escaping (Any) -> Void, @escaping (RevalidateMediaReferenceError) -> Void) -> Disposable) -> Signal<Any, RevalidateMediaReferenceError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.genericItem(key: key, request: request, { result in
|
||||
subscriber.putNext(result)
|
||||
subscriber.putCompletion()
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
func message(postbox: Postbox, network: Network, message: MessageReference) -> Signal<Message, RevalidateMediaReferenceError> {
|
||||
return self.genericItem(key: .message(message: message), request: { next, error in
|
||||
return fetchRemoteMessage(postbox: postbox, network: network, message: message).start(next: { value in
|
||||
if let value = value {
|
||||
next(value)
|
||||
} else {
|
||||
error(.generic)
|
||||
}
|
||||
}, error: { _ in
|
||||
error(.generic)
|
||||
})
|
||||
}) |> mapToSignal { next -> Signal<Message, RevalidateMediaReferenceError> in
|
||||
if let next = next as? Message {
|
||||
return .single(next)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stickerPack(postbox: Postbox, network: Network, stickerPack: StickerPackReference) -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), RevalidateMediaReferenceError> {
|
||||
return self.genericItem(key: .stickerPack(stickerPack: stickerPack), request: { next, error in
|
||||
return (updatedRemoteStickerPack(postbox: postbox, network: network, reference: stickerPack)
|
||||
|> mapError { _ -> RevalidateMediaReferenceError in
|
||||
return .generic
|
||||
}).start(next: { value in
|
||||
if let value = value {
|
||||
next(value)
|
||||
} else {
|
||||
error(.generic)
|
||||
}
|
||||
}, error: { _ in
|
||||
error(.generic)
|
||||
})
|
||||
}) |> mapToSignal { next -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), RevalidateMediaReferenceError> in
|
||||
if let next = next as? (StickerPackCollectionInfo, [ItemCollectionItem]) {
|
||||
return .single(next)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func webPage(postbox: Postbox, network: Network, webPage: WebpageReference) -> Signal<TelegramMediaWebpage, RevalidateMediaReferenceError> {
|
||||
return self.genericItem(key: .webPage(webPage: webPage), request: { next, error in
|
||||
return (updatedRemoteWebpage(postbox: postbox, network: network, webPage: webPage)
|
||||
|> mapError { _ -> RevalidateMediaReferenceError in
|
||||
return .generic
|
||||
}).start(next: { value in
|
||||
if let value = value {
|
||||
next(value)
|
||||
} else {
|
||||
error(.generic)
|
||||
}
|
||||
}, error: { _ in
|
||||
error(.generic)
|
||||
})
|
||||
}) |> mapToSignal { next -> Signal<TelegramMediaWebpage, RevalidateMediaReferenceError> in
|
||||
if let next = next as? TelegramMediaWebpage {
|
||||
return .single(next)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func savedGifs(postbox: Postbox, network: Network) -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> {
|
||||
return self.genericItem(key: .savedGifs, request: { next, error in
|
||||
let loadRecentGifs: Signal<[TelegramMediaFile], Void> = postbox.transaction { transaction -> [TelegramMediaFile] in
|
||||
return transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs).compactMap({ item -> TelegramMediaFile? in
|
||||
if let contents = item.contents as? RecentMediaItem, let file = contents.media as? TelegramMediaFile {
|
||||
return file
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return (managedRecentGifs(postbox: postbox, network: network, forceFetch: true)
|
||||
|> mapToSignal { _ -> Signal<[TelegramMediaFile], Void> in
|
||||
return .complete()
|
||||
}
|
||||
|> then(loadRecentGifs)
|
||||
|> mapError { _ -> RevalidateMediaReferenceError in
|
||||
return .generic
|
||||
}).start(next: { value in
|
||||
next(value)
|
||||
}, error: { _ in
|
||||
error(.generic)
|
||||
})
|
||||
}) |> mapToSignal { next -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> in
|
||||
if let next = next as? [TelegramMediaFile] {
|
||||
return .single(next)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func peer(postbox: Postbox, network: Network, peer: PeerReference) -> Signal<Peer, RevalidateMediaReferenceError> {
|
||||
return self.genericItem(key: .peer(peer: peer), request: { next, error in
|
||||
return (updatedRemotePeer(postbox: postbox, network: network, peer: peer)
|
||||
|> mapError { _ -> RevalidateMediaReferenceError in
|
||||
return .generic
|
||||
}).start(next: { value in
|
||||
next(value)
|
||||
}, error: { _ in
|
||||
error(.generic)
|
||||
})
|
||||
}) |> mapToSignal { next -> Signal<Peer, RevalidateMediaReferenceError> in
|
||||
if let next = next as? Peer {
|
||||
return .single(next)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func wallpapers(postbox: Postbox, network: Network) -> Signal<[TelegramWallpaper], RevalidateMediaReferenceError> {
|
||||
return self.genericItem(key: .wallpapers, request: { next, error in
|
||||
return (telegramWallpapers(postbox: postbox, network: network)
|
||||
|> last
|
||||
|> mapError { _ -> RevalidateMediaReferenceError in
|
||||
return .generic
|
||||
}).start(next: { value in
|
||||
if let value = value {
|
||||
next(value)
|
||||
} else {
|
||||
error(.generic)
|
||||
}
|
||||
}, error: { _ in
|
||||
error(.generic)
|
||||
})
|
||||
}) |> mapToSignal { next -> Signal<[TelegramWallpaper], RevalidateMediaReferenceError> in
|
||||
if let next = next as? [TelegramWallpaper] {
|
||||
return .single(next)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func revalidateMediaResourceReference(postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo, resource: MediaResource) -> Signal<Data, RevalidateMediaReferenceError> {
|
||||
switch info.reference {
|
||||
case let .media(media, _):
|
||||
switch media {
|
||||
case let .message(message, _):
|
||||
return revalidationContext.message(postbox: postbox, network: network, message: message)
|
||||
|> mapToSignal { message -> Signal<Data, RevalidateMediaReferenceError> in
|
||||
for media in message.media {
|
||||
if let fileReference = findMediaResourceReference(media: media, resource: resource) {
|
||||
return .single(fileReference)
|
||||
}
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
case let .stickerPack(stickerPack, media):
|
||||
return revalidationContext.stickerPack(postbox: postbox, network: network, stickerPack: stickerPack)
|
||||
|> mapToSignal { result -> Signal<Data, RevalidateMediaReferenceError> in
|
||||
for item in result.1 {
|
||||
if let item = item as? StickerPackItem {
|
||||
if media.id != nil && item.file.id == media.id {
|
||||
if let fileReference = findMediaResourceReference(media: item.file, resource: resource) {
|
||||
return .single(fileReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
case let .webPage(webPage, _):
|
||||
return revalidationContext.webPage(postbox: postbox, network: network, webPage: webPage)
|
||||
|> mapToSignal { result -> Signal<Data, RevalidateMediaReferenceError> in
|
||||
if let fileReference = findMediaResourceReference(media: result, resource: resource) {
|
||||
return .single(fileReference)
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
case let .savedGif(media):
|
||||
return revalidationContext.savedGifs(postbox: postbox, network: network)
|
||||
|> mapToSignal { result -> Signal<Data, RevalidateMediaReferenceError> in
|
||||
for file in result {
|
||||
if media.id != nil && file.id == media.id {
|
||||
if let fileReference = findMediaResourceReference(media: file, resource: resource) {
|
||||
return .single(fileReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
case let .standalone(media):
|
||||
if let file = media as? TelegramMediaFile {
|
||||
for attribute in file.attributes {
|
||||
if case let .Sticker(sticker) = attribute, let stickerPack = sticker.packReference {
|
||||
return revalidationContext.stickerPack(postbox: postbox, network: network, stickerPack: stickerPack)
|
||||
|> mapToSignal { result -> Signal<Data, RevalidateMediaReferenceError> in
|
||||
for item in result.1 {
|
||||
if let item = item as? StickerPackItem {
|
||||
if media.id != nil && item.file.id == media.id {
|
||||
if let fileReference = findMediaResourceReference(media: item.file, resource: resource) {
|
||||
return .single(fileReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
case let .avatar(peer, _):
|
||||
return revalidationContext.peer(postbox: postbox, network: network, peer: peer)
|
||||
|> mapToSignal { updatedPeer -> Signal<Data, RevalidateMediaReferenceError> in
|
||||
for representation in updatedPeer.profileImageRepresentations {
|
||||
if representation.resource.id.isEqual(to: resource.id), let representationResource = representation.resource as? CloudFileMediaResource, let fileReference = representationResource.fileReference {
|
||||
return .single(fileReference)
|
||||
}
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
case .wallpaper:
|
||||
return revalidationContext.wallpapers(postbox: postbox, network: network)
|
||||
|> mapToSignal { wallpapers -> Signal<Data, RevalidateMediaReferenceError> in
|
||||
for wallpaper in wallpapers {
|
||||
if case let .image(representations) = wallpaper {
|
||||
for representation in representations {
|
||||
if representation.resource.id.isEqual(to: resource.id), let representationResource = representation.resource as? CloudFileMediaResource, let fileReference = representationResource.fileReference {
|
||||
return .single(fileReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
case .standalone:
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
@ -28,15 +28,15 @@ public enum LoadedStickerPack {
|
||||
case result(info: StickerPackCollectionInfo, items: [ItemCollectionItem], installed: Bool)
|
||||
}
|
||||
|
||||
func remoteStickerPack(network: Network, reference: StickerPackReference) -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> {
|
||||
func updatedRemoteStickerPack(postbox: Postbox, network: Network, reference: StickerPackReference) -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> {
|
||||
return network.request(Api.functions.messages.getStickerSet(stickerset: reference.apiInputStickerSet))
|
||||
|> map { Optional($0) }
|
||||
|> `catch` { _ -> Signal<Api.messages.StickerSet?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result -> (StickerPackCollectionInfo, [ItemCollectionItem])? in
|
||||
|> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> in
|
||||
guard let result = result else {
|
||||
return nil
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
let info: StickerPackCollectionInfo
|
||||
@ -82,7 +82,14 @@ func remoteStickerPack(network: Network, reference: StickerPackReference) -> Sig
|
||||
}
|
||||
}
|
||||
|
||||
return (info, items)
|
||||
return postbox.transaction { transaction -> (StickerPackCollectionInfo, [ItemCollectionItem])? in
|
||||
if transaction.getItemCollectionInfo(collectionId: info.id) != nil {
|
||||
transaction.replaceItemCollectionItems(collectionId: info.id, items: items)
|
||||
}
|
||||
cacheStickerPack(transaction: transaction, info: info, items: items)
|
||||
|
||||
return (info, items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ private func hashForIds(_ ids: [Int64]) -> Int32 {
|
||||
return Int32(bitPattern: acc & UInt32(0x7FFFFFFF))
|
||||
}
|
||||
|
||||
private func managedRecentMedia(postbox: Postbox, network: Network, collectionId: Int32, reverseHashOrder: Bool, fetch: @escaping (Int32) -> Signal<[OrderedItemListEntry]?, NoError>) -> Signal<Void, NoError> {
|
||||
private func managedRecentMedia(postbox: Postbox, network: Network, collectionId: Int32, reverseHashOrder: Bool, forceFetch: Bool, fetch: @escaping (Int32) -> Signal<[OrderedItemListEntry]?, NoError>) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
var itemIds = transaction.getOrderedListItemIds(collectionId: collectionId).map {
|
||||
RecentMediaItemId($0).mediaId.id
|
||||
@ -28,7 +28,7 @@ private func managedRecentMedia(postbox: Postbox, network: Network, collectionId
|
||||
if reverseHashOrder {
|
||||
itemIds.reverse()
|
||||
}
|
||||
return fetch(hashForIds(itemIds))
|
||||
return fetch(forceFetch ? 0 : hashForIds(itemIds))
|
||||
|> mapToSignal { items in
|
||||
if let items = items {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
@ -42,7 +42,7 @@ private func managedRecentMedia(postbox: Postbox, network: Network, collectionId
|
||||
}
|
||||
|
||||
func managedRecentStickers(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentStickers, reverseHashOrder: false, fetch: { hash in
|
||||
return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentStickers, reverseHashOrder: false, forceFetch: false, fetch: { hash in
|
||||
return network.request(Api.functions.messages.getRecentStickers(flags: 0, hash: hash))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
|
||||
@ -62,8 +62,8 @@ func managedRecentStickers(postbox: Postbox, network: Network) -> Signal<Void, N
|
||||
})
|
||||
}
|
||||
|
||||
func managedRecentGifs(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentGifs, reverseHashOrder: false, fetch: { hash in
|
||||
func managedRecentGifs(postbox: Postbox, network: Network, forceFetch: Bool = false) -> Signal<Void, NoError> {
|
||||
return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentGifs, reverseHashOrder: false, forceFetch: forceFetch, fetch: { hash in
|
||||
return network.request(Api.functions.messages.getSavedGifs(hash: hash))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
|
||||
@ -84,7 +84,7 @@ func managedRecentGifs(postbox: Postbox, network: Network) -> Signal<Void, NoErr
|
||||
}
|
||||
|
||||
func managedSavedStickers(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudSavedStickers, reverseHashOrder: true, fetch: { hash in
|
||||
return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudSavedStickers, reverseHashOrder: true, forceFetch: false, fetch: { hash in
|
||||
return network.request(Api.functions.messages.getFavedStickers(hash: hash))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
|
||||
|
||||
@ -984,7 +984,7 @@ private func resourceThumbnailData(mediaBox: MediaBox, resource: MediaResource,
|
||||
return mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false))
|
||||
|> take(1)
|
||||
|> map { data -> (MediaId, Data)? in
|
||||
if data.complete, data.size < 1024 * 16, let content = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||
if data.complete, data.size < 1024 * 1024, let content = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||
return (mediaId, content)
|
||||
} else {
|
||||
return nil
|
||||
@ -1072,7 +1072,7 @@ private func sendMessage(postbox: Postbox, network: Network, messageId: MessageI
|
||||
if let fromMedia = currentMessage.media.first, let encryptedFile = encryptedFile, let file = file {
|
||||
var toMedia: Media?
|
||||
if let fromMedia = fromMedia as? TelegramMediaFile {
|
||||
let updatedFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: encryptedFile.id), resource: SecretFileMediaResource(fileId: encryptedFile.id, accessHash: encryptedFile.accessHash, containerSize: encryptedFile.size, decryptedSize: file.size, datacenterId: Int(encryptedFile.datacenterId), key: file.key), previewRepresentations: fromMedia.previewRepresentations, mimeType: fromMedia.mimeType, size: fromMedia.size, attributes: fromMedia.attributes)
|
||||
let updatedFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: encryptedFile.id), reference: nil, resource: SecretFileMediaResource(fileId: encryptedFile.id, accessHash: encryptedFile.accessHash, containerSize: encryptedFile.size, decryptedSize: file.size, datacenterId: Int(encryptedFile.datacenterId), key: file.key), previewRepresentations: fromMedia.previewRepresentations, mimeType: fromMedia.mimeType, size: fromMedia.size, attributes: fromMedia.attributes)
|
||||
toMedia = updatedFile
|
||||
updatedMedia = [updatedFile]
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOpera
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
func managedSynchronizeSavedGifsOperations(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
func managedSynchronizeSavedGifsOperations(postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext) -> Signal<Void, NoError> {
|
||||
return Signal { _ in
|
||||
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeSavedGifs
|
||||
|
||||
@ -88,7 +88,7 @@ func managedSynchronizeSavedGifsOperations(postbox: Postbox, network: Network) -
|
||||
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in
|
||||
if let entry = entry {
|
||||
if let operation = entry.contents as? SynchronizeSavedGifsOperation {
|
||||
return synchronizeSavedGifs(transaction: transaction, postbox: postbox, network: network, operation: operation)
|
||||
return synchronizeSavedGifs(transaction: transaction, postbox: postbox, network: network, revalidationContext: revalidationContext, operation: operation)
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
@ -115,24 +115,64 @@ func managedSynchronizeSavedGifsOperations(postbox: Postbox, network: Network) -
|
||||
}
|
||||
}
|
||||
|
||||
private func synchronizeSavedGifs(transaction: Transaction, postbox: Postbox, network: Network, operation: SynchronizeSavedGifsOperation) -> Signal<Void, NoError> {
|
||||
private enum SaveGifError {
|
||||
case generic
|
||||
case invalidReference
|
||||
}
|
||||
|
||||
private func synchronizeSavedGifs(transaction: Transaction, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, operation: SynchronizeSavedGifsOperation) -> Signal<Void, NoError> {
|
||||
switch operation.content {
|
||||
case let .add(id, accessHash):
|
||||
return network.request(Api.functions.messages.saveGif(id: .inputDocument(id: id, accessHash: accessHash), unsave: .boolFalse))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
case let .add(id, accessHash, fileReference):
|
||||
guard let fileReference = fileReference else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
let saveGif: (Data) -> Signal<Api.Bool, SaveGifError> = { fileReference in
|
||||
return network.request(Api.functions.messages.saveGif(id: .inputDocument(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), unsave: .boolFalse))
|
||||
|> mapError { error -> SaveGifError in
|
||||
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {
|
||||
return .invalidReference
|
||||
}
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|
||||
let initialSignal: Signal<Api.Bool, SaveGifError>
|
||||
if let reference = (fileReference.media.resource as? CloudDocumentMediaResource)?.fileReference {
|
||||
initialSignal = saveGif(reference)
|
||||
} else {
|
||||
initialSignal = .fail(.invalidReference)
|
||||
}
|
||||
|
||||
return initialSignal
|
||||
|> `catch` { error -> Signal<Api.Bool, SaveGifError> in
|
||||
switch error {
|
||||
case .generic:
|
||||
return .fail(.generic)
|
||||
case .invalidReference:
|
||||
return revalidateMediaResourceReference(postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: fileReference.resourceReference(fileReference.media.resource)), resource: fileReference.media.resource)
|
||||
|> mapError { _ -> SaveGifError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { reference -> Signal<Api.Bool, SaveGifError> in
|
||||
return saveGif(reference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
case let .remove(id, accessHash):
|
||||
return network.request(Api.functions.messages.saveGif(id: .inputDocument(id: id, accessHash: accessHash), unsave: .boolTrue))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
return network.request(Api.functions.messages.saveGif(id: .inputDocument(id: id, accessHash: accessHash, fileReference: Buffer()), unsave: .boolTrue))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
case .sync:
|
||||
return managedRecentGifs(postbox: postbox, network: network)
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOpera
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
func managedSynchronizeSavedStickersOperations(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
func managedSynchronizeSavedStickersOperations(postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext) -> Signal<Void, NoError> {
|
||||
return Signal { _ in
|
||||
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeSavedStickers
|
||||
|
||||
@ -88,7 +88,7 @@ func managedSynchronizeSavedStickersOperations(postbox: Postbox, network: Networ
|
||||
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in
|
||||
if let entry = entry {
|
||||
if let operation = entry.contents as? SynchronizeSavedStickersOperation {
|
||||
return synchronizeSavedStickers(transaction: transaction, postbox: postbox, network: network, operation: operation)
|
||||
return synchronizeSavedStickers(transaction: transaction, postbox: postbox, network: network, revalidationContext: revalidationContext, operation: operation)
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
@ -115,18 +115,58 @@ func managedSynchronizeSavedStickersOperations(postbox: Postbox, network: Networ
|
||||
}
|
||||
}
|
||||
|
||||
private func synchronizeSavedStickers(transaction: Transaction, postbox: Postbox, network: Network, operation: SynchronizeSavedStickersOperation) -> Signal<Void, NoError> {
|
||||
private enum SaveStickerError {
|
||||
case generic
|
||||
case invalidReference
|
||||
}
|
||||
|
||||
private func synchronizeSavedStickers(transaction: Transaction, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, operation: SynchronizeSavedStickersOperation) -> Signal<Void, NoError> {
|
||||
switch operation.content {
|
||||
case let .add(id, accessHash):
|
||||
return network.request(Api.functions.messages.faveSticker(id: .inputDocument(id: id, accessHash: accessHash), unfave: .boolFalse))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
case let .add(id, accessHash, fileReference):
|
||||
guard let fileReference = fileReference else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
let saveSticker: (Data) -> Signal<Api.Bool, SaveStickerError> = { fileReference in
|
||||
return network.request(Api.functions.messages.faveSticker(id: .inputDocument(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), unfave: .boolFalse))
|
||||
|> mapError { error -> SaveStickerError in
|
||||
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {
|
||||
return .invalidReference
|
||||
}
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|
||||
let initialSignal: Signal<Api.Bool, SaveStickerError>
|
||||
if let reference = (fileReference.media.resource as? CloudDocumentMediaResource)?.fileReference {
|
||||
initialSignal = saveSticker(reference)
|
||||
} else {
|
||||
initialSignal = .fail(.invalidReference)
|
||||
}
|
||||
|
||||
return initialSignal
|
||||
|> `catch` { error -> Signal<Api.Bool, SaveStickerError> in
|
||||
switch error {
|
||||
case .generic:
|
||||
return .fail(.generic)
|
||||
case .invalidReference:
|
||||
return revalidateMediaResourceReference(postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: fileReference.resourceReference(fileReference.media.resource)), resource: fileReference.media.resource)
|
||||
|> mapError { _ -> SaveStickerError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { reference -> Signal<Api.Bool, SaveStickerError> in
|
||||
return saveSticker(reference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
case let .remove(id, accessHash):
|
||||
return network.request(Api.functions.messages.faveSticker(id: .inputDocument(id: id, accessHash: accessHash), unfave: .boolTrue))
|
||||
return network.request(Api.functions.messages.faveSticker(id: .inputDocument(id: id, accessHash: accessHash, fileReference: Buffer()), unfave: .boolTrue))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|
||||
@ -60,11 +60,12 @@ private enum MultipartFetchDownloadError {
|
||||
case generic
|
||||
case switchToCdn(id: Int32, token: Data, key: Data, iv: Data, partHashes: [Int32: Data])
|
||||
case reuploadToCdn(masterDatacenterId: Int32, token: Data)
|
||||
case revalidateMediaReference
|
||||
case hashesMissing
|
||||
}
|
||||
|
||||
private enum MultipartFetchMasterLocation {
|
||||
case generic(Int32, Api.InputFileLocation)
|
||||
case generic(Int32, (Data?) -> Api.InputFileLocation?)
|
||||
case web(Int32, Api.InputWebFileLocation)
|
||||
|
||||
var datacenterId: Int32 {
|
||||
@ -269,7 +270,7 @@ private enum MultipartFetchSource {
|
||||
case master(location: MultipartFetchMasterLocation, download: DownloadWrapper)
|
||||
case cdn(masterDatacenterId: Int32, fileToken: Data, key: Data, iv: Data, download: DownloadWrapper, masterDownload: DownloadWrapper, hashSource: MultipartCdnHashSource)
|
||||
|
||||
func request(offset: Int32, limit: Int32, tag: MediaResourceFetchTag?) -> Signal<Data, MultipartFetchDownloadError> {
|
||||
func request(offset: Int32, limit: Int32, tag: MediaResourceFetchTag?, fileReference: Data?) -> Signal<Data, MultipartFetchDownloadError> {
|
||||
switch self {
|
||||
case .none:
|
||||
return .never()
|
||||
@ -281,29 +282,37 @@ private enum MultipartFetchSource {
|
||||
|
||||
switch location {
|
||||
case let .generic(_, location):
|
||||
return download.request(Api.functions.upload.getFile(location: location, offset: offset, limit: Int32(updatedLength)), tag: tag)
|
||||
|> mapError { _ -> MultipartFetchDownloadError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Data, MultipartFetchDownloadError> in
|
||||
switch result {
|
||||
case let .file(_, _, bytes):
|
||||
var resultData = bytes.makeData()
|
||||
if resultData.count > Int(limit) {
|
||||
resultData.count = Int(limit)
|
||||
}
|
||||
return .single(resultData)
|
||||
case let .fileCdnRedirect(dcId, fileToken, encryptionKey, encryptionIv, partHashes):
|
||||
var parsedPartHashes: [Int32: Data] = [:]
|
||||
for part in partHashes {
|
||||
switch part {
|
||||
case let .fileHash(offset, limit, bytes):
|
||||
assert(limit == 128 * 1024)
|
||||
parsedPartHashes[offset] = bytes.makeData()
|
||||
}
|
||||
}
|
||||
return .fail(.switchToCdn(id: dcId, token: fileToken.makeData(), key: encryptionKey.makeData(), iv: encryptionIv.makeData(), partHashes: parsedPartHashes))
|
||||
if let parsedLocation = location(fileReference) {
|
||||
|
||||
return download.request(Api.functions.upload.getFile(location: parsedLocation, offset: offset, limit: Int32(updatedLength)), tag: tag)
|
||||
|> mapError { error -> MultipartFetchDownloadError in
|
||||
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {
|
||||
return .revalidateMediaReference
|
||||
}
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Data, MultipartFetchDownloadError> in
|
||||
switch result {
|
||||
case let .file(_, _, bytes):
|
||||
var resultData = bytes.makeData()
|
||||
if resultData.count > Int(limit) {
|
||||
resultData.count = Int(limit)
|
||||
}
|
||||
return .single(resultData)
|
||||
case let .fileCdnRedirect(dcId, fileToken, encryptionKey, encryptionIv, partHashes):
|
||||
var parsedPartHashes: [Int32: Data] = [:]
|
||||
for part in partHashes {
|
||||
switch part {
|
||||
case let .fileHash(offset, limit, bytes):
|
||||
assert(limit == 128 * 1024)
|
||||
parsedPartHashes[offset] = bytes.makeData()
|
||||
}
|
||||
}
|
||||
return .fail(.switchToCdn(id: dcId, token: fileToken.makeData(), key: encryptionKey.makeData(), iv: encryptionIv.makeData(), partHashes: parsedPartHashes))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .fail(.revalidateMediaReference)
|
||||
}
|
||||
case let .web(_, location):
|
||||
return download.request(Api.functions.upload.getWebFile(location: location, offset: offset, limit: Int32(updatedLength)), tag: tag)
|
||||
@ -377,9 +386,12 @@ private final class MultipartFetchManager {
|
||||
let defaultPartSize = 128 * 1024
|
||||
let partAlignment = 128 * 1024
|
||||
|
||||
let tag: MediaResourceFetchTag?
|
||||
let resource: TelegramMediaResource
|
||||
let parameters: MediaResourceFetchParameters?
|
||||
let consumerId: Int64
|
||||
|
||||
var fileReference: Data?
|
||||
|
||||
let queue = Queue()
|
||||
|
||||
var currentRanges: IndexSet?
|
||||
@ -388,7 +400,9 @@ private final class MultipartFetchManager {
|
||||
var completeSize: Int?
|
||||
var completeSizeReported = false
|
||||
|
||||
let postbox: Postbox
|
||||
let network: Network
|
||||
let revalidationContext: MediaReferenceRevalidationContext
|
||||
let partReady: (Int, Data) -> Void
|
||||
let reportCompleteSize: (Int) -> Void
|
||||
|
||||
@ -401,32 +415,34 @@ private final class MultipartFetchManager {
|
||||
var reuploadingToCdn = false
|
||||
let reuploadToCdnDisposable = MetaDisposable()
|
||||
|
||||
var revalidatedMediaReference = false
|
||||
var revalidatingMediaReference = false
|
||||
let revalidateMediaReferenceDisposable = MetaDisposable()
|
||||
|
||||
var state: MultipartDownloadState
|
||||
|
||||
var rangesDisposable: Disposable?
|
||||
|
||||
init(tag: MediaResourceFetchTag?, size: Int?, ranges: Signal<IndexSet, NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int32?, location: MultipartFetchMasterLocation, network: Network, partReady: @escaping (Int, Data) -> Void, reportCompleteSize: @escaping (Int) -> Void) {
|
||||
self.tag = tag
|
||||
init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int?, ranges: Signal<IndexSet, NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int32?, location: MultipartFetchMasterLocation, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, partReady: @escaping (Int, Data) -> Void, reportCompleteSize: @escaping (Int) -> Void) {
|
||||
self.resource = resource
|
||||
self.parameters = parameters
|
||||
self.consumerId = arc4random64()
|
||||
|
||||
self.completeSize = size
|
||||
if let size = size {
|
||||
if let _ = size {
|
||||
self.parallelParts = 4
|
||||
/*if size <= range.lowerBound {
|
||||
self.range = range
|
||||
self.parallelParts = 0
|
||||
} else {
|
||||
self.range = range.lowerBound ..< min(range.upperBound, size)
|
||||
|
||||
}*/
|
||||
} else {
|
||||
//self.range = range
|
||||
self.parallelParts = 1
|
||||
}
|
||||
|
||||
if let info = parameters?.info as? TelegramCloudMediaResourceFetchInfo {
|
||||
self.fileReference = info.reference.apiFileReference
|
||||
}
|
||||
|
||||
self.state = MultipartDownloadState(encryptionKey: encryptionKey, decryptedSize: decryptedSize)
|
||||
//self.committedOffset = range.lowerBound
|
||||
self.postbox = postbox
|
||||
self.network = network
|
||||
self.revalidationContext = revalidationContext
|
||||
self.source = .master(location: location, download: DownloadWrapper(consumerId: self.consumerId, datacenterId: location.datacenterId, isCdn: false, network: network))
|
||||
self.partReady = partReady
|
||||
self.reportCompleteSize = reportCompleteSize
|
||||
@ -465,6 +481,7 @@ private final class MultipartFetchManager {
|
||||
disposable.dispose()
|
||||
}
|
||||
self.reuploadToCdnDisposable.dispose()
|
||||
self.revalidateMediaReferenceDisposable.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,7 +532,7 @@ private final class MultipartFetchManager {
|
||||
}
|
||||
}
|
||||
|
||||
while !rangesToFetch.isEmpty && self.fetchingParts.count < self.parallelParts && !self.reuploadingToCdn {
|
||||
while !rangesToFetch.isEmpty && self.fetchingParts.count < self.parallelParts && !self.reuploadingToCdn && !self.revalidatingMediaReference {
|
||||
var selectedRange: (Range<Int>, Range<Int>)?
|
||||
for range in rangesToFetch.rangeView {
|
||||
var dataRange: Range<Int> = range.lowerBound ..< min(range.lowerBound + self.defaultPartSize, range.upperBound)
|
||||
@ -537,9 +554,13 @@ private final class MultipartFetchManager {
|
||||
if requestLimit % self.partAlignment != 0 {
|
||||
requestLimit = (requestLimit / self.partAlignment + 1) * self.partAlignment
|
||||
}
|
||||
let part = self.source.request(offset: Int32(downloadRange.lowerBound), limit: Int32(requestLimit), tag: self.tag)
|
||||
|> deliverOn(self.queue)
|
||||
self.fetchingParts[downloadRange.lowerBound] = (downloadRange.count, part.start(next: { [weak self] data in
|
||||
|
||||
let part = self.source.request(offset: Int32(downloadRange.lowerBound), limit: Int32(requestLimit), tag: self.parameters?.tag, fileReference: self.fileReference)
|
||||
|> deliverOn(self.queue)
|
||||
let partDisposable = MetaDisposable()
|
||||
self.fetchingParts[downloadRange.lowerBound] = (downloadRange.count, partDisposable)
|
||||
|
||||
partDisposable.set(part.start(next: { [weak self] data in
|
||||
if let strongSelf = self {
|
||||
var data = data
|
||||
if data.count < downloadRange.count {
|
||||
@ -555,6 +576,26 @@ private final class MultipartFetchManager {
|
||||
switch error {
|
||||
case .generic:
|
||||
break
|
||||
case .revalidateMediaReference:
|
||||
if !strongSelf.revalidatingMediaReference && !strongSelf.revalidatedMediaReference {
|
||||
strongSelf.revalidatingMediaReference = true
|
||||
if let info = strongSelf.parameters?.info as? TelegramCloudMediaResourceFetchInfo {
|
||||
strongSelf.revalidateMediaReferenceDisposable.set((revalidateMediaResourceReference(postbox: strongSelf.postbox, network: strongSelf.network, revalidationContext: strongSelf.revalidationContext, info: info, resource: strongSelf.resource)
|
||||
|> deliverOn(strongSelf.queue)).start(next: { fileReference in
|
||||
if let strongSelf = self {
|
||||
strongSelf.revalidatingMediaReference = false
|
||||
strongSelf.revalidatedMediaReference = true
|
||||
strongSelf.fileReference = fileReference
|
||||
strongSelf.checkState()
|
||||
}
|
||||
}, error: { _ in
|
||||
if let strongSelf = self {
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
Logger.shared.log("MultipartFetch", "reference invalidation requested, but no valid reference given")
|
||||
}
|
||||
}
|
||||
case let .switchToCdn(id, token, key, iv, partHashes):
|
||||
switch strongSelf.source {
|
||||
case let .master(location, download):
|
||||
@ -594,11 +635,13 @@ private final class MultipartFetchManager {
|
||||
}
|
||||
}
|
||||
|
||||
func multipartFetch(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int?, ranges: Signal<IndexSet, NoError>, tag: MediaResourceFetchTag?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int32? = nil) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
func multipartFetch(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int?, ranges: Signal<IndexSet, NoError>, parameters: MediaResourceFetchParameters?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int32? = nil) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
return Signal { subscriber in
|
||||
let location: MultipartFetchMasterLocation
|
||||
if let resource = resource as? TelegramCloudMediaResource {
|
||||
location = .generic(Int32(datacenterId), resource.apiInputLocation)
|
||||
location = .generic(Int32(datacenterId), { fileReference in
|
||||
return resource.apiInputLocation(fileReference: fileReference)
|
||||
})
|
||||
} else if let resource = resource as? WebFileReferenceMediaResource {
|
||||
location = .web(Int32(datacenterId), resource.apiInputLocation)
|
||||
} else {
|
||||
@ -610,7 +653,7 @@ func multipartFetch(account: Account, resource: TelegramMediaResource, datacente
|
||||
subscriber.putNext(.reset)
|
||||
}
|
||||
|
||||
let manager = MultipartFetchManager(tag: tag, size: size, ranges: ranges, encryptionKey: encryptionKey, decryptedSize: decryptedSize, location: location, network: account.network, partReady: { dataOffset, data in
|
||||
let manager = MultipartFetchManager(resource: resource, parameters: parameters, size: size, ranges: ranges, encryptionKey: encryptionKey, decryptedSize: decryptedSize, location: location, postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, partReady: { dataOffset, data in
|
||||
subscriber.putNext(.dataPart(resourceOffset: dataOffset, data: data, range: 0 ..< data.count, complete: false))
|
||||
}, reportCompleteSize: { size in
|
||||
subscriber.putNext(.resourceSizeUpdated(size))
|
||||
|
||||
@ -393,7 +393,7 @@ func multipartUpload(network: Network, postbox: Postbox, source: MultipartUpload
|
||||
case let .resource(resource):
|
||||
dataSignal = postbox.mediaBox.resourceData(resource, option: .incremental(waitUntilFetchStatus: true)) |> map { MultipartUploadData.resourceData($0) }
|
||||
headerSize = resource.headerSize
|
||||
fetchedResource = postbox.mediaBox.fetchedResource(resource, tag: tag) |> map {_ in}
|
||||
fetchedResource = postbox.mediaBox.fetchedResource(resource, parameters: nil) |> map {_ in}
|
||||
case let .data(data):
|
||||
dataSignal = .single(.data(data))
|
||||
headerSize = 0
|
||||
|
||||
@ -90,7 +90,7 @@ public func outgoingMessageWithChatContextResult(_ results: ChatContextResultCol
|
||||
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), resource: EmptyMediaResource(), previewRepresentations: previewRepresentations, mimeType: content?.mimeType ?? "application/binary", size: nil, attributes: fileAttributes)
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), reference: nil, resource: EmptyMediaResource(), previewRepresentations: previewRepresentations, mimeType: content?.mimeType ?? "application/binary", size: nil, attributes: fileAttributes)
|
||||
return .message(text: caption, attributes: attributes, media: file, replyToMessageId: nil, localGroupingKey: nil)
|
||||
} else {
|
||||
return .message(text: caption, attributes: attributes, media: nil, replyToMessageId: nil, localGroupingKey: nil)
|
||||
|
||||
@ -70,8 +70,8 @@ public func updatePeerPhoto(account: Account, peerId: PeerId, photo: Signal<Uplo
|
||||
switch apiPhoto {
|
||||
case .photoEmpty:
|
||||
representations = []
|
||||
case let .photo(flags: _, id: _, accessHash: _, date: _, sizes: sizes):
|
||||
var sizes = sizes
|
||||
case let .photo(photo):
|
||||
var sizes = photo.sizes
|
||||
if sizes.count == 3 {
|
||||
sizes.remove(at: 1)
|
||||
}
|
||||
@ -194,13 +194,17 @@ public func updatePeerPhoto(account: Account, peerId: PeerId, photo: Signal<Uplo
|
||||
public func removeAccountPhoto(network: Network, reference: TelegramMediaImageReference?) -> Signal<Void, NoError> {
|
||||
if let reference = reference {
|
||||
switch reference {
|
||||
case let .cloud(imageId, accessHash):
|
||||
return network.request(Api.functions.photos.deletePhotos(id: [.inputPhoto(id: imageId, accessHash: accessHash)]))
|
||||
case let .cloud(imageId, accessHash, fileReference):
|
||||
if let fileReference = fileReference {
|
||||
return network.request(Api.functions.photos.deletePhotos(id: [.inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: fileReference))]))
|
||||
|> `catch` { _ -> Signal<[Int64], NoError> in
|
||||
return .single([])
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -2,9 +2,11 @@ import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
import MtProtoKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import MtProtoKitDynamic
|
||||
#endif
|
||||
|
||||
public struct PendingMessageStatus: Equatable {
|
||||
@ -20,7 +22,7 @@ private enum PendingMessageState {
|
||||
case none
|
||||
case waitingForUploadToStart(groupId: Int64?, upload: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>)
|
||||
case uploading(groupId: Int64?)
|
||||
case waitingToBeSent(groupId: Int64?, content: PendingMessageUploadedContent)
|
||||
case waitingToBeSent(groupId: Int64?, content: PendingMessageUploadedContentAndReuploadInfo)
|
||||
case sending(groupId: Int64?)
|
||||
|
||||
var groupId: Int64? {
|
||||
@ -41,9 +43,11 @@ private enum PendingMessageState {
|
||||
|
||||
private final class PendingMessageContext {
|
||||
var state: PendingMessageState = .none
|
||||
let disposable = MetaDisposable()
|
||||
let uploadDisposable = MetaDisposable()
|
||||
let sendDisposable = MetaDisposable()
|
||||
var status: PendingMessageStatus?
|
||||
var statusSubscribers = Bag<(PendingMessageStatus?) -> Void>()
|
||||
var forcedReuploadOnce: Bool = false
|
||||
}
|
||||
|
||||
private final class PeerPendingMessagesSummaryContext {
|
||||
@ -91,6 +95,7 @@ public final class PendingMessageManager {
|
||||
private let auxiliaryMethods: AccountAuxiliaryMethods
|
||||
private let stateManager: AccountStateManager
|
||||
private let messageMediaPreuploadManager: MessageMediaPreuploadManager
|
||||
private let revalidationContext: MediaReferenceRevalidationContext
|
||||
|
||||
private let queue = Queue()
|
||||
|
||||
@ -107,12 +112,13 @@ public final class PendingMessageManager {
|
||||
|
||||
var transformOutgoingMessageMedia: TransformOutgoingMessageMedia?
|
||||
|
||||
init(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, stateManager: AccountStateManager, messageMediaPreuploadManager: MessageMediaPreuploadManager) {
|
||||
init(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, stateManager: AccountStateManager, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext) {
|
||||
self.network = network
|
||||
self.postbox = postbox
|
||||
self.auxiliaryMethods = auxiliaryMethods
|
||||
self.stateManager = stateManager
|
||||
self.messageMediaPreuploadManager = messageMediaPreuploadManager
|
||||
self.revalidationContext = revalidationContext
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -133,7 +139,8 @@ public final class PendingMessageManager {
|
||||
}
|
||||
context.state = .none
|
||||
updateUploadingPeerIds.insert(id.peerId)
|
||||
context.disposable.dispose()
|
||||
context.sendDisposable.dispose()
|
||||
context.uploadDisposable.dispose()
|
||||
if context.statusSubscribers.isEmpty {
|
||||
self.messageContexts.removeValue(forKey: id)
|
||||
}
|
||||
@ -252,29 +259,12 @@ public final class PendingMessageManager {
|
||||
continue
|
||||
}
|
||||
|
||||
let contentToUpload = messageContentToUpload(network: strongSelf.network, postbox: strongSelf.postbox, auxiliaryMethods: strongSelf.auxiliaryMethods, transformOutgoingMessageMedia: strongSelf.transformOutgoingMessageMedia, messageMediaPreuploadManager: strongSelf.messageMediaPreuploadManager, message: message)
|
||||
let contentUploadSignal = messageContentToUpload(network: strongSelf.network, postbox: strongSelf.postbox, auxiliaryMethods: strongSelf.auxiliaryMethods, transformOutgoingMessageMedia: strongSelf.transformOutgoingMessageMedia, messageMediaPreuploadManager: strongSelf.messageMediaPreuploadManager, revalidationContext: strongSelf.revalidationContext, forceReupload: messageContext.forcedReuploadOnce, message: message)
|
||||
|
||||
switch contentToUpload {
|
||||
case let .ready(content):
|
||||
if let groupingKey = message.groupingKey {
|
||||
strongSelf.beginSendingMessage(messageContext: messageContext, messageId: message.id, groupId: message.groupingKey, content: content)
|
||||
if let current = currentGroupId, current != groupingKey {
|
||||
strongSelf.beginSendingGroupIfPossible(groupId: current)
|
||||
}
|
||||
} else {
|
||||
if let currentGroupId = currentGroupId {
|
||||
strongSelf.beginSendingGroupIfPossible(groupId: currentGroupId)
|
||||
}
|
||||
strongSelf.beginSendingMessage(messageContext: messageContext, messageId: message.id, groupId: message.groupingKey, content: content)
|
||||
}
|
||||
|
||||
currentGroupId = message.groupingKey
|
||||
case let .upload(uploadSignal):
|
||||
if strongSelf.canBeginUploadingMessage(id: message.id) {
|
||||
strongSelf.beginUploadingMessage(messageContext: messageContext, id: message.id, groupId: message.groupingKey, uploadSignal: uploadSignal)
|
||||
} else {
|
||||
messageContext.state = .waitingForUploadToStart(groupId: message.groupingKey, upload: uploadSignal)
|
||||
}
|
||||
if strongSelf.canBeginUploadingMessage(id: message.id) {
|
||||
strongSelf.beginUploadingMessage(messageContext: messageContext, id: message.id, groupId: message.groupingKey, uploadSignal: contentUploadSignal)
|
||||
} else {
|
||||
messageContext.state = .waitingForUploadToStart(groupId: message.groupingKey, upload: contentUploadSignal)
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,7 +275,7 @@ public final class PendingMessageManager {
|
||||
}))
|
||||
}
|
||||
|
||||
private func beginSendingMessage(messageContext: PendingMessageContext, messageId: MessageId, groupId: Int64?, content: PendingMessageUploadedContent) {
|
||||
private func beginSendingMessage(messageContext: PendingMessageContext, messageId: MessageId, groupId: Int64?, content: PendingMessageUploadedContentAndReuploadInfo) {
|
||||
if let groupId = groupId {
|
||||
messageContext.state = .waitingToBeSent(groupId: groupId, content: content)
|
||||
} else {
|
||||
@ -299,8 +289,8 @@ public final class PendingMessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
private func dataForPendingMessageGroup(_ groupId: Int64) -> [(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContent)]? {
|
||||
var result: [(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContent)] = []
|
||||
private func dataForPendingMessageGroup(_ groupId: Int64) -> [(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContentAndReuploadInfo)]? {
|
||||
var result: [(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContentAndReuploadInfo)] = []
|
||||
|
||||
loop: for (id, context) in self.messageContexts {
|
||||
switch context.state {
|
||||
@ -332,16 +322,18 @@ public final class PendingMessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
private func commitSendingMessageGroup(groupId: Int64, messages: [(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContent)]) {
|
||||
private func commitSendingMessageGroup(groupId: Int64, messages: [(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContentAndReuploadInfo)]) {
|
||||
for (context, _, _) in messages {
|
||||
context.state = .sending(groupId: groupId)
|
||||
}
|
||||
let sendMessage: Signal<PendingMessageResult, NoError> = self.sendGroupMessagesContent(network: self.network, postbox: self.postbox, stateManager: self.stateManager, group: messages.map { ($0.1, $0.2) })
|
||||
|> map { next -> PendingMessageResult in
|
||||
return .progress(1.0)
|
||||
}
|
||||
messages[0].0.disposable.set((sendMessage |> deliverOn(self.queue) |> afterDisposed { [weak self] in
|
||||
if let strongSelf = self {
|
||||
|> map { next -> PendingMessageResult in
|
||||
return .progress(1.0)
|
||||
}
|
||||
messages[0].0.sendDisposable.set((sendMessage
|
||||
|> deliverOn(self.queue)
|
||||
|> afterDisposed { [weak self] in
|
||||
/*if let strongSelf = self {
|
||||
assert(strongSelf.queue.isCurrent())
|
||||
for (_, id, _) in messages {
|
||||
if let current = strongSelf.messageContexts[id] {
|
||||
@ -354,18 +346,20 @@ public final class PendingMessageManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}).start())
|
||||
}
|
||||
|
||||
private func commitSendingSingleMessage(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContent) {
|
||||
private func commitSendingSingleMessage(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContentAndReuploadInfo) {
|
||||
messageContext.state = .sending(groupId: nil)
|
||||
let sendMessage: Signal<PendingMessageResult, NoError> = self.sendMessageContent(network: self.network, postbox: self.postbox, stateManager: self.stateManager, messageId: messageId, content: content)
|
||||
|> map { next -> PendingMessageResult in
|
||||
return .progress(1.0)
|
||||
}
|
||||
messageContext.disposable.set((sendMessage |> deliverOn(self.queue) |> afterDisposed { [weak self] in
|
||||
if let strongSelf = self {
|
||||
|> map { next -> PendingMessageResult in
|
||||
return .progress(1.0)
|
||||
}
|
||||
messageContext.sendDisposable.set((sendMessage
|
||||
|> deliverOn(self.queue)
|
||||
|> afterDisposed { [weak self] in
|
||||
/*if let strongSelf = self {
|
||||
assert(strongSelf.queue.isCurrent())
|
||||
if let current = strongSelf.messageContexts[messageId] {
|
||||
current.status = .none
|
||||
@ -376,7 +370,7 @@ public final class PendingMessageManager {
|
||||
strongSelf.messageContexts.removeValue(forKey: messageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}).start(next: { [weak self] next in
|
||||
if let strongSelf = self {
|
||||
assert(strongSelf.queue.isCurrent())
|
||||
@ -404,7 +398,9 @@ public final class PendingMessageManager {
|
||||
subscriber(status)
|
||||
}
|
||||
|
||||
messageContext.disposable.set((uploadSignal |> deliverOn(self.queue) |> `catch` { [weak self] _ -> Signal<PendingMessageUploadedContentResult, NoError> in
|
||||
messageContext.uploadDisposable.set((uploadSignal
|
||||
|> deliverOn(self.queue)
|
||||
|> `catch` { [weak self] _ -> Signal<PendingMessageUploadedContentResult, NoError> in
|
||||
if let strongSelf = self {
|
||||
let modify = strongSelf.postbox.transaction { transaction -> Void in
|
||||
transaction.updateMessage(id, update: { currentMessage in
|
||||
@ -415,7 +411,8 @@ public final class PendingMessageManager {
|
||||
return .update(StoreMessage(id: id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
return modify |> mapToSignal { _ in return .complete() }
|
||||
return modify
|
||||
|> mapToSignal { _ in return .complete() }
|
||||
}
|
||||
return .fail(Void())
|
||||
}).start(next: { [weak self] next in
|
||||
@ -459,7 +456,7 @@ public final class PendingMessageManager {
|
||||
subscriber(status)
|
||||
}
|
||||
|
||||
context.disposable.set((uploadSignal |> deliverOn(self.queue)).start(next: { [weak self] next in
|
||||
context.uploadDisposable.set((uploadSignal |> deliverOn(self.queue)).start(next: { [weak self] next in
|
||||
if let strongSelf = self {
|
||||
assert(strongSelf.queue.isCurrent())
|
||||
|
||||
@ -489,7 +486,7 @@ public final class PendingMessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
private func sendGroupMessagesContent(network: Network, postbox: Postbox, stateManager: AccountStateManager, group: [(messageId: MessageId, content: PendingMessageUploadedContent)]) -> Signal<Void, NoError> {
|
||||
private func sendGroupMessagesContent(network: Network, postbox: Postbox, stateManager: AccountStateManager, group: [(messageId: MessageId, content: PendingMessageUploadedContentAndReuploadInfo)]) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { [weak self] transaction -> Signal<Void, NoError> in
|
||||
if group.isEmpty {
|
||||
return .complete()
|
||||
@ -497,7 +494,7 @@ public final class PendingMessageManager {
|
||||
|
||||
let peerId = group[0].messageId.peerId
|
||||
|
||||
var messages: [(Message, PendingMessageUploadedContent)] = []
|
||||
var messages: [(Message, PendingMessageUploadedContentAndReuploadInfo)] = []
|
||||
for (id, content) in group {
|
||||
if let message = transaction.getMessage(id) {
|
||||
messages.append((message, content))
|
||||
@ -532,7 +529,7 @@ public final class PendingMessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
let sendMessageRequest: Signal<Api.Updates, NoError>
|
||||
let sendMessageRequest: Signal<Api.Updates, MTRpcError>
|
||||
if isForward {
|
||||
flags |= (1 << 9)
|
||||
|
||||
@ -547,7 +544,7 @@ public final class PendingMessageManager {
|
||||
}
|
||||
|
||||
if let uniqueId = uniqueId {
|
||||
switch content {
|
||||
switch content.content {
|
||||
case let .forward(forwardAttribute):
|
||||
forwardIds.append((forwardAttribute.messageId, uniqueId))
|
||||
default:
|
||||
@ -561,27 +558,21 @@ public final class PendingMessageManager {
|
||||
let forwardPeerIds = Set(forwardIds.map { $0.0.peerId })
|
||||
if forwardPeerIds.count != 1 {
|
||||
assertionFailure()
|
||||
sendMessageRequest = .fail(NoError())
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "Invalid forward peer ids"))
|
||||
} else if let inputSourcePeerId = forwardPeerIds.first, let inputSourcePeer = transaction.getPeer(inputSourcePeerId).flatMap(apiInputPeer) {
|
||||
let dependencyTag = PendingMessageRequestDependencyTag(messageId: messages[0].0.id)
|
||||
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: inputSourcePeer, id: forwardIds.map { $0.0.id }, randomId: forwardIds.map { $0.1 }, toPeer: inputPeer), tag: dependencyTag)
|
||||
|> mapError { _ -> NoError in
|
||||
return NoError()
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
sendMessageRequest = .fail(NoError())
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "Invalid forward source"))
|
||||
}
|
||||
//messages.forwardMessages flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer grouped:flags.9?true = Updates;
|
||||
} else {
|
||||
flags |= (1 << 7)
|
||||
if let _ = replyMessageId {
|
||||
flags |= Int32(1 << 0)
|
||||
}
|
||||
|
||||
//messages.sendMultiMedia flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
|
||||
|
||||
var singleMedias: [Api.InputSingleMedia] = []
|
||||
for (message, content) in messages {
|
||||
var uniqueId: Int64?
|
||||
@ -592,7 +583,7 @@ public final class PendingMessageManager {
|
||||
}
|
||||
}
|
||||
if let uniqueId = uniqueId {
|
||||
switch content {
|
||||
switch content.content {
|
||||
case let .media(inputMedia, text):
|
||||
var messageEntities: [Api.MessageEntity]?
|
||||
for attribute in message.attributes {
|
||||
@ -616,32 +607,36 @@ public final class PendingMessageManager {
|
||||
}
|
||||
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendMultiMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, multiMedia: singleMedias))
|
||||
|> mapError { _ -> NoError in
|
||||
return NoError()
|
||||
}
|
||||
}
|
||||
|
||||
return sendMessageRequest
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
if let strongSelf = self {
|
||||
return strongSelf.applySentGroupMessages(postbox: postbox, stateManager: stateManager, messages: messages.map { $0.0 }, result: result)
|
||||
} else {
|
||||
return .never()
|
||||
|> mapToSignal { result -> Signal<Void, MTRpcError> in
|
||||
if let strongSelf = self {
|
||||
return strongSelf.applySentGroupMessages(postbox: postbox, stateManager: stateManager, messages: messages.map { $0.0 }, result: result)
|
||||
|> mapError { _ -> MTRpcError in
|
||||
return MTRpcError(errorCode: 400, errorDescription: "empty")
|
||||
}
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
|> `catch` { _ -> Signal<Void, NoError> in
|
||||
return failMessages(postbox: postbox, ids: group.map { $0.0 })
|
||||
}
|
||||
|> `catch` { error -> Signal<Void, NoError> in
|
||||
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {
|
||||
|
||||
}
|
||||
return failMessages(postbox: postbox, ids: group.map { $0.0 })
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
return failMessages(postbox: postbox, ids: group.map { $0.0 })
|
||||
}
|
||||
} |> switchToLatest
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
private static func sendSecretMessageContent(transaction: Transaction, message: Message, content: PendingMessageUploadedContent) {
|
||||
private static func sendSecretMessageContent(transaction: Transaction, message: Message, content: PendingMessageUploadedContentAndReuploadInfo) {
|
||||
var secretFile: SecretChatOutgoingFile?
|
||||
switch content {
|
||||
switch content.content {
|
||||
case let .secretMedia(file, size, key):
|
||||
if let fileReference = SecretChatOutgoingFileReference(file) {
|
||||
secretFile = SecretChatOutgoingFile(reference: fileReference, size: size, key: key)
|
||||
@ -724,7 +719,8 @@ public final class PendingMessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
private func sendMessageContent(network: Network, postbox: Postbox, stateManager: AccountStateManager, messageId: MessageId, content: PendingMessageUploadedContent) -> Signal<Void, NoError> {
|
||||
private func sendMessageContent(network: Network, postbox: Postbox, stateManager: AccountStateManager, messageId: MessageId, content: PendingMessageUploadedContentAndReuploadInfo) -> Signal<Void, NoError> {
|
||||
let queue = self.queue
|
||||
return postbox.transaction { [weak self] transaction -> Signal<Void, NoError> in
|
||||
guard let message = transaction.getMessage(messageId) else {
|
||||
return .complete()
|
||||
@ -772,47 +768,49 @@ public final class PendingMessageManager {
|
||||
|
||||
let dependencyTag = PendingMessageRequestDependencyTag(messageId: messageId)
|
||||
|
||||
let sendMessageRequest: Signal<Api.Updates, NoError>
|
||||
switch content {
|
||||
let sendMessageRequest: Signal<Api.Updates, MTRpcError>
|
||||
switch content.content {
|
||||
case .text:
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, message: message.text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities), tag: dependencyTag)
|
||||
|> mapError { _ -> NoError in
|
||||
return NoError()
|
||||
}
|
||||
case let .media(inputMedia, text):
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities), tag: dependencyTag)
|
||||
|> mapError { _ -> NoError in
|
||||
return NoError()
|
||||
}
|
||||
case let .forward(sourceInfo):
|
||||
if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = transaction.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) {
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: 0, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer), tag: dependencyTag)
|
||||
|> mapError { _ -> NoError in
|
||||
return NoError()
|
||||
}
|
||||
} else {
|
||||
sendMessageRequest = .fail(NoError())
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||
}
|
||||
case let .chatContextResult(chatContextResult):
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendInlineBotResult(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, randomId: uniqueId, queryId: chatContextResult.queryId, id: chatContextResult.id))
|
||||
|> mapError { _ -> NoError in
|
||||
return NoError()
|
||||
}
|
||||
case .secretMedia:
|
||||
assertionFailure()
|
||||
sendMessageRequest = .fail(NoError())
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||
}
|
||||
|
||||
return sendMessageRequest
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
if let strongSelf = self {
|
||||
return strongSelf.applySentMessage(postbox: postbox, stateManager: stateManager, message: message, result: result)
|
||||
} else {
|
||||
return .never()
|
||||
|> mapToSignal { result -> Signal<Void, MTRpcError> in
|
||||
if let strongSelf = self {
|
||||
return strongSelf.applySentMessage(postbox: postbox, stateManager: stateManager, message: message, result: result)
|
||||
|> mapError { _ -> MTRpcError in
|
||||
return MTRpcError(errorCode: 400, errorDescription: "internal")
|
||||
}
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
|> `catch` { _ -> Signal<Void, NoError> in
|
||||
let modify = postbox.transaction { transaction -> Void in
|
||||
}
|
||||
|> `catch` { error -> Signal<Void, NoError> in
|
||||
queue.async {
|
||||
guard let strongSelf = self, let context = strongSelf.messageContexts[messageId] else {
|
||||
return
|
||||
}
|
||||
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {
|
||||
if !context.forcedReuploadOnce {
|
||||
context.forcedReuploadOnce = true
|
||||
strongSelf.beginSendingMessages([messageId])
|
||||
return
|
||||
}
|
||||
}
|
||||
let _ = (postbox.transaction { transaction -> Void in
|
||||
transaction.updateMessage(message.id, update: { currentMessage in
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
@ -820,9 +818,10 @@ public final class PendingMessageManager {
|
||||
}
|
||||
return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
|
||||
return modify
|
||||
}).start()
|
||||
}
|
||||
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
|
||||
@ -17,25 +17,29 @@ enum PendingMessageUploadedContent {
|
||||
case secretMedia(Api.InputEncryptedFile, Int32, SecretFileEncryptionKey)
|
||||
}
|
||||
|
||||
enum PendingMessageReuploadInfo {
|
||||
case reuploadFile(FileMediaReference)
|
||||
}
|
||||
|
||||
struct PendingMessageUploadedContentAndReuploadInfo {
|
||||
let content: PendingMessageUploadedContent
|
||||
let reuploadInfo: PendingMessageReuploadInfo?
|
||||
}
|
||||
|
||||
enum PendingMessageUploadedContentResult {
|
||||
case progress(Float)
|
||||
case content(PendingMessageUploadedContent)
|
||||
case content(PendingMessageUploadedContentAndReuploadInfo)
|
||||
}
|
||||
|
||||
enum PendingMessageUploadError {
|
||||
case generic
|
||||
}
|
||||
|
||||
enum PendingMessageUploadContent {
|
||||
case ready(PendingMessageUploadedContent)
|
||||
case upload(Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>)
|
||||
func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, message: Message) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> {
|
||||
return messageContentToUpload(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, peerId: message.id.peerId, messageId: message.id, attributes: message.attributes, text: message.text, media: message.media)
|
||||
}
|
||||
|
||||
func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, message: Message) -> PendingMessageUploadContent {
|
||||
return messageContentToUpload(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: message.id.peerId, messageId: message.id, attributes: message.attributes, text: message.text, media: message.media)
|
||||
}
|
||||
|
||||
func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media]) -> PendingMessageUploadContent {
|
||||
func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media]) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> {
|
||||
var contextResult: OutgoingChatContextResultMessageAttribute?
|
||||
var autoremoveAttribute: AutoremoveTimeoutMessageAttribute?
|
||||
for attribute in attributes {
|
||||
@ -58,40 +62,51 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods
|
||||
}
|
||||
|
||||
if let forwardInfo = forwardInfo {
|
||||
return .ready(.forward(forwardInfo))
|
||||
}
|
||||
|
||||
if let forwardInfo = forwardInfo {
|
||||
return .ready(.forward(forwardInfo))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .forward(forwardInfo), reuploadInfo: nil)))
|
||||
} else if let contextResult = contextResult {
|
||||
return .ready(.chatContextResult(contextResult))
|
||||
} else if let media = media.first, let mediaResult = mediaContentToUpload(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, media: media, text: text, autoremoveAttribute: autoremoveAttribute, messageId: messageId, attributes: attributes) {
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .chatContextResult(contextResult), reuploadInfo: nil)))
|
||||
} else if let media = media.first, let mediaResult = mediaContentToUpload(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, peerId: peerId, media: media, text: text, autoremoveAttribute: autoremoveAttribute, messageId: messageId, attributes: attributes) {
|
||||
return mediaResult
|
||||
} else {
|
||||
return .ready(.text(text))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil)))
|
||||
}
|
||||
}
|
||||
|
||||
func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, peerId: PeerId, media: Media, text: String, autoremoveAttribute: AutoremoveTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute]) -> PendingMessageUploadContent? {
|
||||
func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, peerId: PeerId, media: Media, text: String, autoremoveAttribute: AutoremoveTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute]) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? {
|
||||
if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) {
|
||||
if let reference = image.reference, case let .cloud(id, accessHash) = reference {
|
||||
return .ready(.media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash), ttlSeconds: nil), text))
|
||||
if let reference = image.reference, case let .cloud(id, accessHash, maybeFileReference) = reference, let fileReference = maybeFileReference {
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil), text), reuploadInfo: nil)))
|
||||
} else {
|
||||
return .upload(uploadedMediaImageContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, peerId: peerId, image: image, text: text, autoremoveAttribute: autoremoveAttribute))
|
||||
return uploadedMediaImageContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, peerId: peerId, image: image, text: text, autoremoveAttribute: autoremoveAttribute)
|
||||
}
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
if let resource = file.resource as? CloudDocumentMediaResource {
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
return .upload(uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file))
|
||||
return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)
|
||||
} else {
|
||||
return .ready(.media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text))
|
||||
if forceReupload {
|
||||
let mediaReference: AnyMediaReference
|
||||
if file.isSticker {
|
||||
mediaReference = .standalone(media: file)
|
||||
} else {
|
||||
mediaReference = .savedGif(media: file)
|
||||
}
|
||||
return revalidateMediaResourceReference(postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: mediaReference.resourceReference(file.resource)), resource: resource)
|
||||
|> mapError { _ -> PendingMessageUploadError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { fileReference -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil), text), reuploadInfo: nil)))
|
||||
}
|
||||
}
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil), text), reuploadInfo: nil)))
|
||||
}
|
||||
} else {
|
||||
return .upload(uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file))
|
||||
return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)
|
||||
}
|
||||
} else if let contact = media as? TelegramMediaContact {
|
||||
let input = Api.InputMedia.inputMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName, vcard: contact.vCardData ?? "")
|
||||
return .ready(.media(input, text))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(input, text), reuploadInfo: nil)))
|
||||
} else if let map = media as? TelegramMediaMap {
|
||||
let input: Api.InputMedia
|
||||
if let liveBroadcastingTimeout = map.liveBroadcastingTimeout {
|
||||
@ -101,7 +116,7 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
|
||||
} else {
|
||||
input = .inputMediaGeoPoint(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude))
|
||||
}
|
||||
return .ready(.media(input, text))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(input, text), reuploadInfo: nil)))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -152,13 +167,14 @@ private func maybePredownloadedImageResource(postbox: Postbox, peerId: PeerId, r
|
||||
}
|
||||
}
|
||||
})
|
||||
let fetched = postbox.mediaBox.fetchedResource(resource, tag: nil).start()
|
||||
let fetched = postbox.mediaBox.fetchedResource(resource, parameters: nil).start()
|
||||
|
||||
return ActionDisposable {
|
||||
data.dispose()
|
||||
fetched.dispose()
|
||||
}
|
||||
} |> switchToLatest
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
private func maybePredownloadedFileResource(postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, peerId: PeerId, resource: MediaResource) -> Signal<PredownloadedResource, PendingMessageUploadError> {
|
||||
@ -167,20 +183,21 @@ private func maybePredownloadedFileResource(postbox: Postbox, auxiliaryMethods:
|
||||
}
|
||||
|
||||
return auxiliaryMethods.fetchResourceMediaReferenceHash(resource)
|
||||
|> mapToSignal { hash -> Signal<PredownloadedResource, NoError> in
|
||||
if let hash = hash {
|
||||
let reference: CachedSentMediaReferenceKey = .file(hash: hash)
|
||||
return cachedSentMediaReference(postbox: postbox, key: reference) |> map { media -> PredownloadedResource in
|
||||
if let media = media {
|
||||
return .media(media)
|
||||
} else {
|
||||
return .localReference(reference)
|
||||
}
|
||||
|> mapToSignal { hash -> Signal<PredownloadedResource, NoError> in
|
||||
if let hash = hash {
|
||||
let reference: CachedSentMediaReferenceKey = .file(hash: hash)
|
||||
return cachedSentMediaReference(postbox: postbox, key: reference) |> map { media -> PredownloadedResource in
|
||||
if let media = media {
|
||||
return .media(media)
|
||||
} else {
|
||||
return .localReference(reference)
|
||||
}
|
||||
} else {
|
||||
return .single(.localReference(nil))
|
||||
}
|
||||
} |> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
} else {
|
||||
return .single(.localReference(nil))
|
||||
}
|
||||
}
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
}
|
||||
|
||||
private func maybeCacheUploadedResource(postbox: Postbox, key: CachedSentMediaReferenceKey?, result: PendingMessageUploadedContentResult, media: Media) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> {
|
||||
@ -198,77 +215,78 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, trans
|
||||
if let largestRepresentation = largestImageRepresentation(image.representations) {
|
||||
let predownloadedResource: Signal<PredownloadedResource, PendingMessageUploadError> = maybePredownloadedImageResource(postbox: postbox, peerId: peerId, resource: largestRepresentation.resource)
|
||||
return predownloadedResource
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
var referenceKey: CachedSentMediaReferenceKey?
|
||||
switch result {
|
||||
case let .media(media):
|
||||
if let image = media as? TelegramMediaImage, let reference = image.reference, case let .cloud(id, accessHash) = reference {
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
var referenceKey: CachedSentMediaReferenceKey?
|
||||
switch result {
|
||||
case let .media(media):
|
||||
if let image = media as? TelegramMediaImage, let reference = image.reference, case let .cloud(id, accessHash, maybeFileReference) = reference, let fileReference = maybeFileReference {
|
||||
var flags: Int32 = 0
|
||||
var ttlSeconds: Int32?
|
||||
if let autoremoveAttribute = autoremoveAttribute {
|
||||
flags |= 1 << 1
|
||||
ttlSeconds = autoremoveAttribute.timeout
|
||||
}
|
||||
return .single(.progress(1.0))
|
||||
|> then(.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds), text), reuploadInfo: nil))))
|
||||
}
|
||||
case let .localReference(key):
|
||||
referenceKey = key
|
||||
case .none:
|
||||
referenceKey = nil
|
||||
}
|
||||
return multipartUpload(network: network, postbox: postbox, source: .resource(largestRepresentation.resource), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false)
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { next -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch next {
|
||||
case let .progress(progress):
|
||||
return .single(.progress(progress))
|
||||
case let .inputFile(file):
|
||||
var flags: Int32 = 0
|
||||
var ttlSeconds: Int32?
|
||||
if let autoremoveAttribute = autoremoveAttribute {
|
||||
flags |= 1 << 1
|
||||
ttlSeconds = autoremoveAttribute.timeout
|
||||
}
|
||||
return .single(.progress(1.0)) |> then(.single(.content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), ttlSeconds: ttlSeconds), text))))
|
||||
}
|
||||
case let .localReference(key):
|
||||
referenceKey = key
|
||||
case .none:
|
||||
referenceKey = nil
|
||||
}
|
||||
return multipartUpload(network: network, postbox: postbox, source: .resource(largestRepresentation.resource), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false)
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { next -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch next {
|
||||
case let .progress(progress):
|
||||
return .single(.progress(progress))
|
||||
case let .inputFile(file):
|
||||
var flags: Int32 = 0
|
||||
var ttlSeconds: Int32?
|
||||
if let autoremoveAttribute = autoremoveAttribute {
|
||||
flags |= 1 << 1
|
||||
ttlSeconds = autoremoveAttribute.timeout
|
||||
}
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
if let inputPeer = inputPeer {
|
||||
if autoremoveAttribute != nil {
|
||||
return .single(.content(.media(.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds), text)))
|
||||
}
|
||||
|
||||
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds)))
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch result {
|
||||
case let .messageMediaPhoto(_, photo, _):
|
||||
if let photo = photo, let mediaImage = telegramMediaImageFromApiPhoto(photo), let reference = mediaImage.reference, case let .cloud(id, accessHash) = reference {
|
||||
var flags: Int32 = 0
|
||||
var ttlSeconds: Int32?
|
||||
if let autoremoveAttribute = autoremoveAttribute {
|
||||
flags |= 1 << 1
|
||||
ttlSeconds = autoremoveAttribute.timeout
|
||||
}
|
||||
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), ttlSeconds: ttlSeconds), text)), media: mediaImage)
|
||||
}
|
||||
default:
|
||||
break
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
if let inputPeer = inputPeer {
|
||||
if autoremoveAttribute != nil {
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds), text), reuploadInfo: nil)))
|
||||
}
|
||||
|
||||
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds)))
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch result {
|
||||
case let .messageMediaPhoto(_, photo, _):
|
||||
if let photo = photo, let mediaImage = telegramMediaImageFromApiPhoto(photo), let reference = mediaImage.reference, case let .cloud(id, accessHash, maybeFileReference) = reference, let fileReference = maybeFileReference {
|
||||
var flags: Int32 = 0
|
||||
var ttlSeconds: Int32?
|
||||
if let autoremoveAttribute = autoremoveAttribute {
|
||||
flags |= 1 << 1
|
||||
ttlSeconds = autoremoveAttribute.timeout
|
||||
}
|
||||
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds), text), reuploadInfo: nil)), media: mediaImage)
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
break
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
case let .inputSecretFile(file, size, key):
|
||||
return .single(.content(.secretMedia(file, size, key)))
|
||||
}
|
||||
}
|
||||
case let .inputSecretFile(file, size, key):
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(file, size, key), reuploadInfo: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.content(.text(text)))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,9 +358,9 @@ private enum UploadedMediaThumbnailResult {
|
||||
case none
|
||||
}
|
||||
|
||||
private enum UploadedMediaThumbnail {
|
||||
private enum UploadedMediaFileAndThumbnail {
|
||||
case pending
|
||||
case done(UploadedMediaThumbnailResult)
|
||||
case done(TelegramMediaFile, UploadedMediaThumbnailResult)
|
||||
}
|
||||
|
||||
private func uploadedThumbnail(network: Network, postbox: Postbox, image: TelegramMediaImageRepresentation) -> Signal<Api.InputFile?, PendingMessageUploadError> {
|
||||
@ -375,12 +393,14 @@ public func statsCategoryForFileWithAttributes(_ attributes: [TelegramMediaFileA
|
||||
}
|
||||
|
||||
private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, peerId: PeerId, messageId: MessageId?, text: String, attributes: [MessageAttribute], file: TelegramMediaFile) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> {
|
||||
return maybePredownloadedFileResource(postbox: postbox, auxiliaryMethods: auxiliaryMethods, peerId: peerId, resource: file.resource) |> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
return maybePredownloadedFileResource(postbox: postbox, auxiliaryMethods: auxiliaryMethods, peerId: peerId, resource: file.resource)
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
var referenceKey: CachedSentMediaReferenceKey?
|
||||
switch result {
|
||||
case let .media(media):
|
||||
if let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource {
|
||||
return .single(.progress(1.0)) |> then(.single(.content(.media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text))))
|
||||
if let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference {
|
||||
return .single(.progress(1.0))
|
||||
|> then(.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil), text), reuploadInfo: nil))))
|
||||
}
|
||||
case let .localReference(key):
|
||||
referenceKey = key
|
||||
@ -412,10 +432,10 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
let transform: Signal<UploadedMediaTransform, Void>
|
||||
if let transformOutgoingMessageMedia = transformOutgoingMessageMedia, let messageId = messageId, !alreadyTransformed {
|
||||
transform = .single(.pending)
|
||||
|> then(transformOutgoingMessageMedia(postbox, network, file, false)
|
||||
|> mapToSignal { media -> Signal<UploadedMediaTransform, NoError> in
|
||||
|> then(transformOutgoingMessageMedia(postbox, network, .standalone(media: file), false)
|
||||
|> mapToSignal { mediaReference -> Signal<UploadedMediaTransform, NoError> in
|
||||
return postbox.transaction { transaction -> UploadedMediaTransform in
|
||||
if let media = media {
|
||||
if let media = mediaReference?.media {
|
||||
if let id = media.id {
|
||||
transaction.updateMedia(id, update: media)
|
||||
transaction.updateMessage(messageId, update: { currentMessage in
|
||||
@ -443,40 +463,40 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
transform = .single(.done(file))
|
||||
}
|
||||
|
||||
let thumbnail: Signal<UploadedMediaThumbnail, PendingMessageUploadError> = .single(.pending)
|
||||
let transformedFileAndThumbnail: Signal<UploadedMediaFileAndThumbnail, PendingMessageUploadError> = .single(.pending)
|
||||
|> then(transform
|
||||
|> mapToSignalPromotingError { media -> Signal<UploadedMediaThumbnail, PendingMessageUploadError> in
|
||||
|> mapToSignalPromotingError { media -> Signal<UploadedMediaFileAndThumbnail, PendingMessageUploadError> in
|
||||
switch media {
|
||||
case .pending:
|
||||
return .single(.pending)
|
||||
case let .done(media):
|
||||
if let media = media as? TelegramMediaFile, let smallestThumbnail = smallestImageRepresentation(media.previewRepresentations) {
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
return .single(.done(.none))
|
||||
return .single(.done(media, .none))
|
||||
} else {
|
||||
return uploadedThumbnail(network: network, postbox: postbox, image: smallestThumbnail)
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> map { result in
|
||||
if let result = result {
|
||||
return .done(.file(result))
|
||||
return .done(media, .file(result))
|
||||
} else {
|
||||
return .done(.none)
|
||||
return .done(media, .none)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.done(.none))
|
||||
return .single(.done(file, .none))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return combineLatest(upload, thumbnail)
|
||||
|> mapToSignal { content, thumbnailResult -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
return combineLatest(upload, transformedFileAndThumbnail)
|
||||
|> mapToSignal { content, fileAndThumbnailResult -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch content {
|
||||
case let .progress(progress):
|
||||
return .single(.progress(progress))
|
||||
case let .inputFile(inputFile):
|
||||
if case let .done(thumbnail) = thumbnailResult {
|
||||
if case let .done(file, thumbnail) = fileAndThumbnailResult {
|
||||
var flags: Int32 = 0
|
||||
|
||||
var thumbnailFile: Api.InputFile?
|
||||
@ -497,7 +517,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
}
|
||||
|
||||
if ttlSeconds != nil {
|
||||
return .single(.content(.media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds), text)))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds), text), reuploadInfo: nil)))
|
||||
}
|
||||
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
@ -507,18 +527,18 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
if let inputPeer = inputPeer {
|
||||
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds)))
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch result {
|
||||
case let .messageMediaDocument(_, document, _):
|
||||
if let document = document, let mediaFile = telegramMediaFileFromApiDocument(document), let resource = mediaFile.resource as? CloudDocumentMediaResource {
|
||||
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text)), media: mediaFile)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return .fail(.generic)
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch result {
|
||||
case let .messageMediaDocument(_, document, _):
|
||||
if let document = document, let mediaFile = telegramMediaFileFromApiDocument(document), let resource = mediaFile.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference {
|
||||
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil), text), reuploadInfo: nil)), media: mediaFile)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
@ -527,8 +547,8 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
return .complete()
|
||||
}
|
||||
case let .inputSecretFile(file, size, key):
|
||||
if case .done = thumbnailResult {
|
||||
return .single(.content(.secretMedia(file, size, key)))
|
||||
if case .done = fileAndThumbnailResult {
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(file, size, key), reuploadInfo: nil)))
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
@ -562,6 +562,18 @@ private func parseEntities(_ entities: [SecretApi46.MessageEntity]?) -> TextEnti
|
||||
return TextEntitiesMessageAttribute(entities: result)
|
||||
}
|
||||
|
||||
private func maximumMediaAutoremoveTimeout(_ media: [Media]) -> Int32 {
|
||||
var maxDuration: Int32 = 0
|
||||
for media in media {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
if let duration = file.duration {
|
||||
maxDuration = max(maxDuration, duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxDuration
|
||||
}
|
||||
|
||||
private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi46.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? {
|
||||
switch apiMessage {
|
||||
case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId):
|
||||
@ -570,10 +582,6 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
var attributes: [MessageAttribute] = []
|
||||
var resources: [(MediaResource, Data)] = []
|
||||
|
||||
if ttl > 0 {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: ttl, countdownBeginTime: nil))
|
||||
}
|
||||
|
||||
attributes.append(parseEntities(entities))
|
||||
|
||||
if let viaBotName = viaBotName, !viaBotName.isEmpty {
|
||||
@ -599,7 +607,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
}
|
||||
case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv):
|
||||
if let file = file {
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), reference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
|
||||
parsedMedia.append(fileMedia)
|
||||
}
|
||||
case let .decryptedMessageMediaDocument(thumb, thumbW, thumbH, mimeType, size, key, iv, attributes, caption):
|
||||
@ -619,7 +627,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource))
|
||||
resources.append((resource, thumb.makeData()))
|
||||
}
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), reference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
parsedMedia.append(fileMedia)
|
||||
}
|
||||
case let .decryptedMessageMediaVideo(thumb, thumbW, thumbH, duration, mimeType, w, h, size, key, iv, caption):
|
||||
@ -634,7 +642,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource))
|
||||
resources.append((resource, thumb.makeData()))
|
||||
}
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), reference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
parsedMedia.append(fileMedia)
|
||||
}
|
||||
case let .decryptedMessageMediaExternalDocument(id, accessHash, _, mimeType, size, thumb, dcId, attributes):
|
||||
@ -649,7 +657,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
case let .photoSize(_, location, w, h, size):
|
||||
switch location {
|
||||
case let .fileLocation(dcId, volumeId, localId, secret):
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size))))
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size), fileReference: nil)))
|
||||
case .fileLocationUnavailable:
|
||||
break
|
||||
}
|
||||
@ -657,7 +665,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
if bytes.size > 0 {
|
||||
switch location {
|
||||
case let .fileLocation(dcId, volumeId, localId, secret):
|
||||
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size)
|
||||
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size, fileReference: nil)
|
||||
resources.append((resource, bytes.makeData()))
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource))
|
||||
case .fileLocationUnavailable:
|
||||
@ -667,7 +675,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
default:
|
||||
break
|
||||
}
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size)), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), reference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
parsedMedia.append(fileMedia)
|
||||
case let .decryptedMessageMediaWebPage(url):
|
||||
parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url)))
|
||||
@ -681,6 +689,11 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ttl > 0 {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: max(ttl, maximumMediaAutoremoveTimeout(parsedMedia)), countdownBeginTime: nil))
|
||||
}
|
||||
|
||||
if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) {
|
||||
attributes.append(ReplyMessageAttribute(messageId: replyMessageId))
|
||||
}
|
||||
@ -765,10 +778,6 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
var attributes: [MessageAttribute] = []
|
||||
var resources: [(MediaResource, Data)] = []
|
||||
|
||||
if ttl > 0 {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: ttl, countdownBeginTime: nil))
|
||||
}
|
||||
|
||||
if let entitiesAttribute = entities.flatMap(parseEntities) {
|
||||
attributes.append(entitiesAttribute)
|
||||
}
|
||||
@ -796,7 +805,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
}
|
||||
case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv):
|
||||
if let file = file {
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), reference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
|
||||
parsedMedia.append(fileMedia)
|
||||
}
|
||||
case let .decryptedMessageMediaDocument(thumb, thumbW, thumbH, mimeType, size, key, iv, attributes, caption):
|
||||
@ -816,7 +825,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource))
|
||||
resources.append((resource, thumb.makeData()))
|
||||
}
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), reference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
parsedMedia.append(fileMedia)
|
||||
}
|
||||
case let .decryptedMessageMediaVideo(thumb, thumbW, thumbH, duration, mimeType, w, h, size, key, iv, caption):
|
||||
@ -831,7 +840,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource))
|
||||
resources.append((resource, thumb.makeData()))
|
||||
}
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), reference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
parsedMedia.append(fileMedia)
|
||||
}
|
||||
case let .decryptedMessageMediaExternalDocument(id, accessHash, date, mimeType, size, thumb, dcId, attributes):
|
||||
@ -846,7 +855,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
case let .photoSize(_, location, w, h, size):
|
||||
switch location {
|
||||
case let .fileLocation(dcId, volumeId, localId, secret):
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size))))
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size), fileReference: nil)))
|
||||
case .fileLocationUnavailable:
|
||||
break
|
||||
}
|
||||
@ -854,7 +863,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
if bytes.size > 0 {
|
||||
switch location {
|
||||
case let .fileLocation(dcId, volumeId, localId, secret):
|
||||
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size)
|
||||
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size, fileReference: nil)
|
||||
resources.append((resource, bytes.makeData()))
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource))
|
||||
case .fileLocationUnavailable:
|
||||
@ -864,7 +873,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
default:
|
||||
break
|
||||
}
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size)), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), reference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
|
||||
parsedMedia.append(fileMedia)
|
||||
case let .decryptedMessageMediaWebPage(url):
|
||||
parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url)))
|
||||
@ -879,6 +888,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
}
|
||||
}
|
||||
|
||||
if ttl > 0 {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: max(ttl, maximumMediaAutoremoveTimeout(parsedMedia)), countdownBeginTime: nil))
|
||||
}
|
||||
|
||||
var groupingKey: Int64?
|
||||
if let groupedId = groupedId {
|
||||
inner: for media in parsedMedia {
|
||||
|
||||
@ -9,36 +9,27 @@ import Foundation
|
||||
import MtProtoKitDynamic
|
||||
#endif
|
||||
|
||||
public struct ChatContextGeoPoint {
|
||||
let latitude: Double
|
||||
let longtitude: Double
|
||||
public init(latitude: Double, longtitude: Double) {
|
||||
self.latitude = latitude
|
||||
self.longtitude = longtitude
|
||||
}
|
||||
}
|
||||
|
||||
public func requestChatContextResults(account: Account, botId: PeerId, peerId: PeerId, query: String, offset: String, geopoint: ChatContextGeoPoint? = nil) -> Signal<ChatContextResultCollection?, NoError> {
|
||||
return account.postbox.transaction { transaction -> (bot: Peer, peer: Peer)? in
|
||||
public func requestChatContextResults(account: Account, botId: PeerId, peerId: PeerId, query: String, location: Signal<(Double, Double)?, NoError> = .single(nil), offset: String) -> Signal<ChatContextResultCollection?, NoError> {
|
||||
return combineLatest(account.postbox.transaction { transaction -> (bot: Peer, peer: Peer)? in
|
||||
if let bot = transaction.getPeer(botId), let peer = transaction.getPeer(peerId) {
|
||||
return (bot, peer)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|> mapToSignal { botAndPeer -> Signal<ChatContextResultCollection?, NoError> in
|
||||
}, location)
|
||||
|> mapToSignal { botAndPeer, location -> Signal<ChatContextResultCollection?, NoError> in
|
||||
if let (bot, peer) = botAndPeer, let inputBot = apiInputUser(bot) {
|
||||
var flags: Int32 = 0
|
||||
var inputPeer: Api.InputPeer = .inputPeerEmpty
|
||||
var geoPoint: Api.InputGeoPoint?
|
||||
if let actualInputPeer = apiInputPeer(peer) {
|
||||
inputPeer = actualInputPeer
|
||||
}
|
||||
var inputGeo: Api.InputGeoPoint? = nil
|
||||
if let geopoint = geopoint {
|
||||
inputGeo = Api.InputGeoPoint.inputGeoPoint(lat: geopoint.latitude, long: geopoint.longtitude)
|
||||
if let (latitude, longitude) = location {
|
||||
flags |= (1 << 0)
|
||||
geoPoint = Api.InputGeoPoint.inputGeoPoint(lat: latitude, long: longitude)
|
||||
}
|
||||
return account.network.request(Api.functions.messages.getInlineBotResults(flags: flags, bot: inputBot, peer: inputPeer, geoPoint: inputGeo, query: query, offset: offset))
|
||||
return account.network.request(Api.functions.messages.getInlineBotResults(flags: flags, bot: inputBot, peer: inputPeer, geoPoint: geoPoint, query: query, offset: offset))
|
||||
|> map { result -> ChatContextResultCollection? in
|
||||
return ChatContextResultCollection(apiResults: result, botId: bot.id)
|
||||
}
|
||||
|
||||
@ -27,25 +27,21 @@ public func requestEditMessage(account: Account, messageId: MessageId, text: Str
|
||||
let uploadedMedia: Signal<PendingMessageUploadedContentResult?, NoError>
|
||||
switch media {
|
||||
case .keep:
|
||||
uploadedMedia = .single(.progress(0.0)) |> then(.single(nil))
|
||||
uploadedMedia = .single(.progress(0.0))
|
||||
|> then(.single(nil))
|
||||
case let .update(media):
|
||||
if let uploadData = mediaContentToUpload(network: account.network, postbox: account.postbox, auxiliaryMethods: account.auxiliaryMethods, transformOutgoingMessageMedia: account.transformOutgoingMessageMedia, messageMediaPreuploadManager: account.messageMediaPreuploadManager, peerId: messageId.peerId, media: media, text: "", autoremoveAttribute: nil, messageId: nil, attributes: []) {
|
||||
switch uploadData {
|
||||
case let .ready(content):
|
||||
uploadedMedia = .single(.content(content))
|
||||
case let .upload(upload):
|
||||
uploadedMedia = .single(.progress(0.027)) |> then(upload)
|
||||
|> map { result -> PendingMessageUploadedContentResult? in
|
||||
switch result {
|
||||
case let .progress(value):
|
||||
return .progress(max(value, 0.027))
|
||||
case let .content(content):
|
||||
return .content(content)
|
||||
}
|
||||
}
|
||||
|> `catch` { _ -> Signal<PendingMessageUploadedContentResult?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
if let uploadSignal = mediaContentToUpload(network: account.network, postbox: account.postbox, auxiliaryMethods: account.auxiliaryMethods, transformOutgoingMessageMedia: account.transformOutgoingMessageMedia, messageMediaPreuploadManager: account.messageMediaPreuploadManager, revalidationContext: account.mediaReferenceRevalidationContext, forceReupload: false, peerId: messageId.peerId, media: media, text: "", autoremoveAttribute: nil, messageId: nil, attributes: []) {
|
||||
uploadedMedia = .single(.progress(0.027)) |> then(uploadSignal)
|
||||
|> map { result -> PendingMessageUploadedContentResult? in
|
||||
switch result {
|
||||
case let .progress(value):
|
||||
return .progress(max(value, 0.027))
|
||||
case let .content(content):
|
||||
return .content(content)
|
||||
}
|
||||
}
|
||||
|> `catch` { _ -> Signal<PendingMessageUploadedContentResult?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
} else {
|
||||
uploadedMedia = .single(nil)
|
||||
@ -60,7 +56,7 @@ public func requestEditMessage(account: Account, messageId: MessageId, text: Str
|
||||
case let .progress(value):
|
||||
return .single(.progress(value))
|
||||
case let .content(content):
|
||||
pendingMediaContent = content
|
||||
pendingMediaContent = content.content
|
||||
}
|
||||
}
|
||||
return account.postbox.transaction { transaction -> (Peer?, SimpleDictionary<PeerId, Peer>) in
|
||||
|
||||
@ -49,10 +49,10 @@ public func requestPeerPhotos(account:Account, peerId:PeerId) -> Signal<[Telegra
|
||||
let reference: TelegramMediaImageReference
|
||||
switch photo {
|
||||
case let .photo(data):
|
||||
reference = .cloud(imageId: data.id, accessHash: data.accessHash)
|
||||
reference = .cloud(imageId: data.id, accessHash: data.accessHash, fileReference: data.fileReference.makeData())
|
||||
image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudImage, id: data.id), representations: telegramMediaImageRepresentationsFromApiSizes(data.sizes), reference: reference)
|
||||
case let .photoEmpty(id: id):
|
||||
reference = .cloud(imageId: id, accessHash: 0)
|
||||
reference = .cloud(imageId: id, accessHash: 0, fileReference: nil)
|
||||
image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudImage, id: id), representations: [], reference: reference)
|
||||
}
|
||||
images.append(TelegramPeerPhoto(image: image, reference: reference, index: i, totalCount: totalCount))
|
||||
|
||||
@ -169,84 +169,173 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
||||
return processedSearchResult
|
||||
}
|
||||
|
||||
|
||||
public func downloadMessage(account: Account, messageId: MessageId) -> Signal<Message?, NoError> {
|
||||
return account.postbox.transaction { transaction -> Message? in
|
||||
public func downloadMessage(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<Message?, NoError> {
|
||||
return postbox.transaction { transaction -> Message? in
|
||||
return transaction.getMessage(messageId)
|
||||
} |> mapToSignal { message in
|
||||
if let _ = message {
|
||||
return .single(message)
|
||||
} else {
|
||||
return account.postbox.loadedPeerWithId(messageId.peerId) |> mapToSignal { peer -> Signal<Message?, NoError> in
|
||||
let signal: Signal<Api.messages.Messages, MTRpcError>
|
||||
if messageId.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
if let channel = apiInputChannel(peer) {
|
||||
signal = account.network.request(Api.functions.channels.getMessages(channel: channel, id: [Api.InputMessage.inputMessageID(id: messageId.id)]))
|
||||
} else {
|
||||
signal = .complete()
|
||||
}
|
||||
} |> mapToSignal { message in
|
||||
if let _ = message {
|
||||
return .single(message)
|
||||
} else {
|
||||
return postbox.loadedPeerWithId(messageId.peerId) |> mapToSignal { peer -> Signal<Message?, NoError> in
|
||||
let signal: Signal<Api.messages.Messages, MTRpcError>
|
||||
if messageId.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
if let channel = apiInputChannel(peer) {
|
||||
signal = network.request(Api.functions.channels.getMessages(channel: channel, id: [Api.InputMessage.inputMessageID(id: messageId.id)]))
|
||||
} else {
|
||||
signal = account.network.request(Api.functions.messages.getMessages(id: [Api.InputMessage.inputMessageID(id: messageId.id)]))
|
||||
signal = .complete()
|
||||
}
|
||||
|
||||
return signal |> mapError {_ in} |> mapToSignal { result -> Signal<Message?, Void> in
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
switch result {
|
||||
case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .messages(apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let.messagesSlice(_, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case .messagesNotModified:
|
||||
messages = []
|
||||
chats = []
|
||||
users = []
|
||||
}
|
||||
|
||||
let postboxSignal = account.postbox.transaction { transaction -> Message? in
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
|
||||
for user in users {
|
||||
if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||
peers[user.id] = user
|
||||
}
|
||||
}
|
||||
|
||||
for chat in chats {
|
||||
if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) {
|
||||
peers[groupOrChannel.id] = groupOrChannel
|
||||
}
|
||||
}
|
||||
|
||||
var renderedMessages: [Message] = []
|
||||
for message in messages {
|
||||
if let message = StoreMessage(apiMessage: message), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) {
|
||||
renderedMessages.append(renderedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
return renderedMessages.first
|
||||
}
|
||||
|
||||
return postboxSignal
|
||||
}
|
||||
|
||||
}
|
||||
|> `catch` { _ -> Signal<Message?, NoError> in
|
||||
return .single(nil)
|
||||
} else {
|
||||
signal = network.request(Api.functions.messages.getMessages(id: [Api.InputMessage.inputMessageID(id: messageId.id)]))
|
||||
}
|
||||
|
||||
return signal |> mapError {_ in} |> mapToSignal { result -> Signal<Message?, Void> in
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
switch result {
|
||||
case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .messages(apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let.messagesSlice(_, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case .messagesNotModified:
|
||||
messages = []
|
||||
chats = []
|
||||
users = []
|
||||
}
|
||||
|
||||
let postboxSignal = postbox.transaction { transaction -> Message? in
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
|
||||
for user in users {
|
||||
if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||
peers[user.id] = user
|
||||
}
|
||||
}
|
||||
|
||||
for chat in chats {
|
||||
if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) {
|
||||
peers[groupOrChannel.id] = groupOrChannel
|
||||
}
|
||||
}
|
||||
|
||||
var renderedMessages: [Message] = []
|
||||
for message in messages {
|
||||
if let message = StoreMessage(apiMessage: message), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) {
|
||||
renderedMessages.append(renderedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
return renderedMessages.first
|
||||
}
|
||||
|
||||
return postboxSignal
|
||||
}
|
||||
|
||||
}
|
||||
|> `catch` { _ -> Signal<Message?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchRemoteMessage(postbox: Postbox, network: Network, message: MessageReference) -> Signal<Message?, NoError> {
|
||||
guard case let .message(peer, id) = message.content else {
|
||||
return .single(nil)
|
||||
}
|
||||
let signal: Signal<Api.messages.Messages, MTRpcError>
|
||||
if id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
if let channel = peer.inputChannel {
|
||||
signal = network.request(Api.functions.channels.getMessages(channel: channel, id: [Api.InputMessage.inputMessageID(id: id.id)]))
|
||||
} else {
|
||||
signal = .fail(MTRpcError(errorCode: 400, errorDescription: "Peer Not Found"))
|
||||
}
|
||||
} else if id.peerId.namespace == Namespaces.Peer.CloudUser || id.peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
signal = network.request(Api.functions.messages.getMessages(id: [Api.InputMessage.inputMessageID(id: id.id)]))
|
||||
} else {
|
||||
signal = .fail(MTRpcError(errorCode: 400, errorDescription: "Invalid Peer"))
|
||||
}
|
||||
|
||||
return signal
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.Messages?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Message?, Void> in
|
||||
guard let result = result else {
|
||||
return .single(nil)
|
||||
}
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
switch result {
|
||||
case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .messages(apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let.messagesSlice(_, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case .messagesNotModified:
|
||||
messages = []
|
||||
chats = []
|
||||
users = []
|
||||
}
|
||||
|
||||
return postbox.transaction { transaction -> Message? in
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
|
||||
for user in users {
|
||||
if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||
peers[user.id] = user
|
||||
}
|
||||
}
|
||||
|
||||
for chat in chats {
|
||||
if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) {
|
||||
peers[groupOrChannel.id] = groupOrChannel
|
||||
}
|
||||
}
|
||||
|
||||
var renderedMessages: [Message] = []
|
||||
for message in messages {
|
||||
if let message = StoreMessage(apiMessage: message), case let .Id(updatedId) = message.id {
|
||||
var addedExisting = false
|
||||
if transaction.getMessage(updatedId) != nil {
|
||||
transaction.updateMessage(updatedId, update: { _ in
|
||||
return .update(message)
|
||||
})
|
||||
if let updatedMessage = transaction.getMessage(updatedId) {
|
||||
renderedMessages.append(updatedMessage)
|
||||
addedExisting = true
|
||||
}
|
||||
}
|
||||
|
||||
if !addedExisting, let renderedMessage = locallyRenderedMessage(message: message, peers: peers) {
|
||||
renderedMessages.append(renderedMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return renderedMessages.first
|
||||
}
|
||||
}
|
||||
|> `catch` { _ -> Signal<Message?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timestamp: Int32) -> Signal<MessageId?, NoError> {
|
||||
@ -282,3 +371,87 @@ public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timesta
|
||||
}
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
enum UpdatedRemotePeerError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func updatedRemotePeer(postbox: Postbox, network: Network, peer: PeerReference) -> Signal<Peer, UpdatedRemotePeerError> {
|
||||
if let inputUser = peer.inputUser {
|
||||
return network.request(Api.functions.users.getUsers(id: [inputUser]))
|
||||
|> mapError { _ -> UpdatedRemotePeerError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Peer, UpdatedRemotePeerError> in
|
||||
if let updatedPeer = result.first.flatMap(TelegramUser.init(user:)), updatedPeer.id == peer.id {
|
||||
return postbox.transaction { transaction -> Peer in
|
||||
updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
return updatedPeer
|
||||
}
|
||||
|> mapError { _ -> UpdatedRemotePeerError in
|
||||
return .generic
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
} else if case let .group(id) = peer {
|
||||
return network.request(Api.functions.messages.getChats(id: [id]))
|
||||
|> mapError { _ -> UpdatedRemotePeerError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Peer, UpdatedRemotePeerError> in
|
||||
let chats: [Api.Chat]
|
||||
switch result {
|
||||
case let .chats(c):
|
||||
chats = c
|
||||
case let .chatsSlice(_, c):
|
||||
chats = c
|
||||
}
|
||||
if let updatedPeer = chats.first.flatMap(parseTelegramGroupOrChannel), updatedPeer.id == peer.id {
|
||||
return postbox.transaction { transaction -> Peer in
|
||||
updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
return updatedPeer
|
||||
}
|
||||
|> mapError { _ -> UpdatedRemotePeerError in
|
||||
return .generic
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
} else if let inputChannel = peer.inputChannel {
|
||||
return network.request(Api.functions.channels.getChannels(id: [inputChannel]))
|
||||
|> mapError { _ -> UpdatedRemotePeerError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Peer, UpdatedRemotePeerError> in
|
||||
let chats: [Api.Chat]
|
||||
switch result {
|
||||
case let .chats(c):
|
||||
chats = c
|
||||
case let .chatsSlice(_, c):
|
||||
chats = c
|
||||
}
|
||||
if let updatedPeer = chats.first.flatMap(parseTelegramGroupOrChannel), updatedPeer.id == peer.id {
|
||||
return postbox.transaction { transaction -> Peer in
|
||||
updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
return updatedPeer
|
||||
}
|
||||
|> mapError { _ -> UpdatedRemotePeerError in
|
||||
return .generic
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ public class SecureFileMediaResource: TelegramCloudMediaResource, TelegramMultip
|
||||
return Int(self.file.size)
|
||||
}
|
||||
|
||||
var apiInputLocation: Api.InputFileLocation {
|
||||
func apiInputLocation(fileReference: Data?) -> Api.InputFileLocation? {
|
||||
return Api.InputFileLocation.inputSecureFileLocation(id: self.file.id, accessHash: self.file.accessHash)
|
||||
}
|
||||
|
||||
|
||||
@ -204,7 +204,7 @@ public class Serialization: NSObject, MTSerialization {
|
||||
|
||||
|
||||
public func currentLayer() -> UInt {
|
||||
return 82
|
||||
return 83
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
||||
@ -445,6 +445,10 @@ extension StoreMessage {
|
||||
medias.append(mediaValue)
|
||||
|
||||
if let expirationTimer = expirationTimer, expirationTimer > 0 {
|
||||
var updatedExpirationTimer = expirationTimer
|
||||
if let file = mediaValue as? TelegramMediaFile, let duration = file.duration {
|
||||
updatedExpirationTimer = max(updatedExpirationTimer, duration)
|
||||
}
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: expirationTimer, countdownBeginTime: nil))
|
||||
|
||||
consumableContent = (true, false)
|
||||
|
||||
@ -14,14 +14,14 @@ private enum SynchronizeSavedGifsOperationContentType: Int32 {
|
||||
}
|
||||
|
||||
enum SynchronizeSavedGifsOperationContent: PostboxCoding {
|
||||
case add(id: Int64, accessHash: Int64)
|
||||
case add(id: Int64, accessHash: Int64, fileReference: FileMediaReference?)
|
||||
case remove(id: Int64, accessHash: Int64)
|
||||
case sync
|
||||
|
||||
init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("r", orElse: 0) {
|
||||
case SynchronizeSavedGifsOperationContentType.add.rawValue:
|
||||
self = .add(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0))
|
||||
self = .add(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0), fileReference: decoder.decodeAnyObjectForKey("fr", decoder: { FileMediaReference(decoder: $0) }) as? FileMediaReference)
|
||||
case SynchronizeSavedGifsOperationContentType.remove.rawValue:
|
||||
self = .remove(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0))
|
||||
case SynchronizeSavedGifsOperationContentType.sync.rawValue:
|
||||
@ -34,10 +34,15 @@ enum SynchronizeSavedGifsOperationContent: PostboxCoding {
|
||||
|
||||
func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .add(id, accessHash):
|
||||
case let .add(id, accessHash, fileReference):
|
||||
encoder.encodeInt32(SynchronizeSavedGifsOperationContentType.add.rawValue, forKey: "r")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
encoder.encodeInt64(accessHash, forKey: "h")
|
||||
if let fileReference = fileReference {
|
||||
encoder.encodeObjectWithEncoder(fileReference, encoder: fileReference.encode, forKey: "fr")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "fr")
|
||||
}
|
||||
case let .remove(id, accessHash):
|
||||
encoder.encodeInt32(SynchronizeSavedGifsOperationContentType.remove.rawValue, forKey: "r")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
@ -84,11 +89,11 @@ func addSynchronizeSavedGifsOperation(transaction: Transaction, operation: Synch
|
||||
transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeSavedGifsOperation(content: .sync))
|
||||
}
|
||||
|
||||
public func addSavedGif(postbox: Postbox, file: TelegramMediaFile) -> Signal<Void, NoError> {
|
||||
public func addSavedGif(postbox: Postbox, fileReference: FileMediaReference) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
if let resource = file.resource as? CloudDocumentMediaResource {
|
||||
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentGifs, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 200)
|
||||
addSynchronizeSavedGifsOperation(transaction: transaction, operation: .add(id: resource.fileId, accessHash: resource.accessHash))
|
||||
if let resource = fileReference.media.resource as? CloudDocumentMediaResource {
|
||||
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentGifs, item: OrderedItemListEntry(id: RecentMediaItemId(fileReference.media.fileId).rawValue, contents: RecentMediaItem(fileReference.media)), removeTailIfCountExceeds: 200)
|
||||
addSynchronizeSavedGifsOperation(transaction: transaction, operation: .add(id: resource.fileId, accessHash: resource.accessHash, fileReference: fileReference))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,14 +14,14 @@ private enum SynchronizeSavedStickersOperationContentType: Int32 {
|
||||
}
|
||||
|
||||
enum SynchronizeSavedStickersOperationContent: PostboxCoding {
|
||||
case add(id: Int64, accessHash: Int64)
|
||||
case add(id: Int64, accessHash: Int64, fileReference: FileMediaReference?)
|
||||
case remove(id: Int64, accessHash: Int64)
|
||||
case sync
|
||||
|
||||
init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("r", orElse: 0) {
|
||||
case SynchronizeSavedStickersOperationContentType.add.rawValue:
|
||||
self = .add(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0))
|
||||
self = .add(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0), fileReference: decoder.decodeAnyObjectForKey("fr", decoder: { FileMediaReference(decoder: $0) }) as? FileMediaReference)
|
||||
case SynchronizeSavedStickersOperationContentType.remove.rawValue:
|
||||
self = .remove(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0))
|
||||
case SynchronizeSavedStickersOperationContentType.sync.rawValue:
|
||||
@ -34,10 +34,15 @@ enum SynchronizeSavedStickersOperationContent: PostboxCoding {
|
||||
|
||||
func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .add(id, accessHash):
|
||||
case let .add(id, accessHash, fileReference):
|
||||
encoder.encodeInt32(SynchronizeSavedStickersOperationContentType.add.rawValue, forKey: "r")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
encoder.encodeInt64(accessHash, forKey: "h")
|
||||
if let fileReference = fileReference {
|
||||
encoder.encodeObjectWithEncoder(fileReference, encoder: fileReference.encode, forKey: "fr")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "fr")
|
||||
}
|
||||
case let .remove(id, accessHash):
|
||||
encoder.encodeInt32(SynchronizeSavedStickersOperationContentType.remove.rawValue, forKey: "r")
|
||||
encoder.encodeInt64(id, forKey: "i")
|
||||
@ -166,7 +171,7 @@ public func addSavedSticker(postbox: Postbox, network: Network, file: TelegramMe
|
||||
public func addSavedSticker(transaction: Transaction, file: TelegramMediaFile, stringRepresentations: [String]) {
|
||||
if let resource = file.resource as? CloudDocumentMediaResource {
|
||||
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudSavedStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: SavedStickerItem(file: file, stringRepresentations: stringRepresentations)), removeTailIfCountExceeds: 5)
|
||||
addSynchronizeSavedStickersOperation(transaction: transaction, operation: .add(id: resource.fileId, accessHash: resource.accessHash))
|
||||
addSynchronizeSavedStickersOperation(transaction: transaction, operation: .add(id: resource.fileId, accessHash: resource.accessHash, fileReference: .standalone(media: file)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ private let typeVideo: Int32 = 4
|
||||
private let typeAudio: Int32 = 5
|
||||
private let typeHasLinkedStickers: Int32 = 6
|
||||
|
||||
public enum StickerPackReference: PostboxCoding, Equatable {
|
||||
public enum StickerPackReference: PostboxCoding, Hashable, Equatable {
|
||||
case id(id: Int64, accessHash: Int64)
|
||||
case name(String)
|
||||
|
||||
@ -214,8 +214,37 @@ func durationForFileAttributes(_ attributes: [TelegramMediaFileAttribute]) -> In
|
||||
return nil
|
||||
}
|
||||
|
||||
public enum TelegramMediaFileReference: PostboxCoding, Equatable {
|
||||
case cloud(fileId: Int64, accessHash: Int64, fileReference: Data?)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("_v", orElse: 0) {
|
||||
case 0:
|
||||
self = .cloud(fileId: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0), fileReference: decoder.decodeBytesForKey("fr")?.makeData())
|
||||
default:
|
||||
self = .cloud(fileId: 0, accessHash: 0, fileReference: nil)
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .cloud(imageId, accessHash, fileReference):
|
||||
encoder.encodeInt32(0, forKey: "_v")
|
||||
encoder.encodeInt64(imageId, forKey: "i")
|
||||
encoder.encodeInt64(accessHash, forKey: "h")
|
||||
if let fileReference = fileReference {
|
||||
encoder.encodeBytes(MemoryBuffer(data: fileReference), forKey: "fr")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "fr")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class TelegramMediaFile: Media, Equatable {
|
||||
public let fileId: MediaId
|
||||
public let reference: TelegramMediaFileReference?
|
||||
public let resource: TelegramMediaResource
|
||||
public let previewRepresentations: [TelegramMediaImageRepresentation]
|
||||
public let mimeType: String
|
||||
@ -227,8 +256,9 @@ public final class TelegramMediaFile: Media, Equatable {
|
||||
return self.fileId
|
||||
}
|
||||
|
||||
public init(fileId: MediaId, resource: TelegramMediaResource, previewRepresentations: [TelegramMediaImageRepresentation], mimeType: String, size: Int?, attributes: [TelegramMediaFileAttribute]) {
|
||||
public init(fileId: MediaId, reference: TelegramMediaFileReference?, resource: TelegramMediaResource, previewRepresentations: [TelegramMediaImageRepresentation], mimeType: String, size: Int?, attributes: [TelegramMediaFileAttribute]) {
|
||||
self.fileId = fileId
|
||||
self.reference = reference
|
||||
self.resource = resource
|
||||
self.previewRepresentations = previewRepresentations
|
||||
self.mimeType = mimeType
|
||||
@ -238,6 +268,7 @@ public final class TelegramMediaFile: Media, Equatable {
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.fileId = MediaId(decoder.decodeBytesForKeyNoCopy("i")!)
|
||||
self.reference = decoder.decodeObjectForKey("rf", decoder: { TelegramMediaFileReference(decoder: $0) }) as? TelegramMediaFileReference
|
||||
self.resource = decoder.decodeObjectForKey("r") as! TelegramMediaResource
|
||||
self.previewRepresentations = decoder.decodeObjectArrayForKey("pr")
|
||||
self.mimeType = decoder.decodeStringForKey("mt", orElse: "")
|
||||
@ -253,6 +284,11 @@ public final class TelegramMediaFile: Media, Equatable {
|
||||
let buffer = WriteBuffer()
|
||||
self.fileId.encodeToBuffer(buffer)
|
||||
encoder.encodeBytes(buffer, forKey: "i")
|
||||
if let reference = self.reference {
|
||||
encoder.encodeObject(reference, forKey: "rf")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "rf")
|
||||
}
|
||||
encoder.encodeObject(self.resource, forKey: "r")
|
||||
encoder.encodeObjectArray(self.previewRepresentations, forKey: "pr")
|
||||
encoder.encodeString(self.mimeType, forKey: "mt")
|
||||
@ -349,6 +385,10 @@ public final class TelegramMediaFile: Media, Equatable {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.reference != other.reference {
|
||||
return false
|
||||
}
|
||||
|
||||
if !self.resource.isEqual(to: other.resource) {
|
||||
return false
|
||||
}
|
||||
@ -373,15 +413,15 @@ public final class TelegramMediaFile: Media, Equatable {
|
||||
}
|
||||
|
||||
public func withUpdatedSize(_ size: Int?) -> TelegramMediaFile {
|
||||
return TelegramMediaFile(fileId: self.fileId, resource: self.resource, previewRepresentations: self.previewRepresentations, mimeType: self.mimeType, size: size, attributes: self.attributes)
|
||||
return TelegramMediaFile(fileId: self.fileId, reference: self.reference, resource: self.resource, previewRepresentations: self.previewRepresentations, mimeType: self.mimeType, size: size, attributes: self.attributes)
|
||||
}
|
||||
|
||||
public func withUpdatedPreviewRepresentations(_ previewRepresentations: [TelegramMediaImageRepresentation]) -> TelegramMediaFile {
|
||||
return TelegramMediaFile(fileId: self.fileId, resource: self.resource, previewRepresentations: previewRepresentations, mimeType: self.mimeType, size: self.size, attributes: self.attributes)
|
||||
return TelegramMediaFile(fileId: self.fileId, reference: self.reference, resource: self.resource, previewRepresentations: previewRepresentations, mimeType: self.mimeType, size: self.size, attributes: self.attributes)
|
||||
}
|
||||
|
||||
public func withUpdatedAttributes(_ attributes: [TelegramMediaFileAttribute]) -> TelegramMediaFile {
|
||||
return TelegramMediaFile(fileId: self.fileId, resource: self.resource, previewRepresentations: self.previewRepresentations, mimeType: self.mimeType, size: self.size, attributes: attributes)
|
||||
return TelegramMediaFile(fileId: self.fileId, reference: self.reference, resource: self.resource, previewRepresentations: self.previewRepresentations, mimeType: self.mimeType, size: self.size, attributes: attributes)
|
||||
}
|
||||
}
|
||||
|
||||
@ -447,8 +487,8 @@ func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAtt
|
||||
|
||||
func telegramMediaFileFromApiDocument(_ document: Api.Document) -> TelegramMediaFile? {
|
||||
switch document {
|
||||
case let .document(id, accessHash, _, mimeType, size, thumb, dcId, _, attributes):
|
||||
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size)), previewRepresentations: telegramMediaImageRepresentationsFromApiSizes([thumb]), mimeType: mimeType, size: Int(size), attributes: telegramMediaFileAttributesFromApiAttributes(attributes))
|
||||
case let .document(id, accessHash, fileReference, _, mimeType, size, thumb, dcId, attributes):
|
||||
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), reference: .cloud(fileId: id, accessHash: accessHash, fileReference: fileReference.makeData()), resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: fileReference.makeData()), previewRepresentations: telegramMediaImageRepresentationsFromApiSizes([thumb]), mimeType: mimeType, size: Int(size), attributes: telegramMediaFileAttributesFromApiAttributes(attributes))
|
||||
case .documentEmpty:
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -6,31 +6,36 @@ import Foundation
|
||||
#endif
|
||||
|
||||
public enum TelegramMediaImageReference: PostboxCoding, Equatable {
|
||||
case cloud(imageId: Int64, accessHash: Int64)
|
||||
case cloud(imageId: Int64, accessHash: Int64, fileReference: Data?)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("_v", orElse: 0) {
|
||||
case 0:
|
||||
self = .cloud(imageId: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0))
|
||||
self = .cloud(imageId: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0), fileReference: decoder.decodeBytesForKey("fr")?.makeData())
|
||||
default:
|
||||
self = .cloud(imageId: 0, accessHash: 0)
|
||||
self = .cloud(imageId: 0, accessHash: 0, fileReference: nil)
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .cloud(imageId, accessHash):
|
||||
case let .cloud(imageId, accessHash, fileReference):
|
||||
encoder.encodeInt32(0, forKey: "_v")
|
||||
encoder.encodeInt64(imageId, forKey: "i")
|
||||
encoder.encodeInt64(accessHash, forKey: "h")
|
||||
if let fileReference = fileReference {
|
||||
encoder.encodeBytes(MemoryBuffer(data: fileReference), forKey: "fr")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "fr")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: TelegramMediaImageReference, rhs: TelegramMediaImageReference) -> Bool {
|
||||
switch lhs {
|
||||
case let .cloud(imageId, accessHash):
|
||||
if case .cloud(imageId, accessHash) = rhs {
|
||||
case let .cloud(imageId, accessHash, fileReference):
|
||||
if case .cloud(imageId, accessHash, fileReference) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -157,16 +162,16 @@ func telegramMediaImageRepresentationsFromApiSizes(_ sizes: [Api.PhotoSize]) ->
|
||||
var representations: [TelegramMediaImageRepresentation] = []
|
||||
for size in sizes {
|
||||
switch size {
|
||||
case let .photoCachedSize(_, location, w, h, bytes):
|
||||
if let resource = mediaResourceFromApiFileLocation(location, size: bytes.size) {
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource))
|
||||
}
|
||||
case let .photoSize(_, location, w, h, size):
|
||||
if let resource = mediaResourceFromApiFileLocation(location, size: Int(size)) {
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource))
|
||||
}
|
||||
case .photoSizeEmpty:
|
||||
break
|
||||
case let .photoCachedSize(_, location, w, h, bytes):
|
||||
if let resource = mediaResourceFromApiFileLocation(location, size: bytes.size) {
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource))
|
||||
}
|
||||
case let .photoSize(_, location, w, h, size):
|
||||
if let resource = mediaResourceFromApiFileLocation(location, size: Int(size)) {
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource))
|
||||
}
|
||||
case .photoSizeEmpty:
|
||||
break
|
||||
}
|
||||
}
|
||||
return representations
|
||||
@ -174,8 +179,8 @@ func telegramMediaImageRepresentationsFromApiSizes(_ sizes: [Api.PhotoSize]) ->
|
||||
|
||||
func telegramMediaImageFromApiPhoto(_ photo: Api.Photo) -> TelegramMediaImage? {
|
||||
switch photo {
|
||||
case let .photo(_, id, accessHash, _, sizes):
|
||||
return TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudImage, id: id), representations: telegramMediaImageRepresentationsFromApiSizes(sizes), reference: .cloud(imageId: id, accessHash: accessHash))
|
||||
case let .photo(_, id, accessHash, fileReference, _, sizes):
|
||||
return TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudImage, id: id), representations: telegramMediaImageRepresentationsFromApiSizes(sizes), reference: .cloud(imageId: id, accessHash: accessHash, fileReference: fileReference.makeData()))
|
||||
case .photoEmpty:
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -69,9 +69,6 @@ public func updatePeers(transaction: Transaction, peers: [Peer], update: (Peer?,
|
||||
}
|
||||
case Namespaces.Peer.CloudChannel:
|
||||
if let channel = updated as? TelegramChannel {
|
||||
if channel.title.hasPrefix("Qwe") {
|
||||
print("here")
|
||||
}
|
||||
switch channel.participationStatus {
|
||||
case .member:
|
||||
if channel.creationDate != 0 {
|
||||
|
||||
@ -11,14 +11,14 @@ extension Api.MessageMedia {
|
||||
case let .messageMediaPhoto(_, photo, _):
|
||||
if let photo = photo {
|
||||
switch photo {
|
||||
case let .photo(_, _, _, _, sizes):
|
||||
case let .photo(_, _, _, _, _, sizes):
|
||||
for size in sizes {
|
||||
switch size {
|
||||
case let .photoCachedSize(_, location, _, _, bytes):
|
||||
switch location {
|
||||
case let .fileLocation(dcId, volumeId, localId, secret):
|
||||
case let .fileLocation(dcId, volumeId, localId, secret, fileReference):
|
||||
let data = bytes.makeData()
|
||||
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: data.count)
|
||||
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: data.count, fileReference: fileReference.makeData())
|
||||
return [(resource, data)]
|
||||
default:
|
||||
break
|
||||
|
||||
@ -111,7 +111,7 @@ public func uploadSecureIdFile(context: SecureIdAccessContext, postbox: Postbox,
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
return multipartUpload(network: network, postbox: postbox, source: .data(encryptedData.data), encrypt: false, tag: nil, hintFileSize: nil, hintFileIsLarge: false)
|
||||
return multipartUpload(network: network, postbox: postbox, source: .data(encryptedData.data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false)
|
||||
|> mapError { _ -> UploadSecureIdFileError in
|
||||
return .generic
|
||||
}
|
||||
|
||||
@ -8,11 +8,11 @@ import SwiftSignalKit
|
||||
#endif
|
||||
|
||||
public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable {
|
||||
case none
|
||||
//case none
|
||||
case builtin
|
||||
case color(Int32)
|
||||
case image([TelegramMediaImageRepresentation])
|
||||
case custom(String)
|
||||
//case custom(String)
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("v", orElse: 0) {
|
||||
case 0:
|
||||
@ -21,20 +21,20 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable {
|
||||
self = .color(decoder.decodeInt32ForKey("c", orElse: 0))
|
||||
case 2:
|
||||
self = .image(decoder.decodeObjectArrayWithDecoderForKey("i"))
|
||||
case 3:
|
||||
/*case 3:
|
||||
self = .none
|
||||
case 4:
|
||||
self = .custom(decoder.decodeStringForKey("p", orElse: ""))
|
||||
self = .custom(decoder.decodeStringForKey("p", orElse: ""))*/
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .none
|
||||
self = .color(0xffffff)
|
||||
}
|
||||
}
|
||||
|
||||
public var hasWallpaper: Bool {
|
||||
switch self {
|
||||
case .none:
|
||||
return false
|
||||
//case .none:
|
||||
// return false
|
||||
case .color:
|
||||
return false
|
||||
default:
|
||||
@ -52,56 +52,56 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable {
|
||||
case let .image(representations):
|
||||
encoder.encodeInt32(2, forKey: "v")
|
||||
encoder.encodeObjectArray(representations, forKey: "i")
|
||||
case .none:
|
||||
/*case .none:
|
||||
encoder.encodeInt32(3, forKey: "v")
|
||||
case let .custom(path):
|
||||
encoder.encodeInt32(4, forKey: "v")
|
||||
encoder.encodeString(path, forKey: "p")
|
||||
encoder.encodeString(path, forKey: "p")*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func telegramWallpapers(account: Account) -> Signal<[TelegramWallpaper], NoError> {
|
||||
return account.postbox.transaction { transaction -> [TelegramWallpaper] in
|
||||
public func telegramWallpapers(postbox: Postbox, network: Network) -> Signal<[TelegramWallpaper], NoError> {
|
||||
return postbox.transaction { transaction -> [TelegramWallpaper] in
|
||||
let items = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudWallpapers)
|
||||
if items.count == 0 {
|
||||
return [.color(0x000000), .builtin]
|
||||
return [.builtin]
|
||||
} else {
|
||||
return items.map { $0.contents as! TelegramWallpaper }
|
||||
}
|
||||
} |> mapToSignal { list -> Signal<[TelegramWallpaper], NoError> in
|
||||
let remote = account.network.request(Api.functions.account.getWallPapers())
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<[TelegramWallpaper], NoError> in
|
||||
var items: [TelegramWallpaper] = []
|
||||
for item in result {
|
||||
switch item {
|
||||
case let .wallPaper(_, _, sizes, color):
|
||||
items.append(.image(telegramMediaImageRepresentationsFromApiSizes(sizes)))
|
||||
case let .wallPaperSolid(_, _, bgColor, color):
|
||||
items.append(.color(bgColor))
|
||||
}
|
||||
}
|
||||
items.removeFirst()
|
||||
items.insert(.color(0x000000), at: 0)
|
||||
items.insert(.builtin, at: 1)
|
||||
|
||||
if items == list {
|
||||
return .complete()
|
||||
} else {
|
||||
return account.postbox.transaction { transaction -> [TelegramWallpaper] in
|
||||
var entries: [OrderedItemListEntry] = []
|
||||
for item in items {
|
||||
var intValue = Int32(entries.count)
|
||||
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
|
||||
entries.append(OrderedItemListEntry(id: id, contents: item))
|
||||
}
|
||||
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudWallpapers, items: entries)
|
||||
|
||||
return items
|
||||
}
|
||||
let remote = network.request(Api.functions.account.getWallPapers())
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<[TelegramWallpaper], NoError> in
|
||||
var items: [TelegramWallpaper] = []
|
||||
for item in result {
|
||||
switch item {
|
||||
case let .wallPaper(_, _, sizes, _):
|
||||
items.append(.image(telegramMediaImageRepresentationsFromApiSizes(sizes)))
|
||||
case let .wallPaperSolid(_, _, bgColor, _):
|
||||
items.append(.color(bgColor))
|
||||
}
|
||||
}
|
||||
return .single(list) |> then(remote)
|
||||
items.removeFirst()
|
||||
items.insert(.builtin, at: 0)
|
||||
|
||||
if items == list {
|
||||
return .complete()
|
||||
} else {
|
||||
return postbox.transaction { transaction -> [TelegramWallpaper] in
|
||||
var entries: [OrderedItemListEntry] = []
|
||||
for item in items {
|
||||
var intValue = Int32(entries.count)
|
||||
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
|
||||
entries.append(OrderedItemListEntry(id: id, contents: item))
|
||||
}
|
||||
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudWallpapers, items: entries)
|
||||
|
||||
return items
|
||||
}
|
||||
}
|
||||
}
|
||||
return .single(list)
|
||||
|> then(remote)
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,3 +58,26 @@ public func actualizedWebpage(postbox: Postbox, network: Network, webpage: Teleg
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
func updatedRemoteWebpage(postbox: Postbox, network: Network, webPage: WebpageReference) -> Signal<TelegramMediaWebpage?, NoError> {
|
||||
if case let .webPage(id, url) = webPage.content {
|
||||
return network.request(Api.functions.messages.getWebPage(url: url, hash: 0))
|
||||
|> `catch` { _ -> Signal<Api.WebPage, NoError> in
|
||||
return .single(.webPageNotModified)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<TelegramMediaWebpage?, NoError> in
|
||||
if let updatedWebpage = telegramMediaWebpageFromApiWebpage(result, url: nil), case .Loaded = updatedWebpage.content, updatedWebpage.webpageId.id == id {
|
||||
return postbox.transaction { transaction -> TelegramMediaWebpage? in
|
||||
if transaction.getMedia(updatedWebpage.webpageId) != nil {
|
||||
transaction.updateMedia(updatedWebpage.webpageId, update: updatedWebpage)
|
||||
}
|
||||
return updatedWebpage
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user