diff --git a/TelegramCore/AccountViewTracker.swift b/TelegramCore/AccountViewTracker.swift index 39bcb8725c..88cfddf6d6 100644 --- a/TelegramCore/AccountViewTracker.swift +++ b/TelegramCore/AccountViewTracker.swift @@ -11,7 +11,7 @@ import Foundation private func pendingWebpages(entries: [MessageHistoryEntry]) -> Set { var messageIds = Set() - for case let .MessageEntry(message, _, _) in entries { + for case let .MessageEntry(message, _, _, _) in entries { for media in message.media { if let media = media as? TelegramMediaWebpage { if case .Pending = media.content { @@ -422,27 +422,27 @@ public final class AccountViewTracker { } } - public func aroundUnreadMessageHistoryViewForPeerId(_ peerId: PeerId, count: Int, tagMask: MessageTags? = nil, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundUnreadMessageHistoryViewForPeerId(_ peerId: PeerId, count: Int, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { - let signal = account.postbox.aroundUnreadMessageHistoryViewForPeerId(peerId, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, additionalData: additionalData) + let signal = account.postbox.aroundUnreadMessageHistoryViewForPeerId(peerId, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, orderStatistics: orderStatistics, additionalData: additionalData) return wrappedMessageHistorySignal(peerId: peerId, signal: signal) } else { return .never() } } - public func aroundIdMessageHistoryViewForPeerId(_ peerId: PeerId, count: Int, messageId: MessageId, tagMask: MessageTags? = nil, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundIdMessageHistoryViewForPeerId(_ peerId: PeerId, count: Int, messageId: MessageId, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { - let signal = account.postbox.aroundIdMessageHistoryViewForPeerId(peerId, count: count, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, additionalData: additionalData) + let signal = account.postbox.aroundIdMessageHistoryViewForPeerId(peerId, count: count, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, orderStatistics: orderStatistics, additionalData: additionalData) return wrappedMessageHistorySignal(peerId: peerId, signal: signal) } else { return .never() } } - public func aroundMessageHistoryViewForPeerId(_ peerId: PeerId, index: MessageIndex, count: Int, anchorIndex: MessageIndex, fixedCombinedReadState: CombinedPeerReadState?, tagMask: MessageTags? = nil, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageHistoryViewForPeerId(_ peerId: PeerId, index: MessageIndex, count: Int, anchorIndex: MessageIndex, fixedCombinedReadState: CombinedPeerReadState?, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { - let signal = account.postbox.aroundMessageHistoryViewForPeerId(peerId, index: index, count: count, anchorIndex: anchorIndex, fixedCombinedReadState: fixedCombinedReadState, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, additionalData: additionalData) + let signal = account.postbox.aroundMessageHistoryViewForPeerId(peerId, index: index, count: count, anchorIndex: anchorIndex, fixedCombinedReadState: fixedCombinedReadState, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, orderStatistics: orderStatistics, additionalData: additionalData) return wrappedMessageHistorySignal(peerId: peerId, signal: signal) } else { return .never() diff --git a/TelegramCore/MultipartUpload.swift b/TelegramCore/MultipartUpload.swift index 8d024c499f..de53ea8ded 100644 --- a/TelegramCore/MultipartUpload.swift +++ b/TelegramCore/MultipartUpload.swift @@ -112,6 +112,28 @@ private struct MultipartIntermediateResult { let bigTotalParts: Int? } +private enum MultipartUploadData { + case resourceData(MediaResourceData) + case data(Data) + + var size: Int { + switch self { + case let .resourceData(data): + return data.size + case let .data(data): + return data.count + } + } + var complete: Bool { + switch self { + case let .resourceData(data): + return data.complete + case .data: + return true + } + } +} + private final class MultipartUploadManager { let parallelParts: Int = 3 let defaultPartSize: Int @@ -120,7 +142,7 @@ private final class MultipartUploadManager { let queue = Queue() let fileId: Int64 - let dataSignal: Signal + let dataSignal: Signal var committedOffset: Int let uploadPart: (UploadPart) -> Signal @@ -131,13 +153,13 @@ private final class MultipartUploadManager { var uploadedParts: [Int: Int] = [:] let dataDisposable = MetaDisposable() - var resourceData: MediaResourceData? + var resourceData: MultipartUploadData? var headerPartReady: Bool let state: MultipartUploadState - init(resource: MediaResource, data: Signal, encryptionKey: SecretFileEncryptionKey?, hintFileSize: Int?, uploadPart: @escaping (UploadPart) -> Signal, progress: @escaping (Float) -> Void, completed: @escaping (MultipartIntermediateResult) -> Void) { + init(headerSize: Int32, data: Signal, encryptionKey: SecretFileEncryptionKey?, hintFileSize: Int?, uploadPart: @escaping (UploadPart) -> Signal, progress: @escaping (Float) -> Void, completed: @escaping (MultipartIntermediateResult) -> Void) { self.dataSignal = data var fileId: Int64 = 0 @@ -151,7 +173,7 @@ private final class MultipartUploadManager { self.progress = progress self.completed = completed - self.headerPartReady = resource.headerSize == 0 + self.headerPartReady = headerSize == 0 if let hintFileSize = hintFileSize, hintFileSize > 5 * 1024 * 1024 { self.defaultPartSize = 512 * 1024 @@ -219,7 +241,13 @@ private final class MultipartUploadManager { let partOffset = 0 let partSize = min(resourceData.size - partOffset, self.defaultPartSize) let partIndex = partOffset / self.defaultPartSize - let fileData = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.alwaysMapped]) + let fileData: Data? + switch resourceData { + case let .resourceData(data): + fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.alwaysMapped]) + case let .data(data): + fileData = data + } let partData = fileData!.subdata(in: partOffset ..< (partOffset + partSize)) var currentBigTotalParts = self.bigTotalParts if let _ = self.bigTotalParts { @@ -251,7 +279,13 @@ private final class MultipartUploadManager { if nextOffset < resourceData.size && partSize > 0 && (resourceData.complete || partSize == self.defaultPartSize) { let partIndex = partOffset / self.defaultPartSize - let fileData = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.alwaysMapped]) + let fileData: Data? + switch resourceData { + case let .resourceData(data): + fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.alwaysMapped]) + case let .data(data): + fileData = data + } let partData = self.state.transform(data: fileData!.subdata(in: partOffset ..< (partOffset + partSize))) var currentBigTotalParts = self.bigTotalParts if let _ = self.bigTotalParts, resourceData.complete && partOffset + partSize == resourceData.size { @@ -283,7 +317,12 @@ enum MultipartUploadResult { case inputSecretFile(Api.InputEncryptedFile, Int32, SecretFileEncryptionKey) } -func multipartUpload(network: Network, postbox: Postbox, resource: MediaResource, encrypt: Bool, hintFileSize: Int? = nil) -> Signal { +enum MultipartUploadSource { + case resource(MediaResource) + case data(Data) +} + +func multipartUpload(network: Network, postbox: Postbox, source: MultipartUploadSource, encrypt: Bool, hintFileSize: Int? = nil) -> Signal { return network.download(datacenterId: network.datacenterId) |> mapToSignal { download -> Signal in return Signal { subscriber in @@ -302,9 +341,21 @@ func multipartUpload(network: Network, postbox: Postbox, resource: MediaResource encryptionKey = SecretFileEncryptionKey(aesKey: aesKey, aesIv: aesIv) } - let resourceData = postbox.mediaBox.resourceData(resource, option: .incremental(waitUntilFetchStatus: true)) + let dataSignal: Signal + let headerSize: Int32 + let fetchedResource: Signal + switch source { + 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) + case let .data(data): + dataSignal = .single(.data(data)) + headerSize = 0 + fetchedResource = .complete() + } - let manager = MultipartUploadManager(resource: resource, data: resourceData, encryptionKey: encryptionKey, hintFileSize: hintFileSize, uploadPart: { part in + let manager = MultipartUploadManager(headerSize: headerSize, data: dataSignal, encryptionKey: encryptionKey, hintFileSize: hintFileSize, uploadPart: { part in return download.uploadPart(fileId: part.fileId, index: part.index, data: part.data, bigTotalParts: part.bigTotalParts) }, progress: { progress in subscriber.putNext(.progress(progress)) @@ -337,11 +388,11 @@ func multipartUpload(network: Network, postbox: Postbox, resource: MediaResource manager.start() - let fetchedResource = postbox.mediaBox.fetchedResource(resource).start() + let fetchedResourceDisposable = fetchedResource.start() return ActionDisposable { manager.cancel() - fetchedResource.dispose() + fetchedResourceDisposable.dispose() } } } diff --git a/TelegramCore/PeerPhotoUpdater.swift b/TelegramCore/PeerPhotoUpdater.swift index 725a01d531..d5998f3f1b 100644 --- a/TelegramCore/PeerPhotoUpdater.swift +++ b/TelegramCore/PeerPhotoUpdater.swift @@ -25,7 +25,7 @@ public func updateAccountPhoto(account:Account, resource:MediaResource) -> Signa public func updatePeerPhoto(account:Account, peerId:PeerId, resource:MediaResource) -> Signal { return account.postbox.loadedPeerWithId(peerId) |> mapError {_ in return .generic} |> mapToSignal { peer in - return multipartUpload(network: account.network, postbox: account.postbox, resource: resource, encrypt: false) + return multipartUpload(network: account.network, postbox: account.postbox, source: .resource(resource), encrypt: false) |> mapError {_ in return .generic} |> mapToSignal { result -> Signal in switch result { diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index dcf772c3e9..682e7d9478 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -74,7 +74,7 @@ func messageContentToUpload(network: Network, postbox: Postbox, transformOutgoin 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: peerId.namespace == Namespaces.Peer.SecretChat) + return multipartUpload(network: network, postbox: postbox, source: .resource(largestRepresentation.resource), encrypt: peerId.namespace == Namespaces.Peer.SecretChat) |> map { next -> PendingMessageUploadedContentResult in switch next { case let .progress(progress): @@ -90,9 +90,9 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI } } -private func inputDocumentAttributesFromFile(_ file: TelegramMediaFile) -> [Api.DocumentAttribute] { +func inputDocumentAttributesFromFileAttributes(_ fileAttributes: [TelegramMediaFileAttribute]) -> [Api.DocumentAttribute] { var attributes: [Api.DocumentAttribute] = [] - for attribute in file.attributes { + for attribute in fileAttributes { switch attribute { case .Animated: attributes.append(.documentAttributeAnimated) @@ -149,7 +149,7 @@ private enum UploadedMediaThumbnail { } private func uploadedThumbnail(network: Network, postbox: Postbox, image: TelegramMediaImageRepresentation) -> Signal { - return multipartUpload(network: network, postbox: postbox, resource: image.resource, encrypt: false) + return multipartUpload(network: network, postbox: postbox, source: .resource(image.resource), encrypt: false) |> mapToSignal { result -> Signal in switch result { case .progress: @@ -169,7 +169,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, transf } 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: peerId.namespace == Namespaces.Peer.SecretChat, hintFileSize: hintSize) + let upload = multipartUpload(network: network, postbox: postbox, source: .resource(file.resource), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, hintFileSize: hintSize) /*|> map { next -> UploadedMediaFileContent in switch next { case let .progress(progress): @@ -249,9 +249,9 @@ 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: text, stickers: nil) + inputMedia = Api.InputMedia.inputMediaUploadedThumbDocument(flags: 0, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), caption: text, stickers: nil) } else { - inputMedia = Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFile(file), caption: text, stickers: nil) + inputMedia = Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), caption: text, stickers: nil) } return .single(.content(.media(inputMedia))) } else { diff --git a/TelegramCore/StandaloneSendMessage.swift b/TelegramCore/StandaloneSendMessage.swift index bdea43ce4b..b045cd97cb 100644 --- a/TelegramCore/StandaloneSendMessage.swift +++ b/TelegramCore/StandaloneSendMessage.swift @@ -11,32 +11,46 @@ import Foundation public enum StandaloneMedia { case image(Data) - case file(Data) + case file(data: Data, mimeType: String, attributes: [TelegramMediaFileAttribute]) } -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 enum StandaloneMessageContent { + case text(String) + case media(Api.InputMedia) +} + +public func standaloneSendMessage(account: Account, peerId: PeerId, text: String, attributes: [MessageAttribute], media: StandaloneMedia?, replyToMessageId: MessageId?) -> Signal { + let content: Signal + if let media = media { + switch media { + case let .image(data): + content = uploadedImage(account: account, text: text, data: data) + |> map { next -> StandaloneMessageContent in + return .media(next) + } + case let .file(data, mimeType, attributes): + content = uploadedFile(account: account, text: text, data: data, mimeType: mimeType, attributes: attributes) + |> map { next -> StandaloneMessageContent in + return .media(next) + } + } + } else { + content = .single(.text(text)) } + + return content + |> mapToSignal { content -> Signal in + return sendMessageContent(account: account, peerId: peerId, attributes: attributes, content: content) + } } -private func sendMessageContent(account: Account, peerId: PeerId, attributes: [MessageAttribute], content: PendingMessageUploadedContent) -> Signal { +private func sendMessageContent(account: Account, peerId: PeerId, attributes: [MessageAttribute], content: StandaloneMessageContent) -> 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 forwardSourceInfoAttribute: ForwardSourceInfoAttribute? var messageEntities: [Api.MessageEntity]? var replyMessageId: Int32? @@ -49,8 +63,8 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M 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 as? ForwardSourceInfoAttribute { + //forwardSourceInfoAttribute = attribute } else if let attribute = attribute as? TextEntitiesMessageAttribute { messageEntities = apiTextAttributeEntities(attribute, associatedPeers: SimpleDictionary()) } else if let attribute = attribute as? OutgoingContentInfoMessageAttribute { @@ -79,23 +93,6 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M |> 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 @@ -115,3 +112,27 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M } } |> switchToLatest } + +private func uploadedImage(account: Account, text: String, data: Data) -> Signal { + return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false) + |> mapToSignal { next -> Signal in + switch next { + case let .inputFile(inputFile): + return .single(Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, caption: text, stickers: nil)) + case .inputSecretFile, .progress: + return .complete() + } + } +} + +private func uploadedFile(account: Account, text: String, data: Data, mimeType: String, attributes: [TelegramMediaFileAttribute]) -> Signal { + return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false) + |> mapToSignal { next -> Signal in + switch next { + case let .inputFile(inputFile): + return .single(Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), caption: text, stickers: nil)) + case .inputSecretFile, .progress: + return .complete() + } + } +}