diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index c7765ea8fa..738a61afc0 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -5922,7 +5922,7 @@ Sorry for the inconvenience."; "Conversation.AddMembers" = "Add Members"; -"Conversation.ImportedMessageHint" = "The messages was imported from another app. We can't guarantee it's real."; +"Conversation.ImportedMessageHint" = "This message was imported from another app. We can't guarantee it's real."; "Conversation.GreetingText" = "Send a message or tap on the greeting below."; @@ -5933,7 +5933,7 @@ Sorry for the inconvenience."; "Conversation.AudioRateTooltipSpeedUp" = "The audio will now play 2 times faster."; "Conversation.AudioRateTooltipNormal" = "The audio will now play at normal speed."; -"ChatImport.Title" = "Import Chat"; +"ChatImport.Title" = "Select Chat"; "ChatImport.SelectionErrorNotAdmin" = "You need to be an admin of the group to import messages into it."; "ChatImport.SelectionErrorGroupGeneric" = "You can't import history into this group."; "ChatImport.SelectionConfirmationGroupWithTitle" = "Are you sure you want to import messages from **%1$@** into **%2$@**?"; @@ -5956,5 +5956,6 @@ Sorry for the inconvenience."; "ChatImportActivity.InProgress" = "Please keep this window open\nduring the import."; "ChatImportActivity.ErrorNotAdmin" = "You need to be an admin."; "ChatImportActivity.ErrorInvalidChatType" = "You can't import this history in this type of chat."; +"ChatImportActivity.ErrorUserBlocked" = "You need to be an admin."; "ChatImportActivity.ErrorGeneric" = "An error occurred."; "ChatImportActivity.Success" = "Chat imported\nsuccessfully."; diff --git a/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift b/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift index 0a5fda0e05..c98fd2981e 100644 --- a/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift +++ b/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift @@ -549,8 +549,8 @@ public final class ChatImportActivityScreen: ViewController { } } } - |> mapToSignal { session -> Signal<(String, Float), ImportError> in - var importSignal: Signal<(String, Float), ImportError> = .single(("", 0.0)) + |> mapToSignal { session -> Signal<[(String, Float)], ImportError> in + var mediaSignals: [Signal<(String, Float), ImportError>] = [] for (_, fileName, mediaType, fileData) in otherEntries { let unpackedFile: Signal = fileData.get() @@ -588,27 +588,27 @@ public final class ChatImportActivityScreen: ViewController { } } - importSignal = importSignal - |> then(uploadedMedia) + mediaSignals.append(Signal<(String, Float), ImportError>.single((fileName, 0.0)) + |> then(uploadedMedia)) } - importSignal = importSignal + return combineLatest(mediaSignals) |> then(ChatHistoryImport.startImport(account: context.account, session: session) |> mapError { _ -> ImportError in return .generic } - |> map { _ -> (String, Float) in + |> map { _ -> [(String, Float)] in }) - - return importSignal } - |> deliverOnMainQueue).start(next: { [weak self] (fileName, progress) in + |> deliverOnMainQueue).start(next: { [weak self] fileNameAndProgress in guard let strongSelf = self else { return } - if let (fileSize, _) = strongSelf.pendingEntries[fileName] { - strongSelf.pendingEntries[fileName] = (fileSize, progress) + for (fileName, progress) in fileNameAndProgress { + if let (fileSize, _) = strongSelf.pendingEntries[fileName] { + strongSelf.pendingEntries[fileName] = (fileSize, progress) + } } var totalDoneBytes = strongSelf.mainEntrySize diff --git a/submodules/TelegramCore/Sources/ChatHistoryImport.swift b/submodules/TelegramCore/Sources/ChatHistoryImport.swift index 59fedf5d39..1ca297b9b6 100644 --- a/submodules/TelegramCore/Sources/ChatHistoryImport.swift +++ b/submodules/TelegramCore/Sources/ChatHistoryImport.swift @@ -104,7 +104,7 @@ public enum ChatHistoryImport { } public static func uploadMedia(account: Account, session: Session, file: TempBoxFile, fileName: String, mimeType: String, type: MediaType) -> Signal { - return multipartUpload(network: account.network, postbox: account.postbox, source: .tempFile(file), encrypt: false, tag: nil, hintFileSize: nil, hintFileIsLarge: false) + return multipartUpload(network: account.network, postbox: account.postbox, source: .tempFile(file), encrypt: false, tag: nil, hintFileSize: nil, hintFileIsLarge: false, useLargerParts: true, useMultiplexedRequests: true) |> mapError { _ -> UploadMediaError in return .generic } diff --git a/submodules/TelegramCore/Sources/Download.swift b/submodules/TelegramCore/Sources/Download.swift index e9b2e207f8..00d4108fd2 100644 --- a/submodules/TelegramCore/Sources/Download.swift +++ b/submodules/TelegramCore/Sources/Download.swift @@ -82,6 +82,33 @@ class Download: NSObject, MTRequestMessageServiceDelegate { self.context.authTokenForDatacenter(withIdRequired: self.datacenterId, authToken:self.mtProto.requiredAuthToken, masterDatacenterId: self.mtProto.authTokenMasterDatacenterId) } + static func uploadPart(multiplexedManager: MultiplexedRequestManager, datacenterId: Int, consumerId: Int64, tag: MediaResourceFetchTag?, fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil) -> Signal { + let saveFilePart: (FunctionDescription, Buffer, DeserializeFunctionResponse) + if asBigPart { + let totalParts: Int32 + if let bigTotalParts = bigTotalParts { + totalParts = Int32(bigTotalParts) + } else { + totalParts = -1 + } + saveFilePart = Api.functions.upload.saveBigFilePart(fileId: fileId, filePart: Int32(index), fileTotalParts: totalParts, bytes: Buffer(data: data)) + } else { + saveFilePart = Api.functions.upload.saveFilePart(fileId: fileId, filePart: Int32(index), bytes: Buffer(data: data)) + } + + return multiplexedManager.request(to: .main(datacenterId), consumerId: consumerId, data: saveFilePart, tag: tag, continueInBackground: true) + |> mapError { error -> UploadPartError in + if error.errorCode == 400 { + return .invalidMedia + } else { + return .generic + } + } + |> mapToSignal { _ -> Signal in + return .complete() + } + } + func uploadPart(fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil) -> Signal { return Signal { subscriber in let request = MTRequest() diff --git a/submodules/TelegramCore/Sources/MultipartUpload.swift b/submodules/TelegramCore/Sources/MultipartUpload.swift index 9da058949b..1b788b37ec 100644 --- a/submodules/TelegramCore/Sources/MultipartUpload.swift +++ b/submodules/TelegramCore/Sources/MultipartUpload.swift @@ -117,6 +117,7 @@ private final class MultipartUploadManager { var defaultPartSize: Int var bigTotalParts: Int? var bigParts: Bool + private let useLargerParts: Bool let queue = Queue() let fileId: Int64 @@ -138,13 +139,15 @@ private final class MultipartUploadManager { let state: MultipartUploadState - init(headerSize: Int32, data: Signal, encryptionKey: SecretFileEncryptionKey?, hintFileSize: Int?, hintFileIsLarge: Bool, uploadPart: @escaping (UploadPart) -> Signal, progress: @escaping (Float) -> Void, completed: @escaping (MultipartIntermediateResult?) -> Void) { + init(headerSize: Int32, data: Signal, encryptionKey: SecretFileEncryptionKey?, hintFileSize: Int?, hintFileIsLarge: Bool, useLargerParts: Bool, uploadPart: @escaping (UploadPart) -> Signal, progress: @escaping (Float) -> Void, completed: @escaping (MultipartIntermediateResult?) -> Void) { self.dataSignal = data var fileId: Int64 = 0 arc4random_buf(&fileId, 8) self.fileId = fileId + self.useLargerParts = useLargerParts + self.state = MultipartUploadState(encryptionKey: encryptionKey) self.committedOffset = 0 @@ -166,6 +169,10 @@ private final class MultipartUploadManager { self.defaultPartSize = 512 * 1024 self.bigTotalParts = nil self.bigParts = true + } else if useLargerParts { + self.bigParts = false + self.defaultPartSize = 128 * 1024 + self.bigTotalParts = nil } else { self.bigParts = false self.defaultPartSize = 16 * 1024 @@ -202,7 +209,11 @@ private final class MultipartUploadManager { self.bigParts = true } else { self.bigParts = false - self.defaultPartSize = 16 * 1024 + if self.useLargerParts { + self.defaultPartSize = 128 * 1024 + } else { + self.defaultPartSize = 16 * 1024 + } self.bigTotalParts = nil } } @@ -368,9 +379,24 @@ enum MultipartUploadError { case generic } -func multipartUpload(network: Network, postbox: Postbox, source: MultipartUploadSource, encrypt: Bool, tag: MediaResourceFetchTag?, hintFileSize: Int?, hintFileIsLarge: Bool) -> Signal { - return network.upload(tag: tag) - |> mapToSignalPromotingError { download -> Signal in +func multipartUpload(network: Network, postbox: Postbox, source: MultipartUploadSource, encrypt: Bool, tag: MediaResourceFetchTag?, hintFileSize: Int?, hintFileIsLarge: Bool, useLargerParts: Bool = false, useMultiplexedRequests: Bool = false) -> Signal { + enum UploadInterface { + case download(Download) + case multiplexed(manager: MultiplexedRequestManager, datacenterId: Int, consumerId: Int64) + } + + let uploadInterface: Signal + if useMultiplexedRequests { + uploadInterface = .single(.multiplexed(manager: network.multiplexedRequestManager, datacenterId: network.datacenterId, consumerId: arc4random64())) + } else { + uploadInterface = network.upload(tag: tag) + |> map { download -> UploadInterface in + return .download(download) + } + } + + return uploadInterface + |> mapToSignalPromotingError { uploadInterface -> Signal in return Signal { subscriber in var encryptionKey: SecretFileEncryptionKey? if encrypt { @@ -419,8 +445,13 @@ func multipartUpload(network: Network, postbox: Postbox, source: MultipartUpload fetchedResource = .complete() } - let manager = MultipartUploadManager(headerSize: headerSize, data: dataSignal, encryptionKey: encryptionKey, hintFileSize: hintFileSize, hintFileIsLarge: hintFileIsLarge, uploadPart: { part in - return download.uploadPart(fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts) + let manager = MultipartUploadManager(headerSize: headerSize, data: dataSignal, encryptionKey: encryptionKey, hintFileSize: hintFileSize, hintFileIsLarge: hintFileIsLarge, useLargerParts: useLargerParts, uploadPart: { part in + switch uploadInterface { + case let .download(download): + return download.uploadPart(fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts) + case let .multiplexed(multiplexed, datacenterId, consumerId): + return Download.uploadPart(multiplexedManager: multiplexed, datacenterId: datacenterId, consumerId: consumerId, tag: nil, fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts) + } }, progress: { progress in subscriber.putNext(.progress(progress)) }, completed: { result in