no message

This commit is contained in:
Peter 2018-07-20 13:09:56 +03:00
parent c192cd39e4
commit cbe8943bc7
43 changed files with 2064 additions and 724 deletions

View File

@ -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 */,

View File

@ -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()

View File

@ -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)
}

View File

@ -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) }

View File

@ -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

View File

@ -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:

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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 {

View File

@ -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)

View File

@ -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)
}

View 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)
}
}

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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]
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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))

View File

@ -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

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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()
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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

View File

@ -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))

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -204,7 +204,7 @@ public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 82
return 83
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -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)

View File

@ -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))
}
}
}

View File

@ -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)))
}
}

View 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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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)
}
}