diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index fbb97ce3b6..b2a0765f95 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -243,6 +243,8 @@ D0561DE41E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */; }; D0561DEA1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */; }; D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */; }; + D058E0D11E8AD65C00A442DE /* StandaloneSendMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D058E0D01E8AD65C00A442DE /* StandaloneSendMessage.swift */; }; + D058E0D21E8AD65C00A442DE /* StandaloneSendMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D058E0D01E8AD65C00A442DE /* StandaloneSendMessage.swift */; }; D05A32E11E6F0982002760B4 /* UpdatedAccountPrivacySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05A32E01E6F0982002760B4 /* UpdatedAccountPrivacySettings.swift */; }; D05A32E21E6F0982002760B4 /* UpdatedAccountPrivacySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05A32E01E6F0982002760B4 /* UpdatedAccountPrivacySettings.swift */; }; D05A32E41E6F0B2E002760B4 /* RecentAccountSessions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05A32E31E6F0B2E002760B4 /* RecentAccountSessions.swift */; }; @@ -624,6 +626,7 @@ D05452061E7B5093006EEF19 /* LoadedStickerPack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadedStickerPack.swift; sourceTree = ""; }; D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatePeerInfo.swift; sourceTree = ""; }; D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelAdmins.swift; sourceTree = ""; }; + D058E0D01E8AD65C00A442DE /* StandaloneSendMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StandaloneSendMessage.swift; sourceTree = ""; }; D05A32E01E6F0982002760B4 /* UpdatedAccountPrivacySettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatedAccountPrivacySettings.swift; sourceTree = ""; }; D05A32E31E6F0B2E002760B4 /* RecentAccountSessions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentAccountSessions.swift; sourceTree = ""; }; D05A32E61E6F0B5C002760B4 /* RecentAccountSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentAccountSession.swift; sourceTree = ""; }; @@ -1013,6 +1016,7 @@ D0E23DD91E806F7700B9B6D2 /* ManagedSynchronizeMarkFeaturedStickerPacksAsSeenOperations.swift */, D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */, D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */, + D058E0D01E8AD65C00A442DE /* StandaloneSendMessage.swift */, ); name = State; sourceTree = ""; @@ -1642,6 +1646,7 @@ D0B843C71DA7FF30005F29E1 /* NBPhoneNumberDefines.m in Sources */, D049EAF51E44DF3300A2CD3A /* AccountState.swift in Sources */, D03B0D5D1D631A6900955575 /* MultipartFetch.swift in Sources */, + D058E0D11E8AD65C00A442DE /* StandaloneSendMessage.swift in Sources */, D0BC38751E40A7F70044D6FE /* RemovePeerChat.swift in Sources */, D0AB0B961D662F0B002C78E7 /* ManagedChatListHoles.swift in Sources */, D05A32E41E6F0B2E002760B4 /* RecentAccountSessions.swift in Sources */, @@ -1881,6 +1886,7 @@ D0F7B1E41E045C7B007EB8A5 /* InstantPage.swift in Sources */, D03E5E0D1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */, D0B418AD1D7E0597004562A4 /* Serialization.swift in Sources */, + D058E0D21E8AD65C00A442DE /* StandaloneSendMessage.swift in Sources */, D03C536F1DAD5CA9004C17B3 /* BotInfo.swift in Sources */, D0FA8BBA1E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */, D033FEB41E61F3C000644997 /* ReportPeer.swift in Sources */, diff --git a/TelegramCore/Account.swift b/TelegramCore/Account.swift index b84cc28570..a478f9a20c 100644 --- a/TelegramCore/Account.swift +++ b/TelegramCore/Account.swift @@ -145,7 +145,7 @@ public class UnauthorizedAccount { postbox.removeKeychainEntryForKey(key) }) - return initializedNetwork(apiId: self.apiId, datacenterId: Int(masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: self.basePath), testingEnvironment: self.testingEnvironment) + return initializedNetwork(apiId: self.apiId, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: self.basePath), testingEnvironment: self.testingEnvironment) |> map { network in let updated = UnauthorizedAccount(apiId: self.apiId, id: self.id, appGroupPath: self.appGroupPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network) updated.shouldBeServiceTaskMaster.set(self.shouldBeServiceTaskMaster.get()) @@ -183,7 +183,6 @@ private var declaredEncodables: Void = { declareEncodable(CachedChannelData.self, f: { CachedChannelData(decoder: $0) }) declareEncodable(TelegramUserPresence.self, f: { TelegramUserPresence(decoder: $0) }) declareEncodable(LocalFileMediaResource.self, f: { LocalFileMediaResource(decoder: $0) }) - declareEncodable(PhotoLibraryMediaResource.self, f: { PhotoLibraryMediaResource(decoder: $0) }) declareEncodable(StickerPackCollectionInfo.self, f: { StickerPackCollectionInfo(decoder: $0) }) declareEncodable(StickerPackItem.self, f: { StickerPackItem(decoder: $0) }) declareEncodable(LocalFileReferenceMediaResource.self, f: { LocalFileReferenceMediaResource(decoder: $0) }) @@ -231,7 +230,7 @@ private func accountRecordIdPathName(_ id: AccountRecordId) -> String { return "account-\(UInt64(bitPattern: id.int64))" } -public func accountWithId(apiId: Int32, id: AccountRecordId, appGroupPath: String, testingEnvironment: Bool, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal, NoError> { +public func accountWithId(apiId: Int32, id: AccountRecordId, supplementary: Bool, appGroupPath: String, testingEnvironment: Bool, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal, NoError> { return Signal<(String, Postbox, Coding?), NoError> { subscriber in let _ = declaredEncodables @@ -265,12 +264,12 @@ public func accountWithId(apiId: Int32, id: AccountRecordId, appGroupPath: Strin if let accountState = accountState { switch accountState { case let unauthorizedState as UnauthorizedAccountState: - return initializedNetwork(apiId: apiId, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) + return initializedNetwork(apiId: apiId, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) |> map { network -> Either in .left(value: UnauthorizedAccount(apiId: apiId, id: id, appGroupPath: appGroupPath, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection)) } case let authorizedState as AuthorizedAccountState: - return initializedNetwork(apiId: apiId, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) + return initializedNetwork(apiId: apiId, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) |> map { network -> Either in return .right(value: Account(id: id, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, peerId: authorizedState.peerId, auxiliaryMethods: auxiliaryMethods)) } @@ -279,7 +278,7 @@ public func accountWithId(apiId: Int32, id: AccountRecordId, appGroupPath: Strin } } - return initializedNetwork(apiId: apiId, datacenterId: 2, keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) + return initializedNetwork(apiId: apiId, supplementary: supplementary, datacenterId: 2, keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) |> map { network -> Either in return .left(value: UnauthorizedAccount(apiId: apiId, id: id, appGroupPath: appGroupPath, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection)) } diff --git a/TelegramCore/AccountManager.swift b/TelegramCore/AccountManager.swift index 31d74d9709..11cfdc743b 100644 --- a/TelegramCore/AccountManager.swift +++ b/TelegramCore/AccountManager.swift @@ -14,7 +14,7 @@ private enum AccountKind { case unauthorized } -public func currentAccount(apiId: Int32, manager: AccountManager, appGroupPath: String, testingEnvironment: Bool, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal?, NoError> { +public func currentAccount(apiId: Int32, supplementary: Bool, manager: AccountManager, appGroupPath: String, testingEnvironment: Bool, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal?, NoError> { return manager.allocatedCurrentAccountId() |> distinctUntilChanged(isEqual: { lhs, rhs in return lhs == rhs @@ -23,7 +23,7 @@ public func currentAccount(apiId: Int32, manager: AccountManager, appGroupPath: if let id = id { let reload = ValuePromise(true, ignoreRepeated: false) return reload.get() |> mapToSignal { _ -> Signal?, NoError> in - return accountWithId(apiId: apiId, id: id, appGroupPath: appGroupPath, testingEnvironment: testingEnvironment, auxiliaryMethods: auxiliaryMethods) + return accountWithId(apiId: apiId, id: id, supplementary: supplementary, appGroupPath: appGroupPath, testingEnvironment: testingEnvironment, auxiliaryMethods: auxiliaryMethods) |> mapToSignal { account -> Signal?, NoError> in let postbox: Postbox let initialKind: AccountKind @@ -151,7 +151,7 @@ public func managedCleanupAccounts(apiId: Int32, accountManager: AccountManager, private func cleanupAccount(apiId: Int32, accountManager: AccountManager, id: AccountRecordId, appGroupPath: String, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal { - return accountWithId(apiId: apiId, id: id, appGroupPath: appGroupPath, testingEnvironment: false, auxiliaryMethods: auxiliaryMethods) + return accountWithId(apiId: apiId, id: id, supplementary: true, appGroupPath: appGroupPath, testingEnvironment: false, auxiliaryMethods: auxiliaryMethods) |> mapToSignal { account -> Signal in switch account { case .left: diff --git a/TelegramCore/CloudFileMediaResource.swift b/TelegramCore/CloudFileMediaResource.swift index 99aa10203e..20c72d3d23 100644 --- a/TelegramCore/CloudFileMediaResource.swift +++ b/TelegramCore/CloudFileMediaResource.swift @@ -228,54 +228,6 @@ public class LocalFileMediaResource: TelegramMediaResource { } } -public struct PhotoLibraryMediaResourceId: MediaResourceId { - public let localIdentifier: String - - public var uniqueId: String { - return "ph-\(self.localIdentifier.replacingOccurrences(of: "/", with: "_"))" - } - - public var hashValue: Int { - return self.localIdentifier.hashValue - } - - public func isEqual(to: MediaResourceId) -> Bool { - if let to = to as? PhotoLibraryMediaResourceId { - return self.localIdentifier == to.localIdentifier - } else { - return false - } - } -} - -public class PhotoLibraryMediaResource: TelegramMediaResource { - let localIdentifier: String - - public init(localIdentifier: String) { - self.localIdentifier = localIdentifier - } - - public required init(decoder: Decoder) { - self.localIdentifier = decoder.decodeStringForKey("i") - } - - public func encode(_ encoder: Encoder) { - encoder.encodeString(self.localIdentifier, forKey: "i") - } - - public var id: MediaResourceId { - return PhotoLibraryMediaResourceId(localIdentifier: self.localIdentifier) - } - - public func isEqual(to: TelegramMediaResource) -> Bool { - if let to = to as? PhotoLibraryMediaResource { - return self.localIdentifier == to.localIdentifier - } else { - return false - } - } -} - public struct LocalFileReferenceMediaResourceId: MediaResourceId { public let randomId: Int64 diff --git a/TelegramCore/EnqueueMessage.swift b/TelegramCore/EnqueueMessage.swift index 1efd274d4b..b7eeadf3c3 100644 --- a/TelegramCore/EnqueueMessage.swift +++ b/TelegramCore/EnqueueMessage.swift @@ -190,6 +190,12 @@ func enqueueMessages(modifier: Modifier, account: Account, peerId: PeerId, messa mediaList.append(media) } + if let file = media as? TelegramMediaFile, file.isVoice { + if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup { + attributes.append(ConsumableContentMessageAttribute(consumed: false)) + } + } + var entitiesAttribute: TextEntitiesMessageAttribute? for attribute in attributes { if let attribute = attribute as? TextEntitiesMessageAttribute { diff --git a/TelegramCore/Fetch.swift b/TelegramCore/Fetch.swift index f7ea6628e5..318242301c 100644 --- a/TelegramCore/Fetch.swift +++ b/TelegramCore/Fetch.swift @@ -13,50 +13,6 @@ private func fetchCloudMediaLocation(account: Account, resource: TelegramCloudMe return multipartFetch(account: account, resource: resource, size: size, range: range) } -#if os(iOS) -private func fetchPhotoLibraryResource(localIdentifier: String) -> Signal { - return Signal { subscriber in - let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil) - var requestId: PHImageRequestID? - if fetchResult.count != 0 { - let asset = fetchResult.object(at: 0) - let option = PHImageRequestOptions() - option.deliveryMode = .highQualityFormat - let size = CGSize(width: 1280.0, height: 1280.0) - requestId = PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .aspectFit, options: option, resultHandler: { (image, info) -> Void in - Queue.concurrentDefaultQueue().async { - requestId = nil - if let image = image { - let scale = min(1.0, min(size.width / max(1.0, image.size.width), size.height / max(1.0, image.size.height))) - let scaledSize = CGSize(width: floor(image.size.width * scale), height: floor(image.size.height * scale)) - - UIGraphicsBeginImageContextWithOptions(scaledSize, true, image.scale) - image.draw(in: CGRect(origin: CGPoint(), size: scaledSize)) - let scaledImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - if let scaledImage = scaledImage, let data = UIImageJPEGRepresentation(scaledImage, 0.6) { - subscriber.putNext(.dataPart(data: data, range: 0 ..< data.count, complete: true)) - subscriber.putCompletion() - } else { - subscriber.putCompletion() - } - } else { - subscriber.putCompletion() - } - } - }) - } - - return ActionDisposable { - if let requestId = requestId { - PHImageManager.default().cancelImageRequest(requestId) - } - } - } -} -#endif - private func fetchLocalFileResource(path: String, move: Bool) -> Signal { return Signal { subscriber in if move { @@ -81,12 +37,6 @@ func fetchResource(account: Account, resource: MediaResource, range: Range) return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchSecretFileResource(account: account, resource: secretFileResource, range: range)) } else if let cloudResource = resource as? TelegramCloudMediaResource { return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, size: resource.size, range: range)) - } else if let photoLibraryResource = resource as? PhotoLibraryMediaResource { - #if os(iOS) - return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchPhotoLibraryResource(localIdentifier: photoLibraryResource.localIdentifier)) - #else - return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) - #endif } else if let localFileResource = resource as? LocalFileReferenceMediaResource { if false { //return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchLocalFileResource(path: localFileResource.localFilePath) |> delay(10.0, queue: Queue.concurrentDefaultQueue())) diff --git a/TelegramCore/MarkMessageContentAsConsumedInteractively.swift b/TelegramCore/MarkMessageContentAsConsumedInteractively.swift index 8044e46d2a..374451d714 100644 --- a/TelegramCore/MarkMessageContentAsConsumedInteractively.swift +++ b/TelegramCore/MarkMessageContentAsConsumedInteractively.swift @@ -7,7 +7,7 @@ import Foundation import SwiftSignalKit #endif -public func markMessageContentAsConsumedInteractively(postbox: Postbox, network: Network, messageId: MessageId) -> Signal { +public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageId: MessageId) -> Signal { return postbox.modify { modifier -> Void in if let message = modifier.getMessage(messageId), message.flags.contains(.Incoming) { var updateMessage = false diff --git a/TelegramCore/Network.swift b/TelegramCore/Network.swift index f78cce631c..9192ffff72 100644 --- a/TelegramCore/Network.swift +++ b/TelegramCore/Network.swift @@ -94,7 +94,7 @@ private var registeredLoggingFunctions: Void = { registerLoggingFunctions() }() -func initializedNetwork(apiId: Int32, datacenterId: Int, keychain: Keychain, networkUsageInfoPath: String?, testingEnvironment: Bool) -> Signal { +func initializedNetwork(apiId: Int32, supplementary: Bool, datacenterId: Int, keychain: Keychain, networkUsageInfoPath: String?, testingEnvironment: Bool) -> Signal { return Signal { subscriber in Queue.concurrentDefaultQueue().async { let _ = registeredLoggingFunctions @@ -105,6 +105,7 @@ func initializedNetwork(apiId: Int32, datacenterId: Int, keychain: Keychain, net apiEnvironment.apiId = apiId apiEnvironment.layer = NSNumber(value: Int(serialization.currentLayer())) + apiEnvironment.disableUpdates = supplementary let context = MTContext(serialization: serialization, apiEnvironment: apiEnvironment)! diff --git a/TelegramCore/PendingMessageManager.swift b/TelegramCore/PendingMessageManager.swift index c17e5774a4..9c57f0431e 100644 --- a/TelegramCore/PendingMessageManager.swift +++ b/TelegramCore/PendingMessageManager.swift @@ -207,8 +207,8 @@ public final class PendingMessageManager { let contentToUpload = messageContentToUpload(network: strongSelf.network, postbox: strongSelf.postbox, transformOutgoingMessageMedia: strongSelf.transformOutgoingMessageMedia, message: message) switch contentToUpload { - case let .ready(message, content): - strongSelf.beginSendingMessage(messageContext: messageContext, message: message, content: content) + case let .ready(content): + strongSelf.beginSendingMessage(messageContext: messageContext, messageId: message.id, content: content) case let .upload(uploadSignal): if strongSelf.canBeginUploadingMessage(id: message.id) { strongSelf.beginUploadingMessage(messageContext: messageContext, id: message.id, uploadSignal: uploadSignal) @@ -216,75 +216,27 @@ public final class PendingMessageManager { messageContext.state = .waitingForUploadToStart(uploadSignal) } } - - /*let uploadedContent = uploadedMessageContent(network: strongSelf.network, postbox: strongSelf.postbox, transformOutgoingMessageMedia: strongSelf.transformOutgoingMessageMedia, message: message) - - let sendMessage = uploadedContent - |> mapToSignal { contentResult -> Signal in - if let strongSelf = self { - switch contentResult { - case let .progress(progress): - return .single(.progress(progress)) - case let .content(message, content): - return strongSelf.sendMessageContent(network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, message: message, content: content) - |> map { next -> PendingMessageResult in - return .progress(1.0) - } - } - } else { - return .complete() - } - } - - messageContext.disposable.set((sendMessage |> deliverOn(strongSelf.queue) |> afterDisposed { - if let strongSelf = self { - assert(strongSelf.queue.isCurrent()) - if let current = strongSelf.messageContexts[message.id] { - for subscriber in current.statusSubscribers.copyItems() { - subscriber(nil) - } - if current.statusSubscribers.isEmpty { - strongSelf.messageContexts.removeValue(forKey: message.id) - } - } - } - }).start(next: { next in - if let strongSelf = self { - assert(strongSelf.queue.isCurrent()) - - switch next { - case let .progress(progress): - if let current = strongSelf.messageContexts[message.id] { - let status = PendingMessageStatus(progress: progress) - current.status = status - for subscriber in current.statusSubscribers.copyItems() { - subscriber(status) - } - } - } - } - }))*/ } } })) } - private func beginSendingMessage(messageContext: PendingMessageContext, message: Message, content: PendingMessageUploadedContent) { + private func beginSendingMessage(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContent) { messageContext.state = .sending - let sendMessage: Signal = self.sendMessageContent(network: self.network, postbox: self.postbox, stateManager: self.stateManager, message: message, content: content) + let sendMessage: Signal = 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 { assert(strongSelf.queue.isCurrent()) - if let current = strongSelf.messageContexts[message.id] { + if let current = strongSelf.messageContexts[messageId] { current.status = .none for subscriber in current.statusSubscribers.copyItems() { subscriber(nil) } if current.statusSubscribers.isEmpty { - strongSelf.messageContexts.removeValue(forKey: message.id) + strongSelf.messageContexts.removeValue(forKey: messageId) } } } @@ -294,7 +246,7 @@ public final class PendingMessageManager { switch next { case let .progress(progress): - if let current = strongSelf.messageContexts[message.id] { + if let current = strongSelf.messageContexts[messageId] { let status = PendingMessageStatus(progress: progress) current.status = status for subscriber in current.statusSubscribers.copyItems() { @@ -322,9 +274,9 @@ public final class PendingMessageManager { subscriber(status) } } - case let .content(message, content): + case let .content(content): if let current = strongSelf.messageContexts[id] { - strongSelf.beginSendingMessage(messageContext: current, message: message, content: content) + strongSelf.beginSendingMessage(messageContext: current, messageId: id, content: content) strongSelf.updateWaitingUploads(peerId: id.peerId) } } @@ -354,11 +306,11 @@ public final class PendingMessageManager { subscriber(status) } } - case let .content(message, content): - if let current = strongSelf.messageContexts[message.id] { + case let .content(content): + if let current = strongSelf.messageContexts[contextId] { current.state = .sending - current.disposable.set((strongSelf.sendMessageContent(network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, message: message, content: content) + current.disposable.set((strongSelf.sendMessageContent(network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, messageId: contextId, content: content) |> map { next -> PendingMessageResult in return .progress(1.0) } |> deliverOn(strongSelf.queue)).start(next: { next in @@ -367,7 +319,7 @@ public final class PendingMessageManager { switch next { case let .progress(progress): - if let current = strongSelf.messageContexts[message.id] { + if let current = strongSelf.messageContexts[contextId] { let status = PendingMessageStatus(progress: progress) current.status = status for subscriber in current.statusSubscribers.copyItems() { @@ -387,9 +339,13 @@ public final class PendingMessageManager { } } - private func sendMessageContent(network: Network, postbox: Postbox, stateManager: AccountStateManager, message: Message, content: PendingMessageUploadedContent) -> Signal { + private func sendMessageContent(network: Network, postbox: Postbox, stateManager: AccountStateManager, messageId: MessageId, content: PendingMessageUploadedContent) -> Signal { return postbox.modify { [weak self] modifier -> Signal in - if message.id.peerId.namespace == Namespaces.Peer.SecretChat { + guard let message = modifier.getMessage(messageId) else { + return .complete() + } + + if messageId.peerId.namespace == Namespaces.Peer.SecretChat { var secretFile: SecretChatOutgoingFile? switch content { case let .secretMedia(file, size, key): @@ -401,7 +357,7 @@ public final class PendingMessageManager { } var layer: SecretChatLayer? - let state = modifier.getPeerChatState(message.id.peerId) as? SecretChatState + let state = modifier.getPeerChatState(messageId.peerId) as? SecretChatState if let state = state { switch state.embeddedState { case .terminated, .handshake: @@ -440,11 +396,11 @@ public final class PendingMessageManager { } if !sentAsAction { - let updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: message.id.peerId, operation: .sendMessage(layer: layer, id: message.id, file: secretFile), state: state) + let updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: messageId.peerId, operation: .sendMessage(layer: layer, id: messageId, file: secretFile), state: state) if updatedState != state { - modifier.setPeerChatState(message.id.peerId, state: updatedState) + modifier.setPeerChatState(messageId.peerId, state: updatedState) } - modifier.updateMessage(message.id, update: { currentMessage in + modifier.updateMessage(messageId, update: { currentMessage in var flags = StoreMessageFlags(message.flags) if !flags.contains(.Failed) { flags.insert(.Sending) @@ -457,7 +413,7 @@ public final class PendingMessageManager { }) } } else { - modifier.updateMessage(message.id, update: { currentMessage in + modifier.updateMessage(messageId, update: { currentMessage in var storeForwardInfo: StoreMessageForwardInfo? if let forwardInfo = currentMessage.forwardInfo { storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date) @@ -466,7 +422,7 @@ public final class PendingMessageManager { }) } return .complete() - } else if let peer = modifier.getPeer(message.id.peerId), let inputPeer = apiInputPeer(peer) { + } else if let peer = modifier.getPeer(messageId.peerId), let inputPeer = apiInputPeer(peer) { var uniqueId: Int64 = 0 var forwardSourceInfoAttribute: ForwardSourceInfoAttribute? var messageEntities: [Api.MessageEntity]? @@ -499,7 +455,7 @@ public final class PendingMessageManager { flags |= Int32(1 << 3) } - let dependencyTag = PendingMessageRequestDependencyTag(messageId: message.id) + let dependencyTag = PendingMessageRequestDependencyTag(messageId: messageId) let sendMessageRequest: Signal switch content { diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index 5ef7b0d757..dcf772c3e9 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -17,115 +17,76 @@ enum PendingMessageUploadedContent { enum PendingMessageUploadedContentResult { case progress(Float) - case content(Message, PendingMessageUploadedContent) + case content(PendingMessageUploadedContent) } enum PendingMessageUploadContent { - case ready(Message, PendingMessageUploadedContent) + case ready(PendingMessageUploadedContent) case upload(Signal) } func messageContentToUpload(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, message: Message) -> PendingMessageUploadContent { - var outgoingChatContextResultAttribute: OutgoingChatContextResultMessageAttribute? - for attribute in message.attributes { + return messageContentToUpload(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, peerId: message.id.peerId, messageId: message.id, attributes: message.attributes, text: message.text, media: message.media) +} + +func messageContentToUpload(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media]) -> PendingMessageUploadContent { + var contextResult: OutgoingChatContextResultMessageAttribute? + for attribute in attributes { if let attribute = attribute as? OutgoingChatContextResultMessageAttribute { - outgoingChatContextResultAttribute = attribute + contextResult = attribute } } - if let _ = message.forwardInfo { - var forwardSourceInfo: ForwardSourceInfoAttribute? - for attribute in message.attributes { - if let attribute = attribute as? ForwardSourceInfoAttribute { - forwardSourceInfo = attribute - } - } - if let forwardSourceInfo = forwardSourceInfo { - return .ready(message, .forward(forwardSourceInfo)) - } else { - assertionFailure() - return .ready(message, .text(message.text)) - } - } else if let outgoingChatContextResultAttribute = outgoingChatContextResultAttribute { - return .ready(message, .chatContextResult(outgoingChatContextResultAttribute)) - } else if let media = message.media.first { - if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) { - return .upload(uploadedMediaImageContent(network: network, postbox: postbox, image: image, message: message)) - } else if let file = media as? TelegramMediaFile { - if let resource = file.resource as? CloudDocumentMediaResource { - return .ready(message, .media(Api.InputMedia.inputMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: message.text))) - } else { - return .upload(uploadedMediaFileContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, file: file, message: message)) - } - } else if let contact = media as? TelegramMediaContact { - let input = Api.InputMedia.inputMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName) - return .ready(message, .media(input)) - } else { - return .ready(message, .text(message.text)) - } - } else { - return .ready(message, .text(message.text)) - } -} - -func uploadedMessageContent(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, message: Message) -> Signal { - var outgoingChatContextResultAttribute: OutgoingChatContextResultMessageAttribute? - for attribute in message.attributes { - if let attribute = attribute as? OutgoingChatContextResultMessageAttribute { - outgoingChatContextResultAttribute = attribute + var forwardInfo: ForwardSourceInfoAttribute? + for attribute in attributes { + if let attribute = attribute as? ForwardSourceInfoAttribute { + forwardInfo = attribute } } - if let _ = message.forwardInfo { - var forwardSourceInfo: ForwardSourceInfoAttribute? - for attribute in message.attributes { - if let attribute = attribute as? ForwardSourceInfoAttribute { - forwardSourceInfo = attribute - } - } - if let forwardSourceInfo = forwardSourceInfo { - return .single(.content(message, .forward(forwardSourceInfo))) - } else { - assertionFailure() - return .never() - } - } else if let outgoingChatContextResultAttribute = outgoingChatContextResultAttribute { - return .single(.content(message, .chatContextResult(outgoingChatContextResultAttribute))) - } else if let media = message.media.first { + if let forwardInfo = forwardInfo { + return .ready(.forward(forwardInfo)) + } + + if let forwardInfo = forwardInfo { + return .ready(.forward(forwardInfo)) + } else if let contextResult = contextResult { + return .ready(.chatContextResult(contextResult)) + } else if let media = media.first { if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) { - return uploadedMediaImageContent(network: network, postbox: postbox, image: image, message: message) + return .upload(uploadedMediaImageContent(network: network, postbox: postbox, peerId: peerId, image: image, text: text)) } else if let file = media as? TelegramMediaFile { if let resource = file.resource as? CloudDocumentMediaResource { - return .single(.content(message, .media(Api.InputMedia.inputMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: message.text)))) + return .ready(.media(Api.InputMedia.inputMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: text))) } else { - return uploadedMediaFileContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, file: file, message: message) + return .upload(uploadedMediaFileContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, 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) - return .single(.content(message, .media(input))) + return .ready(.media(input)) } else { - return .single(.content(message, .text(message.text))) + return .ready(.text(text)) } } else { - return .single(.content(message, .text(message.text))) + return .ready(.text(text)) } } -private func uploadedMediaImageContent(network: Network, postbox: Postbox, image: TelegramMediaImage, message: Message) -> Signal { +private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerId: PeerId, image: TelegramMediaImage, text: String) -> Signal { if let largestRepresentation = largestImageRepresentation(image.representations) { - return multipartUpload(network: network, postbox: postbox, resource: largestRepresentation.resource, encrypt: message.id.peerId.namespace == Namespaces.Peer.SecretChat) + return multipartUpload(network: network, postbox: postbox, resource: largestRepresentation.resource, encrypt: peerId.namespace == Namespaces.Peer.SecretChat) |> map { next -> PendingMessageUploadedContentResult in switch next { case let .progress(progress): return .progress(progress) case let .inputFile(file): - return .content(message, .media(Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: file, caption: message.text, stickers: nil))) + return .content(.media(Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: file, caption: text, stickers: nil))) case let .inputSecretFile(file, size, key): - return .content(message, .secretMedia(file, size, key)) + return .content(.secretMedia(file, size, key)) } } } else { - return .single(.content(message, .text(message.text))) + return .single(.content(.text(text))) } } @@ -201,14 +162,14 @@ private func uploadedThumbnail(network: Network, postbox: Postbox, image: Telegr } } -private func uploadedMediaFileContent(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, file: TelegramMediaFile, message: Message) -> Signal { +private func uploadedMediaFileContent(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, peerId: PeerId, messageId: MessageId?, text: String, attributes: [MessageAttribute], file: TelegramMediaFile) -> Signal { var hintSize: Int? if let size = file.size { hintSize = size } else if let resource = file.resource as? LocalFileReferenceMediaResource, let size = resource.size { hintSize = Int(size) } - let upload = multipartUpload(network: network, postbox: postbox, resource: file.resource, encrypt: message.id.peerId.namespace == Namespaces.Peer.SecretChat, hintFileSize: hintSize) + let upload = multipartUpload(network: network, postbox: postbox, resource: file.resource, encrypt: peerId.namespace == Namespaces.Peer.SecretChat, hintFileSize: hintSize) /*|> map { next -> UploadedMediaFileContent in switch next { case let .progress(progress): @@ -220,7 +181,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, transf } }*/ var alreadyTransformed = false - for attribute in message.attributes { + for attribute in attributes { if let attribute = attribute as? OutgoingMessageInfoAttribute { if attribute.flags.contains(.transformedMedia) { alreadyTransformed = true @@ -230,14 +191,14 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, transf } let transform: Signal - if let transformOutgoingMessageMedia = transformOutgoingMessageMedia, !alreadyTransformed { + if let transformOutgoingMessageMedia = transformOutgoingMessageMedia, let messageId = messageId, !alreadyTransformed { transform = .single(.pending) |> then(transformOutgoingMessageMedia(postbox, network, file, false) |> mapToSignal { media -> Signal in return postbox.modify { modifier -> UploadedMediaTransform in if let media = media { if let id = media.id { modifier.updateMedia(id, update: media) - modifier.updateMessage(message.id, update: { currentMessage in + modifier.updateMessage(messageId, update: { currentMessage in var storeForwardInfo: StoreMessageForwardInfo? if let forwardInfo = currentMessage.forwardInfo { storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date) @@ -288,17 +249,17 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, transf if case let .done(thumbnail) = media { let inputMedia: Api.InputMedia if let thumbnail = thumbnail { - inputMedia = Api.InputMedia.inputMediaUploadedThumbDocument(flags: 0, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFile(file), caption: message.text, stickers: nil) + inputMedia = Api.InputMedia.inputMediaUploadedThumbDocument(flags: 0, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFile(file), caption: text, stickers: nil) } else { - inputMedia = Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFile(file), caption: message.text, stickers: nil) + inputMedia = Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFile(file), caption: text, stickers: nil) } - return .single(.content(message, .media(inputMedia))) + return .single(.content(.media(inputMedia))) } else { return .complete() } case let .inputSecretFile(file, size, key): if case .done = media { - return .single(.content(message, .secretMedia(file, size, key))) + return .single(.content(.secretMedia(file, size, key))) } else { return .complete() } diff --git a/TelegramCore/ResolvePeerByName.swift b/TelegramCore/ResolvePeerByName.swift index 3b48f2e923..4c51326989 100644 --- a/TelegramCore/ResolvePeerByName.swift +++ b/TelegramCore/ResolvePeerByName.swift @@ -12,10 +12,14 @@ final class CachedResolvedByNamePeer: Coding { let timestamp: Int32 static func key(name: String) -> ValueBoxKey { - let nameData = name.data(using: .utf8)! - let key = ValueBoxKey(length: nameData.count) - nameData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - memcpy(key.memory, bytes, nameData.count) + let key: ValueBoxKey + if let nameData = name.data(using: .utf8) { + key = ValueBoxKey(length: nameData.count) + nameData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + memcpy(key.memory, bytes, nameData.count) + } + } else { + key = ValueBoxKey(length: 0) } return key } diff --git a/TelegramCore/StandaloneSendMessage.swift b/TelegramCore/StandaloneSendMessage.swift new file mode 100644 index 0000000000..bdea43ce4b --- /dev/null +++ b/TelegramCore/StandaloneSendMessage.swift @@ -0,0 +1,117 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac + import MtProtoKitMac +#else + import Postbox + import SwiftSignalKit + import MtProtoKitDynamic +#endif + +public enum StandaloneMedia { + case image(Data) + case file(Data) +} + +public func standaloneSendMessage(account: Account, peerId: PeerId, text: String, attributes: [MessageAttribute], replyToMessageId: MessageId?) -> Signal { + let contentToUpload = messageContentToUpload(network: account.network, postbox: account.postbox, transformOutgoingMessageMedia: nil, peerId: peerId, messageId: nil, attributes: attributes, text: text, media: []) + + switch contentToUpload { + case let .ready(content): + return sendMessageContent(account: account, peerId: peerId, attributes: attributes, content: content) + case let .upload(uploadSignal): + return .complete() + /*if strongSelf.canBeginUploadingMessage(id: message.id) { + strongSelf.beginUploadingMessage(messageContext: messageContext, id: message.id, uploadSignal: uploadSignal) + } else { + messageContext.state = .waitingForUploadToStart(uploadSignal) + }*/ + } +} + +private func sendMessageContent(account: Account, peerId: PeerId, attributes: [MessageAttribute], content: PendingMessageUploadedContent) -> Signal { + return account.postbox.modify { modifier -> Signal in + if peerId.namespace == Namespaces.Peer.SecretChat { + return .complete() + } else if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) { + var uniqueId: Int64 = 0 + var forwardSourceInfoAttribute: ForwardSourceInfoAttribute? + var messageEntities: [Api.MessageEntity]? + var replyMessageId: Int32? + + var flags: Int32 = 0 + + flags |= (1 << 7) + + for attribute in attributes { + if let replyAttribute = attribute as? ReplyMessageAttribute { + replyMessageId = replyAttribute.messageId.id + } else if let outgoingInfo = attribute as? OutgoingMessageInfoAttribute { + uniqueId = outgoingInfo.uniqueId + } else if let attribute = attribute as? ForwardSourceInfoAttribute { + forwardSourceInfoAttribute = attribute + } else if let attribute = attribute as? TextEntitiesMessageAttribute { + messageEntities = apiTextAttributeEntities(attribute, associatedPeers: SimpleDictionary()) + } else if let attribute = attribute as? OutgoingContentInfoMessageAttribute { + if attribute.flags.contains(.disableLinkPreviews) { + flags |= Int32(1 << 1) + } + } + } + + if let _ = replyMessageId { + flags |= Int32(1 << 0) + } + if let _ = messageEntities { + flags |= Int32(1 << 3) + } + + let sendMessageRequest: Signal + switch content { + case let .text(text): + sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities)) + |> mapError { _ -> NoError in + return NoError() + } + case let .media(inputMedia): + sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, randomId: uniqueId, replyMarkup: nil)) + |> mapError { _ -> NoError in + return NoError() + } + case let .forward(sourceInfo): + if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = modifier.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) { + sendMessageRequest = account.network.request(Api.functions.messages.forwardMessages(flags: 0, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer)) + |> mapError { _ -> NoError in + return NoError() + } + } else { + sendMessageRequest = .fail(NoError()) + } + case let .chatContextResult(chatContextResult): + sendMessageRequest = account.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()) + } + + return sendMessageRequest + |> mapToSignal { result -> Signal in + return .complete() + /*if let strongSelf = self { + return strongSelf.applySentMessage(postbox: postbox, stateManager: stateManager, message: message, result: result) + } else { + return .never() + }*/ + } + |> `catch` { _ -> Signal in + return .complete() + } + } else { + return .complete() + } + } |> switchToLatest +}