mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Initial downloads list implementation
This commit is contained in:
parent
e87cf3710f
commit
c8bc4b7f12
@ -93,3 +93,10 @@ public func messageMediaFileStatus(context: AccountContext, messageId: MessageId
|
|||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func messageMediaImageStatus(context: AccountContext, messageId: MessageId, image: TelegramMediaImage) -> Signal<MediaResourceStatus, NoError> {
|
||||||
|
guard let representation = image.representations.last else {
|
||||||
|
return .single(.Remote)
|
||||||
|
}
|
||||||
|
return context.fetchManager.fetchStatus(category: .image, location: .chat(messageId.peerId), locationKey: .messageId(messageId), resource: representation.resource)
|
||||||
|
}
|
||||||
|
@ -22,7 +22,8 @@ swift_library(
|
|||||||
"//submodules/OverlayStatusController:OverlayStatusController",
|
"//submodules/OverlayStatusController:OverlayStatusController",
|
||||||
"//submodules/AccountContext:AccountContext",
|
"//submodules/AccountContext:AccountContext",
|
||||||
"//submodules/AppBundle:AppBundle",
|
"//submodules/AppBundle:AppBundle",
|
||||||
"//submodules/GZip:GZip"
|
"//submodules/GZip:GZip",
|
||||||
|
"//third-party/ZipArchive:ZipArchive",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -13,7 +13,7 @@ import PresentationDataUtils
|
|||||||
import OverlayStatusController
|
import OverlayStatusController
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import AppBundle
|
import AppBundle
|
||||||
import GZip
|
import ZipArchive
|
||||||
|
|
||||||
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
||||||
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||||
@ -252,7 +252,19 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let gzippedData = TGGZipData(rawLogData, 1.0)
|
let tempSource = TempBox.shared.tempFile(fileName: "Log.txt")
|
||||||
|
let tempZip = TempBox.shared.tempFile(fileName: "destination.zip")
|
||||||
|
|
||||||
|
let _ = try? rawLogData.write(to: URL(fileURLWithPath: tempSource.path))
|
||||||
|
|
||||||
|
SSZipArchive.createZipFile(atPath: tempZip.path, withFilesAtPaths: [tempSource.path])
|
||||||
|
|
||||||
|
guard let gzippedData = try? Data(contentsOf: URL(fileURLWithPath: tempZip.path)) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
TempBox.shared.dispose(tempSource)
|
||||||
|
TempBox.shared.dispose(tempZip)
|
||||||
|
|
||||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||||
let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
||||||
@ -406,7 +418,19 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let gzippedData = TGGZipData(rawLogData, 1.0)
|
let tempSource = TempBox.shared.tempFile(fileName: "Log.txt")
|
||||||
|
let tempZip = TempBox.shared.tempFile(fileName: "destination.zip")
|
||||||
|
|
||||||
|
let _ = try? rawLogData.write(to: URL(fileURLWithPath: tempSource.path))
|
||||||
|
|
||||||
|
SSZipArchive.createZipFile(atPath: tempZip.path, withFilesAtPaths: [tempSource.path])
|
||||||
|
|
||||||
|
guard let gzippedData = try? Data(contentsOf: URL(fileURLWithPath: tempZip.path)) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
TempBox.shared.dispose(tempSource)
|
||||||
|
TempBox.shared.dispose(tempZip)
|
||||||
|
|
||||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||||
let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
||||||
@ -478,7 +502,19 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let gzippedData = TGGZipData(rawLogData, 1.0)
|
let tempSource = TempBox.shared.tempFile(fileName: "Log.txt")
|
||||||
|
let tempZip = TempBox.shared.tempFile(fileName: "destination.zip")
|
||||||
|
|
||||||
|
let _ = try? rawLogData.write(to: URL(fileURLWithPath: tempSource.path))
|
||||||
|
|
||||||
|
SSZipArchive.createZipFile(atPath: tempZip.path, withFilesAtPaths: [tempSource.path])
|
||||||
|
|
||||||
|
guard let gzippedData = try? Data(contentsOf: URL(fileURLWithPath: tempZip.path)) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
TempBox.shared.dispose(tempSource)
|
||||||
|
TempBox.shared.dispose(tempZip)
|
||||||
|
|
||||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||||
let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
||||||
@ -1055,3 +1091,61 @@ public func debugController(sharedContext: SharedAccountContext, context: Accoun
|
|||||||
}
|
}
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func triggerDebugSendLogsUI(context: AccountContext, additionalInfo: String = "", pushController: @escaping (ViewController) -> Void) {
|
||||||
|
let _ = (Logger.shared.collectLogs()
|
||||||
|
|> deliverOnMainQueue).start(next: { logs in
|
||||||
|
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
|
||||||
|
controller.peerSelected = { [weak controller] peer in
|
||||||
|
let peerId = peer.id
|
||||||
|
|
||||||
|
if let strongController = controller {
|
||||||
|
strongController.dismiss()
|
||||||
|
|
||||||
|
let lineFeed = "\n".data(using: .utf8)!
|
||||||
|
var rawLogData: Data = Data()
|
||||||
|
for (name, path) in logs {
|
||||||
|
if !rawLogData.isEmpty {
|
||||||
|
rawLogData.append(lineFeed)
|
||||||
|
rawLogData.append(lineFeed)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawLogData.append("------ File: \(name) ------\n".data(using: .utf8)!)
|
||||||
|
|
||||||
|
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
||||||
|
rawLogData.append(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !additionalInfo.isEmpty {
|
||||||
|
rawLogData.append("------ Additional Info ------\n".data(using: .utf8)!)
|
||||||
|
rawLogData.append("\(additionalInfo)".data(using: .utf8)!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let tempSource = TempBox.shared.tempFile(fileName: "Log.txt")
|
||||||
|
let tempZip = TempBox.shared.tempFile(fileName: "destination.zip")
|
||||||
|
|
||||||
|
let _ = try? rawLogData.write(to: URL(fileURLWithPath: tempSource.path))
|
||||||
|
|
||||||
|
SSZipArchive.createZipFile(atPath: tempZip.path, withFilesAtPaths: [tempSource.path])
|
||||||
|
|
||||||
|
guard let gzippedData = try? Data(contentsOf: URL(fileURLWithPath: tempZip.path)) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
TempBox.shared.dispose(tempSource)
|
||||||
|
TempBox.shared.dispose(tempZip)
|
||||||
|
|
||||||
|
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||||
|
let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
||||||
|
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData)
|
||||||
|
|
||||||
|
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: gzippedData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt.zip")])
|
||||||
|
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||||
|
|
||||||
|
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushController(controller)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
23
submodules/FetchManagerImpl/BUILD
Normal file
23
submodules/FetchManagerImpl/BUILD
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "FetchManagerImpl",
|
||||||
|
module_name = "FetchManagerImpl",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
|
"//submodules/Postbox:Postbox",
|
||||||
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
|
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
||||||
|
"//submodules/AccountContext:AccountContext",
|
||||||
|
"//submodules/MediaPlayer:UniversalMediaPlayer",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
@ -7,12 +7,12 @@ import TelegramUIPreferences
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
|
|
||||||
private struct FetchManagerLocationEntryId: Hashable {
|
public struct FetchManagerLocationEntryId: Hashable {
|
||||||
let location: FetchManagerLocation
|
public let location: FetchManagerLocation
|
||||||
let resourceId: MediaResourceId
|
public let resourceId: MediaResourceId
|
||||||
let locationKey: FetchManagerLocationKey
|
public let locationKey: FetchManagerLocationKey
|
||||||
|
|
||||||
static func ==(lhs: FetchManagerLocationEntryId, rhs: FetchManagerLocationEntryId) -> Bool {
|
public static func ==(lhs: FetchManagerLocationEntryId, rhs: FetchManagerLocationEntryId) -> Bool {
|
||||||
if lhs.location != rhs.location {
|
if lhs.location != rhs.location {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ private struct FetchManagerLocationEntryId: Hashable {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
hasher.combine(self.resourceId.hashValue)
|
hasher.combine(self.resourceId.hashValue)
|
||||||
hasher.combine(self.locationKey)
|
hasher.combine(self.locationKey)
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ private final class FetchManagerCategoryContext {
|
|||||||
private let postbox: Postbox
|
private let postbox: Postbox
|
||||||
private let storeManager: DownloadedMediaStoreManager?
|
private let storeManager: DownloadedMediaStoreManager?
|
||||||
private let entryCompleted: (FetchManagerLocationEntryId) -> Void
|
private let entryCompleted: (FetchManagerLocationEntryId) -> Void
|
||||||
private let activeEntriesUpdated: () -> Void
|
private let activeEntriesUpdated: ([FetchManagerEntrySummary]) -> Void
|
||||||
|
|
||||||
private var topEntryIdAndPriority: (FetchManagerLocationEntryId, FetchManagerPriorityKey)?
|
private var topEntryIdAndPriority: (FetchManagerLocationEntryId, FetchManagerPriorityKey)?
|
||||||
private var entries: [FetchManagerLocationEntryId: FetchManagerLocationEntry] = [:]
|
private var entries: [FetchManagerLocationEntryId: FetchManagerLocationEntry] = [:]
|
||||||
@ -133,7 +133,7 @@ private final class FetchManagerCategoryContext {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
init(postbox: Postbox, storeManager: DownloadedMediaStoreManager?, entryCompleted: @escaping (FetchManagerLocationEntryId) -> Void, activeEntriesUpdated: @escaping () -> Void) {
|
init(postbox: Postbox, storeManager: DownloadedMediaStoreManager?, entryCompleted: @escaping (FetchManagerLocationEntryId) -> Void, activeEntriesUpdated: @escaping ([FetchManagerEntrySummary]) -> Void) {
|
||||||
self.postbox = postbox
|
self.postbox = postbox
|
||||||
self.storeManager = storeManager
|
self.storeManager = storeManager
|
||||||
self.entryCompleted = entryCompleted
|
self.entryCompleted = entryCompleted
|
||||||
@ -182,6 +182,7 @@ private final class FetchManagerCategoryContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var activeContextsUpdated = false
|
var activeContextsUpdated = false
|
||||||
|
let _ = activeContextsUpdated
|
||||||
|
|
||||||
if self.maybeFindAndActivateNewTopEntry() {
|
if self.maybeFindAndActivateNewTopEntry() {
|
||||||
activeContextsUpdated = true
|
activeContextsUpdated = true
|
||||||
@ -285,9 +286,7 @@ private final class FetchManagerCategoryContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if activeContextsUpdated {
|
self.activeEntriesUpdated(self.entries.values.compactMap(FetchManagerEntrySummary.init).sorted(by: { $0.priority < $1.priority }))
|
||||||
self.activeEntriesUpdated()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func maybeFindAndActivateNewTopEntry() -> Bool {
|
func maybeFindAndActivateNewTopEntry() -> Bool {
|
||||||
@ -406,8 +405,12 @@ private final class FetchManagerCategoryContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var entriesRemoved = false
|
||||||
|
let _ = entriesRemoved
|
||||||
|
|
||||||
if let _ = self.entries[id] {
|
if let _ = self.entries[id] {
|
||||||
self.entries.removeValue(forKey: id)
|
self.entries.removeValue(forKey: id)
|
||||||
|
entriesRemoved = true
|
||||||
|
|
||||||
if let statusContext = self.statusContexts[id] {
|
if let statusContext = self.statusContexts[id] {
|
||||||
if statusContext.hasEntry {
|
if statusContext.hasEntry {
|
||||||
@ -426,6 +429,7 @@ private final class FetchManagerCategoryContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var activeContextsUpdated = false
|
var activeContextsUpdated = false
|
||||||
|
let _ = activeContextsUpdated
|
||||||
|
|
||||||
if let activeContext = self.activeContexts[id] {
|
if let activeContext = self.activeContexts[id] {
|
||||||
activeContext.disposable?.dispose()
|
activeContext.disposable?.dispose()
|
||||||
@ -442,9 +446,7 @@ private final class FetchManagerCategoryContext {
|
|||||||
activeContextsUpdated = true
|
activeContextsUpdated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if activeContextsUpdated {
|
self.activeEntriesUpdated(self.entries.values.compactMap(FetchManagerEntrySummary.init).sorted(by: { $0.priority < $1.priority }))
|
||||||
self.activeEntriesUpdated()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func withFetchStatusContext(_ id: FetchManagerLocationEntryId, _ f: (FetchManagerStatusContext) -> Void) {
|
func withFetchStatusContext(_ id: FetchManagerLocationEntryId, _ f: (FetchManagerStatusContext) -> Void) {
|
||||||
@ -472,6 +474,25 @@ private final class FetchManagerCategoryContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct FetchManagerEntrySummary: Equatable {
|
||||||
|
public let id: FetchManagerLocationEntryId
|
||||||
|
public let mediaReference: AnyMediaReference?
|
||||||
|
public let resourceReference: MediaResourceReference
|
||||||
|
public let priority: FetchManagerPriorityKey
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension FetchManagerEntrySummary {
|
||||||
|
init?(entry: FetchManagerLocationEntry) {
|
||||||
|
guard let priority = entry.priorityKey else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.id = entry.id
|
||||||
|
self.mediaReference = entry.mediaReference
|
||||||
|
self.resourceReference = entry.resourceReference
|
||||||
|
self.priority = priority
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class FetchManagerImpl: FetchManager {
|
public final class FetchManagerImpl: FetchManager {
|
||||||
public let queue = Queue.mainQueue()
|
public let queue = Queue.mainQueue()
|
||||||
private let postbox: Postbox
|
private let postbox: Postbox
|
||||||
@ -486,7 +507,12 @@ public final class FetchManagerImpl: FetchManager {
|
|||||||
return self.hasUserInitiatedEntriesValue.get()
|
return self.hasUserInitiatedEntriesValue.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(postbox: Postbox, storeManager: DownloadedMediaStoreManager?) {
|
private let entriesSummaryValue = ValuePromise<[FetchManagerEntrySummary]>([], ignoreRepeated: true)
|
||||||
|
public var entriesSummary: Signal<[FetchManagerEntrySummary], NoError> {
|
||||||
|
return self.entriesSummaryValue.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(postbox: Postbox, storeManager: DownloadedMediaStoreManager?) {
|
||||||
self.postbox = postbox
|
self.postbox = postbox
|
||||||
self.storeManager = storeManager
|
self.storeManager = storeManager
|
||||||
}
|
}
|
||||||
@ -519,7 +545,7 @@ public final class FetchManagerImpl: FetchManager {
|
|||||||
context.cancelEntry(id, isCompleted: true)
|
context.cancelEntry(id, isCompleted: true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, activeEntriesUpdated: { [weak self] in
|
}, activeEntriesUpdated: { [weak self] entries in
|
||||||
queue.async {
|
queue.async {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -532,6 +558,8 @@ public final class FetchManagerImpl: FetchManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
strongSelf.hasUserInitiatedEntriesValue.set(hasActiveUserInitiatedEntries)
|
strongSelf.hasUserInitiatedEntriesValue.set(hasActiveUserInitiatedEntries)
|
||||||
|
|
||||||
|
strongSelf.entriesSummaryValue.set(entries)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.categoryContexts[key] = context
|
self.categoryContexts[key] = context
|
@ -97,3 +97,26 @@ public func messageFileMediaResourceStatus(context: AccountContext, file: Telegr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func messageImageMediaResourceStatus(context: AccountContext, image: TelegramMediaImage, message: Message, isRecentActions: Bool, isSharedMedia: Bool = false, isGlobalSearch: Bool = false) -> Signal<FileMediaResourceStatus, NoError> {
|
||||||
|
if message.flags.isSending {
|
||||||
|
return combineLatest(messageMediaImageStatus(context: context, messageId: message.id, image: image), context.account.pendingMessageManager.pendingMessageStatus(message.id) |> map { $0.0 })
|
||||||
|
|> map { resourceStatus, pendingStatus -> FileMediaResourceStatus in
|
||||||
|
let mediaStatus: FileMediaResourceMediaStatus
|
||||||
|
if let pendingStatus = pendingStatus {
|
||||||
|
mediaStatus = .fetchStatus(.Fetching(isActive: pendingStatus.isRunning, progress: pendingStatus.progress))
|
||||||
|
} else {
|
||||||
|
mediaStatus = .fetchStatus(resourceStatus)
|
||||||
|
}
|
||||||
|
return FileMediaResourceStatus(mediaStatus: mediaStatus, fetchStatus: resourceStatus)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return messageMediaImageStatus(context: context, messageId: message.id, image: image)
|
||||||
|
|> map { resourceStatus -> FileMediaResourceStatus in
|
||||||
|
let mediaStatus: FileMediaResourceMediaStatus
|
||||||
|
mediaStatus = .fetchStatus(resourceStatus)
|
||||||
|
return FileMediaResourceStatus(mediaStatus: mediaStatus, fetchStatus: resourceStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ final class ChatMediaGalleryThumbnailItem: GalleryThumbnailItem {
|
|||||||
}
|
}
|
||||||
case let .file(fileReference):
|
case let .file(fileReference):
|
||||||
if let representation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
if let representation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
||||||
return (chatWebpageSnippetFile(account: self.account, fileReference: fileReference, representation: representation), representation.dimensions.cgSize)
|
return (chatWebpageSnippetFile(account: self.account, mediaReference: fileReference.abstract, representation: representation), representation.dimensions.cgSize)
|
||||||
} else {
|
} else {
|
||||||
return (.single({ _ in return nil }), CGSize(width: 128.0, height: 128.0))
|
return (.single({ _ in return nil }), CGSize(width: 128.0, height: 128.0))
|
||||||
}
|
}
|
||||||
|
@ -95,30 +95,30 @@ private struct FetchControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum FileIconImage: Equatable {
|
private enum FileIconImage: Equatable {
|
||||||
case imageRepresentation(TelegramMediaFile, TelegramMediaImageRepresentation)
|
case imageRepresentation(Media, TelegramMediaImageRepresentation)
|
||||||
case albumArt(TelegramMediaFile, SharedMediaPlaybackAlbumArt)
|
case albumArt(TelegramMediaFile, SharedMediaPlaybackAlbumArt)
|
||||||
case roundVideo(TelegramMediaFile)
|
case roundVideo(TelegramMediaFile)
|
||||||
|
|
||||||
static func ==(lhs: FileIconImage, rhs: FileIconImage) -> Bool {
|
static func ==(lhs: FileIconImage, rhs: FileIconImage) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case let .imageRepresentation(file, value):
|
case let .imageRepresentation(lhsMedia, lhsValue):
|
||||||
if case .imageRepresentation(file, value) = rhs {
|
if case let .imageRepresentation(rhsMedia, rhsValue) = rhs, lhsMedia.isEqual(to: rhsMedia), lhsValue == rhsValue {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .albumArt(file, value):
|
case let .albumArt(file, value):
|
||||||
if case .albumArt(file, value) = rhs {
|
if case .albumArt(file, value) = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .roundVideo(file):
|
case let .roundVideo(file):
|
||||||
if case .roundVideo(file) = rhs {
|
if case .roundVideo(file) = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,6 +155,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
|
|
||||||
private let offsetContainerNode: ASDisplayNode
|
private let offsetContainerNode: ASDisplayNode
|
||||||
|
|
||||||
|
private var backgroundNode: ASDisplayNode?
|
||||||
private let highlightedBackgroundNode: ASDisplayNode
|
private let highlightedBackgroundNode: ASDisplayNode
|
||||||
public let separatorNode: ASDisplayNode
|
public let separatorNode: ASDisplayNode
|
||||||
|
|
||||||
@ -402,7 +403,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
|
|
||||||
let message = item.message
|
let message = item.message
|
||||||
|
|
||||||
var selectedMedia: TelegramMediaFile?
|
var selectedMedia: Media?
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
if let file = media as? TelegramMediaFile {
|
if let file = media as? TelegramMediaFile {
|
||||||
selectedMedia = file
|
selectedMedia = file
|
||||||
@ -539,6 +540,34 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
} else if let image = media as? TelegramMediaImage {
|
||||||
|
selectedMedia = image
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let fileName: String = "Photo"
|
||||||
|
titleText = NSAttributedString(string: fileName, font: titleFont, textColor: item.presentationData.theme.theme.list.itemPrimaryTextColor)
|
||||||
|
|
||||||
|
if let representation = smallestImageRepresentation(image.representations) {
|
||||||
|
iconImage = .imageRepresentation(image, representation)
|
||||||
|
}
|
||||||
|
|
||||||
|
let dateString = stringForFullDate(timestamp: item.message.timestamp, strings: item.presentationData.strings, dateTimeFormat: item.presentationData.dateTimeFormat)
|
||||||
|
|
||||||
|
var descriptionString: String = ""
|
||||||
|
if !item.isGlobalSearchResult {
|
||||||
|
descriptionString = "\(dateString)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if item.isGlobalSearchResult {
|
||||||
|
let authorString = stringForFullAuthorName(message: EngineMessage(item.message), strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, accountPeerId: item.context.account.peerId)
|
||||||
|
if descriptionString.isEmpty {
|
||||||
|
descriptionString = authorString
|
||||||
|
} else {
|
||||||
|
descriptionString = "\(descriptionString) • \(authorString)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionText = NSAttributedString(string: descriptionString, font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,38 +599,57 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
let context = item.context
|
let context = item.context
|
||||||
updatedFetchControls = FetchControls(fetch: { [weak self] in
|
updatedFetchControls = FetchControls(fetch: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: context, message: message, file: selectedMedia, userInitiated: true).start())
|
if let file = selectedMedia as? TelegramMediaFile {
|
||||||
|
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: context, message: message, file: file, userInitiated: true).start())
|
||||||
|
} else if let image = selectedMedia as? TelegramMediaImage, let representation = image.representations.last {
|
||||||
|
strongSelf.fetchDisposable.set(messageMediaImageInteractiveFetched(context: context, message: message, image: image, resource: representation.resource, userInitiated: true, storeToDownloadsPeerType: nil).start())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, cancel: {
|
}, cancel: {
|
||||||
messageMediaFileCancelInteractiveFetch(context: context, messageId: message.id, file: selectedMedia)
|
if let file = selectedMedia as? TelegramMediaFile {
|
||||||
|
messageMediaFileCancelInteractiveFetch(context: context, messageId: message.id, file: file)
|
||||||
|
} else if let image = selectedMedia as? TelegramMediaImage, let representation = image.representations.last {
|
||||||
|
messageMediaImageCancelInteractiveFetch(context: context, messageId: message.id, image: image, resource: representation.resource)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if statusUpdated {
|
if statusUpdated {
|
||||||
updatedStatusSignal = messageFileMediaResourceStatus(context: item.context, file: selectedMedia, message: message, isRecentActions: false, isSharedMedia: true, isGlobalSearch: item.isGlobalSearchResult)
|
if let file = selectedMedia as? TelegramMediaFile {
|
||||||
|> mapToSignal { value -> Signal<FileMediaResourceStatus, NoError> in
|
updatedStatusSignal = messageFileMediaResourceStatus(context: item.context, file: file, message: message, isRecentActions: false, isSharedMedia: true, isGlobalSearch: item.isGlobalSearchResult)
|
||||||
if case .Fetching = value.fetchStatus {
|
|> mapToSignal { value -> Signal<FileMediaResourceStatus, NoError> in
|
||||||
return .single(value) |> delay(0.1, queue: Queue.concurrentDefaultQueue())
|
if case .Fetching = value.fetchStatus {
|
||||||
} else {
|
return .single(value) |> delay(0.1, queue: Queue.concurrentDefaultQueue())
|
||||||
return .single(value)
|
} else {
|
||||||
|
return .single(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if isAudio || isInstantVideo {
|
||||||
if isAudio || isInstantVideo {
|
if let currentUpdatedStatusSignal = updatedStatusSignal {
|
||||||
if let currentUpdatedStatusSignal = updatedStatusSignal {
|
updatedStatusSignal = currentUpdatedStatusSignal
|
||||||
updatedStatusSignal = currentUpdatedStatusSignal
|
|> map { status in
|
||||||
|> map { status in
|
switch status.mediaStatus {
|
||||||
switch status.mediaStatus {
|
case .fetchStatus:
|
||||||
case .fetchStatus:
|
return FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: status.fetchStatus)
|
||||||
return FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: status.fetchStatus)
|
case .playbackStatus:
|
||||||
case .playbackStatus:
|
return status
|
||||||
return status
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if isVoice {
|
||||||
if isVoice {
|
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: item.context, file: file, message: message, isRecentActions: false, isGlobalSearch: item.isGlobalSearchResult)
|
||||||
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: item.context, file: selectedMedia, message: message, isRecentActions: false, isGlobalSearch: item.isGlobalSearchResult)
|
}
|
||||||
|
} else if let image = selectedMedia as? TelegramMediaImage {
|
||||||
|
updatedStatusSignal = messageImageMediaResourceStatus(context: item.context, image: image, message: message, isRecentActions: false, isSharedMedia: true, isGlobalSearch: item.isGlobalSearchResult)
|
||||||
|
|> mapToSignal { value -> Signal<FileMediaResourceStatus, NoError> in
|
||||||
|
if case .Fetching = value.fetchStatus {
|
||||||
|
return .single(value) |> delay(0.1, queue: Queue.concurrentDefaultQueue())
|
||||||
|
} else {
|
||||||
|
return .single(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -684,8 +732,14 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
if currentIconImage != iconImage {
|
if currentIconImage != iconImage {
|
||||||
if let iconImage = iconImage {
|
if let iconImage = iconImage {
|
||||||
switch iconImage {
|
switch iconImage {
|
||||||
case let .imageRepresentation(file, representation):
|
case let .imageRepresentation(media, representation):
|
||||||
updateIconImageSignal = chatWebpageSnippetFile(account: item.context.account, fileReference: .message(message: MessageReference(message), media: file), representation: representation)
|
if let file = media as? TelegramMediaFile {
|
||||||
|
updateIconImageSignal = chatWebpageSnippetFile(account: item.context.account, mediaReference: FileMediaReference.message(message: MessageReference(message), media: file).abstract, representation: representation)
|
||||||
|
} else if let image = media as? TelegramMediaImage {
|
||||||
|
updateIconImageSignal = mediaGridMessagePhoto(account: item.context.account, photoReference: ImageMediaReference.message(message: MessageReference(message), media: image))
|
||||||
|
} else {
|
||||||
|
updateIconImageSignal = .complete()
|
||||||
|
}
|
||||||
case let .albumArt(file, albumArt):
|
case let .albumArt(file, albumArt):
|
||||||
updateIconImageSignal = playerAlbumArt(postbox: item.context.account.postbox, engine: item.context.engine, fileReference: .message(message: MessageReference(message), media: file), albumArt: albumArt, thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), emptyColor: item.presentationData.theme.theme.list.itemAccentColor)
|
updateIconImageSignal = playerAlbumArt(postbox: item.context.account.postbox, engine: item.context.engine, fileReference: .message(message: MessageReference(message), media: file), albumArt: albumArt, thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), emptyColor: item.presentationData.theme.theme.list.itemAccentColor)
|
||||||
case let .roundVideo(file):
|
case let .roundVideo(file):
|
||||||
@ -746,6 +800,18 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
strongSelf.currentLeftOffset = leftOffset
|
strongSelf.currentLeftOffset = leftOffset
|
||||||
|
|
||||||
if let _ = updatedTheme {
|
if let _ = updatedTheme {
|
||||||
|
if item.displayBackground {
|
||||||
|
let backgroundNode: ASDisplayNode
|
||||||
|
if let current = strongSelf.backgroundNode {
|
||||||
|
backgroundNode = current
|
||||||
|
} else {
|
||||||
|
backgroundNode = ASDisplayNode()
|
||||||
|
strongSelf.backgroundNode = backgroundNode
|
||||||
|
strongSelf.insertSubnode(backgroundNode, at: 0)
|
||||||
|
}
|
||||||
|
backgroundNode.backgroundColor = item.presentationData.theme.theme.list.itemBlocksBackgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.separatorNode.backgroundColor = item.presentationData.theme.theme.list.itemPlainSeparatorColor
|
strongSelf.separatorNode.backgroundColor = item.presentationData.theme.theme.list.itemPlainSeparatorColor
|
||||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.theme.list.itemHighlightedBackgroundColor
|
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.theme.list.itemHighlightedBackgroundColor
|
||||||
strongSelf.linearProgressNode?.updateTheme(theme: item.presentationData.theme.theme)
|
strongSelf.linearProgressNode?.updateTheme(theme: item.presentationData.theme.theme)
|
||||||
@ -778,6 +844,10 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: leftInset + leftOffset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset - leftOffset, height: UIScreenPixel)))
|
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: leftInset + leftOffset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset - leftOffset, height: UIScreenPixel)))
|
||||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - nodeLayout.insets.top), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel))
|
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - nodeLayout.insets.top), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel))
|
||||||
|
|
||||||
|
if let backgroundNode = strongSelf.backgroundNode {
|
||||||
|
backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top), size: CGSize(width: params.width, height: nodeLayout.size.height))
|
||||||
|
}
|
||||||
|
|
||||||
transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset, y: 9.0), size: titleNodeLayout.size))
|
transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset, y: 9.0), size: titleNodeLayout.size))
|
||||||
let _ = titleNodeApply()
|
let _ = titleNodeApply()
|
||||||
|
|
||||||
|
@ -51,12 +51,13 @@ public final class ListMessageItem: ListViewItem {
|
|||||||
public let selection: ChatHistoryMessageSelection
|
public let selection: ChatHistoryMessageSelection
|
||||||
let hintIsLink: Bool
|
let hintIsLink: Bool
|
||||||
let isGlobalSearchResult: Bool
|
let isGlobalSearchResult: Bool
|
||||||
|
let displayBackground: Bool
|
||||||
|
|
||||||
let header: ListViewItemHeader?
|
let header: ListViewItemHeader?
|
||||||
|
|
||||||
public let selectable: Bool = true
|
public let selectable: Bool = true
|
||||||
|
|
||||||
public init(presentationData: ChatPresentationData, context: AccountContext, chatLocation: ChatLocation, interaction: ListMessageItemInteraction, message: Message, selection: ChatHistoryMessageSelection, displayHeader: Bool, customHeader: ListViewItemHeader? = nil, hintIsLink: Bool = false, isGlobalSearchResult: Bool = false) {
|
public init(presentationData: ChatPresentationData, context: AccountContext, chatLocation: ChatLocation, interaction: ListMessageItemInteraction, message: Message, selection: ChatHistoryMessageSelection, displayHeader: Bool, customHeader: ListViewItemHeader? = nil, hintIsLink: Bool = false, isGlobalSearchResult: Bool = false, displayBackground: Bool = false) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.context = context
|
self.context = context
|
||||||
self.chatLocation = chatLocation
|
self.chatLocation = chatLocation
|
||||||
@ -72,6 +73,7 @@ public final class ListMessageItem: ListViewItem {
|
|||||||
self.selection = selection
|
self.selection = selection
|
||||||
self.hintIsLink = hintIsLink
|
self.hintIsLink = hintIsLink
|
||||||
self.isGlobalSearchResult = isGlobalSearchResult
|
self.isGlobalSearchResult = isGlobalSearchResult
|
||||||
|
self.displayBackground = displayBackground
|
||||||
}
|
}
|
||||||
|
|
||||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
@ -82,6 +84,9 @@ public final class ListMessageItem: ListViewItem {
|
|||||||
if let _ = media as? TelegramMediaFile {
|
if let _ = media as? TelegramMediaFile {
|
||||||
viewClassName = ListMessageFileItemNode.self
|
viewClassName = ListMessageFileItemNode.self
|
||||||
break
|
break
|
||||||
|
} else if let _ = media as? TelegramMediaImage {
|
||||||
|
viewClassName = ListMessageFileItemNode.self
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -528,7 +528,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
|||||||
if let imageReference = iconImageReferenceAndRepresentation.0.concrete(TelegramMediaImage.self) {
|
if let imageReference = iconImageReferenceAndRepresentation.0.concrete(TelegramMediaImage.self) {
|
||||||
updateIconImageSignal = chatWebpageSnippetPhoto(account: item.context.account, photoReference: imageReference)
|
updateIconImageSignal = chatWebpageSnippetPhoto(account: item.context.account, photoReference: imageReference)
|
||||||
} else if let fileReference = iconImageReferenceAndRepresentation.0.concrete(TelegramMediaFile.self) {
|
} else if let fileReference = iconImageReferenceAndRepresentation.0.concrete(TelegramMediaFile.self) {
|
||||||
updateIconImageSignal = chatWebpageSnippetFile(account: item.context.account, fileReference: fileReference, representation: iconImageReferenceAndRepresentation.1)
|
updateIconImageSignal = chatWebpageSnippetFile(account: item.context.account, mediaReference: fileReference.abstract, representation: iconImageReferenceAndRepresentation.1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateIconImageSignal = .complete()
|
updateIconImageSignal = .complete()
|
||||||
|
@ -11,9 +11,10 @@ extern "C" {
|
|||||||
|
|
||||||
bool MTLogEnabled();
|
bool MTLogEnabled();
|
||||||
void MTLog(NSString *format, ...);
|
void MTLog(NSString *format, ...);
|
||||||
|
void MTLogWithPrefix(NSString *(^getLogPrefix)(), NSString *format, ...);
|
||||||
void MTShortLog(NSString *format, ...);
|
void MTShortLog(NSString *format, ...);
|
||||||
void MTLogSetLoggingFunction(void (*function)(NSString *, va_list args));
|
void MTLogSetLoggingFunction(void (*function)(NSString *));
|
||||||
void MTLogSetShortLoggingFunction(void (*function)(NSString *, va_list args));
|
void MTLogSetShortLoggingFunction(void (*function)(NSString *));
|
||||||
void MTLogSetEnabled(bool);
|
void MTLogSetEnabled(bool);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -53,6 +53,8 @@
|
|||||||
@property (nonatomic) id requiredAuthToken;
|
@property (nonatomic) id requiredAuthToken;
|
||||||
@property (nonatomic) NSInteger authTokenMasterDatacenterId;
|
@property (nonatomic) NSInteger authTokenMasterDatacenterId;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSString *(^getLogPrefix)();
|
||||||
|
|
||||||
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo requiredAuthToken:(id)requiredAuthToken authTokenMasterDatacenterId:(NSInteger)authTokenMasterDatacenterId;
|
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo requiredAuthToken:(id)requiredAuthToken authTokenMasterDatacenterId:(NSInteger)authTokenMasterDatacenterId;
|
||||||
|
|
||||||
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
|
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
|
||||||
|
@ -43,8 +43,9 @@
|
|||||||
@property (nonatomic, strong, readonly) MTSocksProxySettings * _Nullable proxySettings;
|
@property (nonatomic, strong, readonly) MTSocksProxySettings * _Nullable proxySettings;
|
||||||
@property (nonatomic) bool simultaneousTransactionsEnabled;
|
@property (nonatomic) bool simultaneousTransactionsEnabled;
|
||||||
@property (nonatomic) bool reportTransportConnectionContextUpdateStates;
|
@property (nonatomic) bool reportTransportConnectionContextUpdateStates;
|
||||||
|
@property (nonatomic, strong) NSString * _Nullable (^ _Nullable getLogPrefix)();
|
||||||
|
|
||||||
- (instancetype _Nonnull)initWithDelegate:(id<MTTransportDelegate> _Nullable)delegate context:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes proxySettings:(MTSocksProxySettings * _Null_unspecified)proxySettings usageCalculationInfo:(MTNetworkUsageCalculationInfo * _Nullable)usageCalculationInfo;
|
- (instancetype _Nonnull)initWithDelegate:(id<MTTransportDelegate> _Nullable)delegate context:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes proxySettings:(MTSocksProxySettings * _Null_unspecified)proxySettings usageCalculationInfo:(MTNetworkUsageCalculationInfo * _Nullable)usageCalculationInfo getLogPrefix:(NSString * _Nullable (^ _Nullable)())getLogPrefix;
|
||||||
|
|
||||||
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo * _Null_unspecified)usageCalculationInfo;
|
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo * _Null_unspecified)usageCalculationInfo;
|
||||||
|
|
||||||
|
@ -151,6 +151,8 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
|||||||
@property (nonatomic) bool useTcpNodelay;
|
@property (nonatomic) bool useTcpNodelay;
|
||||||
@property (nonatomic, strong) MTNetworkUsageCalculationInfo *usageCalculationInfo;
|
@property (nonatomic, strong) MTNetworkUsageCalculationInfo *usageCalculationInfo;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSString *(^getLogPrefix)();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GCDAsyncSocket uses the standard delegate paradigm,
|
* GCDAsyncSocket uses the standard delegate paradigm,
|
||||||
* but executes all delegate callbacks on a given delegate dispatch queue.
|
* but executes all delegate callbacks on a given delegate dispatch queue.
|
||||||
|
@ -2468,7 +2468,7 @@ enum GCDAsyncSocketConfig
|
|||||||
freeifaddrs(ifaddr);
|
freeifaddrs(ifaddr);
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"Connection time: %f ms, interface: %@", (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0f, isWifi ? @"Wifi" : @"WAN");
|
MTLogWithPrefix(_getLogPrefix, @"Connection time: %f ms, interface: %@", (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0f, isWifi ? @"Wifi" : @"WAN");
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch_async(socketQueue, ^{ @autoreleasepool {
|
dispatch_async(socketQueue, ^{ @autoreleasepool {
|
||||||
|
@ -95,7 +95,7 @@
|
|||||||
MTPayloadData payloadData;
|
MTPayloadData payloadData;
|
||||||
NSData *data = [self payloadData:&payloadData context:context address:address];
|
NSData *data = [self payloadData:&payloadData context:context address:address];
|
||||||
|
|
||||||
MTTcpConnection *connection = [[MTTcpConnection alloc] initWithContext:context datacenterId:datacenterId scheme:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:address media:false] interface:nil usageCalculationInfo:nil];
|
MTTcpConnection *connection = [[MTTcpConnection alloc] initWithContext:context datacenterId:datacenterId scheme:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:address media:false] interface:nil usageCalculationInfo:nil getLogPrefix:nil];
|
||||||
__weak MTTcpConnection *weakConnection = connection;
|
__weak MTTcpConnection *weakConnection = connection;
|
||||||
connection.connectionOpened = ^
|
connection.connectionOpened = ^
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#import <MtProtoKit/MTLogging.h>
|
#import <MtProtoKit/MTLogging.h>
|
||||||
|
|
||||||
static void (*loggingFunction)(NSString *, va_list args) = NULL;
|
static void (*loggingFunction)(NSString *) = NULL;
|
||||||
static void (*shortLoggingFunction)(NSString *, va_list args) = NULL;
|
static void (*shortLoggingFunction)(NSString *) = NULL;
|
||||||
static bool MTLogEnabledValue = true;
|
static bool MTLogEnabledValue = true;
|
||||||
|
|
||||||
bool MTLogEnabled() {
|
bool MTLogEnabled() {
|
||||||
@ -12,7 +12,24 @@ void MTLog(NSString *format, ...) {
|
|||||||
va_list L;
|
va_list L;
|
||||||
va_start(L, format);
|
va_start(L, format);
|
||||||
if (loggingFunction != NULL) {
|
if (loggingFunction != NULL) {
|
||||||
loggingFunction(format, L);
|
NSString *string = [[NSString alloc] initWithFormat:format arguments:L];
|
||||||
|
loggingFunction(string);
|
||||||
|
}
|
||||||
|
va_end(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MTLogWithPrefix(NSString *(^getLogPrefix)(), NSString *format, ...) {
|
||||||
|
va_list L;
|
||||||
|
va_start(L, format);
|
||||||
|
if (loggingFunction != NULL) {
|
||||||
|
NSString *string = [[NSString alloc] initWithFormat:format arguments:L];
|
||||||
|
if (getLogPrefix) {
|
||||||
|
NSString *prefix = getLogPrefix();
|
||||||
|
if (prefix) {
|
||||||
|
string = [prefix stringByAppendingString:string];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loggingFunction(string);
|
||||||
}
|
}
|
||||||
va_end(L);
|
va_end(L);
|
||||||
}
|
}
|
||||||
@ -21,16 +38,17 @@ void MTShortLog(NSString *format, ...) {
|
|||||||
va_list L;
|
va_list L;
|
||||||
va_start(L, format);
|
va_start(L, format);
|
||||||
if (shortLoggingFunction != NULL) {
|
if (shortLoggingFunction != NULL) {
|
||||||
shortLoggingFunction(format, L);
|
NSString *string = [[NSString alloc] initWithFormat:format arguments:L];
|
||||||
|
shortLoggingFunction(string);
|
||||||
}
|
}
|
||||||
va_end(L);
|
va_end(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MTLogSetLoggingFunction(void (*function)(NSString *, va_list args)) {
|
void MTLogSetLoggingFunction(void (*function)(NSString *)) {
|
||||||
loggingFunction = function;
|
loggingFunction = function;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MTLogSetShortLoggingFunction(void (*function)(NSString *, va_list args)) {
|
void MTLogSetShortLoggingFunction(void (*function)(NSString *)) {
|
||||||
shortLoggingFunction = function;
|
shortLoggingFunction = function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
if ((_mtState & MTProtoStatePaused) == 0)
|
if ((_mtState & MTProtoStatePaused) == 0)
|
||||||
{
|
{
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p pause]", self, _context);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p pause]", self, _context);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p pause]", self, _context);
|
MTShortLog(@"[MTProto#%p@%p pause]", self, _context);
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
if (_mtState & MTProtoStatePaused)
|
if (_mtState & MTProtoStatePaused)
|
||||||
{
|
{
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p resume]", self, _context);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p resume]", self, _context);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p resume]", self, _context);
|
MTShortLog(@"[MTProto#%p@%p resume]", self, _context);
|
||||||
|
|
||||||
@ -274,7 +274,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
[[MTProto managerQueue] dispatchOnQueue:^
|
[[MTProto managerQueue] dispatchOnQueue:^
|
||||||
{
|
{
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p changing transport %@#%p to %@#%p]", self, _context, [_transport class] == nil ? @"" : NSStringFromClass([_transport class]), _transport, [transport class] == nil ? @"" : NSStringFromClass([transport class]), transport);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p changing transport %@#%p to %@#%p]", self, _context, [_transport class] == nil ? @"" : NSStringFromClass([_transport class]), _transport, [transport class] == nil ? @"" : NSStringFromClass([transport class]), transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
[self allTransactionsMayHaveFailed];
|
[self allTransactionsMayHaveFailed];
|
||||||
@ -331,7 +331,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(transportSchemes.count != 0);
|
assert(transportSchemes.count != 0);
|
||||||
MTTransport *transport = [[MTTcpTransport alloc] initWithDelegate:self context:_context datacenterId:_datacenterId schemes:transportSchemes proxySettings:_context.apiEnvironment.socksProxySettings usageCalculationInfo:_usageCalculationInfo];
|
MTTransport *transport = [[MTTcpTransport alloc] initWithDelegate:self context:_context datacenterId:_datacenterId schemes:transportSchemes proxySettings:_context.apiEnvironment.socksProxySettings usageCalculationInfo:_usageCalculationInfo getLogPrefix:_getLogPrefix];
|
||||||
|
|
||||||
[self setTransport:transport];
|
[self setTransport:transport];
|
||||||
}
|
}
|
||||||
@ -343,7 +343,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
[[MTProto managerQueue] dispatchOnQueue:^
|
[[MTProto managerQueue] dispatchOnQueue:^
|
||||||
{
|
{
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p resetting session]", self, _context);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p resetting session]", self, _context);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p resetting session]", self, _context);
|
MTShortLog(@"[MTProto#%p@%p resetting session]", self, _context);
|
||||||
|
|
||||||
@ -383,9 +383,9 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
if (!alreadySyncing)
|
if (!alreadySyncing)
|
||||||
{
|
{
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p begin time sync]", self, _context);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p begin time sync]", self, _context);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p begin time sync]", self, _context);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p begin time sync]", self, _context);
|
||||||
|
|
||||||
MTTimeSyncMessageService *timeSyncService = [[MTTimeSyncMessageService alloc] init];
|
MTTimeSyncMessageService *timeSyncService = [[MTTimeSyncMessageService alloc] init];
|
||||||
timeSyncService.delegate = self;
|
timeSyncService.delegate = self;
|
||||||
@ -416,7 +416,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, haveResendMessagesPending ? "yes" : "no");
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, haveResendMessagesPending ? "yes" : "no");
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, haveResendMessagesPending ? "yes" : "no");
|
MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, haveResendMessagesPending ? "yes" : "no");
|
||||||
|
|
||||||
@ -467,7 +467,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
if (notifyAboutServiceTask)
|
if (notifyAboutServiceTask)
|
||||||
{
|
{
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "yes");
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "yes");
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "yes");
|
MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "yes");
|
||||||
|
|
||||||
@ -520,7 +520,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
if (!performingServiceTasks)
|
if (!performingServiceTasks)
|
||||||
{
|
{
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "no");
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "no");
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "no");
|
MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "no");
|
||||||
|
|
||||||
@ -682,7 +682,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p network state: %s]", self, _context, isNetworkAvailable ? "available" : "waiting");
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p network state: %s]", self, _context, isNetworkAvailable ? "available" : "waiting");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id<MTMessageService> messageService in _messageServices)
|
for (id<MTMessageService> messageService in _messageServices)
|
||||||
@ -713,7 +713,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p transport #%p connection state: %s]", self, _context, transport, isConnected ? "connected" : "connecting");
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p transport #%p connection state: %s]", self, _context, transport, isConnected ? "connected" : "connecting");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id<MTMessageService> messageService in _messageServices)
|
for (id<MTMessageService> messageService in _messageServices)
|
||||||
@ -740,7 +740,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p connection context update state: %s]", self, _context, isUpdatingConnectionContext ? "updating" : "up to date");
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p connection context update state: %s]", self, _context, isUpdatingConnectionContext ? "updating" : "up to date");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id<MTMessageService> messageService in _messageServices)
|
for (id<MTMessageService> messageService in _messageServices)
|
||||||
@ -1048,7 +1048,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
NSString *messageDescription = [self outgoingMessageDescription:outgoingMessage messageId:messageId messageSeqNo:messageSeqNo];
|
NSString *messageDescription = [self outgoingMessageDescription:outgoingMessage messageId:messageId messageSeqNo:messageSeqNo];
|
||||||
MTLog(@"[MTProto#%p@%p preparing %@]", self, _context, messageDescription);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p preparing %@]", self, _context, messageDescription);
|
||||||
}
|
}
|
||||||
NSString *shortMessageDescription = [self outgoingShortMessageDescription:outgoingMessage messageId:messageId messageSeqNo:messageSeqNo];
|
NSString *shortMessageDescription = [self outgoingShortMessageDescription:outgoingMessage messageId:messageId messageSeqNo:messageSeqNo];
|
||||||
MTShortLog(@"[MTProto#%p@%p preparing %@]", self, _context, shortMessageDescription);
|
MTShortLog(@"[MTProto#%p@%p preparing %@]", self, _context, shortMessageDescription);
|
||||||
@ -1094,7 +1094,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
if (monotonityViolated)
|
if (monotonityViolated)
|
||||||
{
|
{
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p client message id monotonity violated]", self, _context);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p client message id monotonity violated]", self, _context);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p client message id monotonity violated]", self, _context);
|
MTShortLog(@"[MTProto#%p@%p client message id monotonity violated]", self, _context);
|
||||||
|
|
||||||
@ -1283,7 +1283,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p transport did not accept transactions with messages (%@)]", self, _context, idsString);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p transport did not accept transactions with messages (%@)]", self, _context, idsString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
@ -1339,7 +1339,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
MTOutputStream *decryptedOs = [[MTOutputStream alloc] init];
|
MTOutputStream *decryptedOs = [[MTOutputStream alloc] init];
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p sending time fix ping (%" PRId64 "/%" PRId32 ", %" PRId64 ")]", self, _context, timeFixMessageId, timeFixSeqNo, _sessionInfo.sessionId);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p sending time fix ping (%" PRId64 "/%" PRId32 ", %" PRId64 ")]", self, _context, timeFixMessageId, timeFixSeqNo, _sessionInfo.sessionId);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p sending time fix ping (%" PRId64 "/%" PRId32 ", %" PRId64 ")]", self, _context, timeFixMessageId, timeFixSeqNo, _sessionInfo.sessionId);
|
MTShortLog(@"[MTProto#%p@%p sending time fix ping (%" PRId64 "/%" PRId32 ", %" PRId64 ")]", self, _context, timeFixMessageId, timeFixSeqNo, _sessionInfo.sessionId);
|
||||||
|
|
||||||
@ -1455,7 +1455,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
|||||||
[idsString appendFormat:@"%lld", [nMessageId longLongValue]];
|
[idsString appendFormat:@"%lld", [nMessageId longLongValue]];
|
||||||
}
|
}
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@" container (%" PRId64 ") of (%@), in %" PRId64 "", containerMessageId, idsString, sessionInfo.sessionId);
|
MTLogWithPrefix(_getLogPrefix, @" container (%" PRId64 ") of (%@), in %" PRId64 "", containerMessageId, idsString, sessionInfo.sessionId);
|
||||||
}
|
}
|
||||||
MTShortLog(@" container (%" PRId64 ") of (%@), in %" PRId64 "", containerMessageId, idsString, sessionInfo.sessionId);
|
MTShortLog(@" container (%" PRId64 ") of (%@), in %" PRId64 "", containerMessageId, idsString, sessionInfo.sessionId);
|
||||||
}
|
}
|
||||||
@ -1931,7 +1931,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
|||||||
[data getBytes:&protocolErrorCode range:NSMakeRange(0, 4)];
|
[data getBytes:&protocolErrorCode range:NSMakeRange(0, 4)];
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p protocol error %" PRId32 "", self, _context, protocolErrorCode);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p protocol error %" PRId32 "", self, _context, protocolErrorCode);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p protocol error %" PRId32 "", self, _context, protocolErrorCode);
|
MTShortLog(@"[MTProto#%p@%p protocol error %" PRId32 "", self, _context, protocolErrorCode);
|
||||||
|
|
||||||
@ -1991,7 +1991,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
|||||||
MTRpcError *rpcError = maybeInternalMessage;
|
MTRpcError *rpcError = maybeInternalMessage;
|
||||||
if (rpcError.errorCode == 401 && [rpcError.errorDescription isEqualToString:@"AUTH_KEY_PERM_EMPTY"]) {
|
if (rpcError.errorCode == 401 && [rpcError.errorDescription isEqualToString:@"AUTH_KEY_PERM_EMPTY"]) {
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p received AUTH_KEY_PERM_EMPTY]", self, _context);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p received AUTH_KEY_PERM_EMPTY]", self, _context);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p received AUTH_KEY_PERM_EMPTY]", self, _context);
|
MTShortLog(@"[MTProto#%p@%p received AUTH_KEY_PERM_EMPTY]", self, _context);
|
||||||
[self handleMissingKey:scheme];
|
[self handleMissingKey:scheme];
|
||||||
@ -2004,7 +2004,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
|||||||
}
|
}
|
||||||
if (parseError) {
|
if (parseError) {
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p incoming data parse error, header: %d:%@]", self, _context, (int)decryptedData.length, dumpHexString(decryptedData, 128));
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p incoming data parse error, header: %d:%@]", self, _context, (int)decryptedData.length, dumpHexString(decryptedData, 128));
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p incoming data parse error, header: %d:%@]", self, _context, (int)decryptedData.length, dumpHexString(decryptedData, 128));
|
MTShortLog(@"[MTProto#%p@%p incoming data parse error, header: %d:%@]", self, _context, (int)decryptedData.length, dumpHexString(decryptedData, 128));
|
||||||
|
|
||||||
@ -2026,7 +2026,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p couldn't decrypt incoming data]", self, _context);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p couldn't decrypt incoming data]", self, _context);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p couldn't decrypt incoming data]", self, _context);
|
MTShortLog(@"[MTProto#%p@%p couldn't decrypt incoming data]", self, _context);
|
||||||
|
|
||||||
@ -2046,7 +2046,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
|||||||
|
|
||||||
if (_useUnauthorizedMode) {
|
if (_useUnauthorizedMode) {
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p don't handleMissingKey when useUnauthorizedMode]", self, _context);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p don't handleMissingKey when useUnauthorizedMode]", self, _context);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2055,7 +2055,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
|||||||
[self getAuthKeyForCurrentScheme:scheme createIfNeeded:false authInfoSelector:&authInfoSelector];
|
[self getAuthKeyForCurrentScheme:scheme createIfNeeded:false authInfoSelector:&authInfoSelector];
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p missing key %lld selector %d useExplicitAuthKey: %lld, canResetAuthData: %s]", self, _context, _validAuthInfo.authInfo.authKeyId, authInfoSelector, _useExplicitAuthKey.authKeyId, _canResetAuthData ? "true" : "false");
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p missing key %lld selector %d useExplicitAuthKey: %lld, canResetAuthData: %s]", self, _context, _validAuthInfo.authInfo.authKeyId, authInfoSelector, _useExplicitAuthKey.authKeyId, _canResetAuthData ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_useExplicitAuthKey != nil) {
|
if (_useExplicitAuthKey != nil) {
|
||||||
@ -2346,7 +2346,7 @@ static bool isDataEqualToDataConstTime(NSData *data1, NSData *data2) {
|
|||||||
if ([_sessionInfo messageProcessed:incomingMessage.messageId])
|
if ([_sessionInfo messageProcessed:incomingMessage.messageId])
|
||||||
{
|
{
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p received duplicate message %" PRId64 "]", self, _context, incomingMessage.messageId);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p received duplicate message %" PRId64 "]", self, _context, incomingMessage.messageId);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p received duplicate message %" PRId64 "]", self, _context, incomingMessage.messageId);
|
MTShortLog(@"[MTProto#%p@%p received duplicate message %" PRId64 "]", self, _context, incomingMessage.messageId);
|
||||||
[_sessionInfo scheduleMessageConfirmation:incomingMessage.messageId size:incomingMessage.size];
|
[_sessionInfo scheduleMessageConfirmation:incomingMessage.messageId size:incomingMessage.size];
|
||||||
@ -2358,7 +2358,7 @@ static bool isDataEqualToDataConstTime(NSData *data1, NSData *data2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p [%d] received %@]", self, _context, totalSize, [self incomingMessageDescription:incomingMessage]);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p [%d] received %@]", self, _context, totalSize, [self incomingMessageDescription:incomingMessage]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[_sessionInfo setMessageProcessed:incomingMessage.messageId];
|
[_sessionInfo setMessageProcessed:incomingMessage.messageId];
|
||||||
@ -2473,7 +2473,7 @@ static bool isDataEqualToDataConstTime(NSData *data1, NSData *data2) {
|
|||||||
int64_t requestMessageId = ((MTMsgDetailedResponseInfoMessage *)detailedInfoMessage).requestMessageId;
|
int64_t requestMessageId = ((MTMsgDetailedResponseInfoMessage *)detailedInfoMessage).requestMessageId;
|
||||||
|
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p detailed info %" PRId64 " is for %" PRId64 "", self, _context, incomingMessage.messageId, requestMessageId);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p detailed info %" PRId64 " is for %" PRId64 "", self, _context, incomingMessage.messageId, requestMessageId);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p detailed info %" PRId64 " is for %" PRId64 "", self, _context, incomingMessage.messageId, requestMessageId);
|
MTShortLog(@"[MTProto#%p@%p detailed info %" PRId64 " is for %" PRId64 "", self, _context, incomingMessage.messageId, requestMessageId);
|
||||||
|
|
||||||
@ -2496,7 +2496,7 @@ static bool isDataEqualToDataConstTime(NSData *data1, NSData *data2) {
|
|||||||
{
|
{
|
||||||
[self requestMessageWithId:detailedInfoMessage.responseMessageId];
|
[self requestMessageWithId:detailedInfoMessage.responseMessageId];
|
||||||
if (MTLogEnabled()) {
|
if (MTLogEnabled()) {
|
||||||
MTLog(@"[MTProto#%p@%p will request message %" PRId64 "", self, _context, detailedInfoMessage.responseMessageId);
|
MTLogWithPrefix(_getLogPrefix, @"[MTProto#%p@%p will request message %" PRId64 "", self, _context, detailedInfoMessage.responseMessageId);
|
||||||
}
|
}
|
||||||
MTShortLog(@"[MTProto#%p@%p will request message %" PRId64 "", self, _context, detailedInfoMessage.responseMessageId);
|
MTShortLog(@"[MTProto#%p@%p will request message %" PRId64 "", self, _context, detailedInfoMessage.responseMessageId);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
|
|
||||||
MTContext *proxyContext = [[MTContext alloc] initWithSerialization:context.serialization encryptionProvider:context.encryptionProvider apiEnvironment:[[context apiEnvironment] withUpdatedSocksProxySettings:settings] isTestingEnvironment:context.isTestingEnvironment useTempAuthKeys:false];
|
MTContext *proxyContext = [[MTContext alloc] initWithSerialization:context.serialization encryptionProvider:context.encryptionProvider apiEnvironment:[[context apiEnvironment] withUpdatedSocksProxySettings:settings] isTestingEnvironment:context.isTestingEnvironment useTempAuthKeys:false];
|
||||||
|
|
||||||
MTTcpConnection *connection = [[MTTcpConnection alloc] initWithContext:proxyContext datacenterId:datacenterId scheme:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpConnection class] address:address media:false] interface:nil usageCalculationInfo:nil];
|
MTTcpConnection *connection = [[MTTcpConnection alloc] initWithContext:proxyContext datacenterId:datacenterId scheme:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpConnection class] address:address media:false] interface:nil usageCalculationInfo:nil getLogPrefix:nil];
|
||||||
__weak MTTcpConnection *weakConnection = connection;
|
__weak MTTcpConnection *weakConnection = connection;
|
||||||
__block NSTimeInterval startTime = CFAbsoluteTimeGetCurrent();
|
__block NSTimeInterval startTime = CFAbsoluteTimeGetCurrent();
|
||||||
connection.connectionOpened = ^ {
|
connection.connectionOpened = ^ {
|
||||||
|
@ -32,9 +32,11 @@
|
|||||||
@property (nonatomic, strong, readonly) MTTransportScheme *scheme;
|
@property (nonatomic, strong, readonly) MTTransportScheme *scheme;
|
||||||
@property (nonatomic, strong, readonly) NSString *interface;
|
@property (nonatomic, strong, readonly) NSString *interface;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSString *(^getLogPrefix)();
|
||||||
|
|
||||||
+ (MTQueue *)tcpQueue;
|
+ (MTQueue *)tcpQueue;
|
||||||
|
|
||||||
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId scheme:(MTTransportScheme *)scheme interface:(NSString *)interface usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
|
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId scheme:(MTTransportScheme *)scheme interface:(NSString *)interface usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo getLogPrefix:(NSString *(^)())getLogPrefix;
|
||||||
|
|
||||||
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
|
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
|
||||||
|
|
||||||
|
@ -689,7 +689,7 @@ struct ctr_state {
|
|||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId scheme:(MTTransportScheme *)scheme interface:(NSString *)interface usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo
|
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId scheme:(MTTransportScheme *)scheme interface:(NSString *)interface usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo getLogPrefix:(NSString *(^)())getLogPrefix
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
NSAssert(scheme != nil, @"scheme should not be nil");
|
NSAssert(scheme != nil, @"scheme should not be nil");
|
||||||
@ -700,6 +700,8 @@ struct ctr_state {
|
|||||||
{
|
{
|
||||||
_internalId = [[MTInternalId(MTTcpConnection) alloc] init];
|
_internalId = [[MTInternalId(MTTcpConnection) alloc] init];
|
||||||
|
|
||||||
|
_getLogPrefix = [getLogPrefix copy];
|
||||||
|
|
||||||
_encryptionProvider = context.encryptionProvider;
|
_encryptionProvider = context.encryptionProvider;
|
||||||
|
|
||||||
_scheme = scheme;
|
_scheme = scheme;
|
||||||
@ -799,6 +801,7 @@ struct ctr_state {
|
|||||||
if (_socket == nil)
|
if (_socket == nil)
|
||||||
{
|
{
|
||||||
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:[[MTTcpConnection tcpQueue] nativeQueue]];
|
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:[[MTTcpConnection tcpQueue] nativeQueue]];
|
||||||
|
_socket.getLogPrefix = _getLogPrefix;
|
||||||
_socket.usageCalculationInfo = _usageCalculationInfo;
|
_socket.usageCalculationInfo = _usageCalculationInfo;
|
||||||
|
|
||||||
NSString *addressIp = _scheme.address.ip;
|
NSString *addressIp = _scheme.address.ip;
|
||||||
|
@ -82,7 +82,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
|
|||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithDelegate:(id<MTTransportDelegate>)delegate context:(MTContext *)context datacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes proxySettings:(MTSocksProxySettings *)proxySettings usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo
|
- (instancetype)initWithDelegate:(id<MTTransportDelegate>)delegate context:(MTContext *)context datacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes proxySettings:(MTSocksProxySettings *)proxySettings usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo getLogPrefix:(NSString *(^)())getLogPrefix
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
NSAssert(context != nil, @"context should not be nil");
|
NSAssert(context != nil, @"context should not be nil");
|
||||||
@ -90,7 +90,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
|
|||||||
NSAssert(schemes.count != 0, @"schemes should not be empty");
|
NSAssert(schemes.count != 0, @"schemes should not be empty");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
self = [super initWithDelegate:delegate context:context datacenterId:datacenterId schemes:schemes proxySettings:proxySettings usageCalculationInfo:usageCalculationInfo];
|
self = [super initWithDelegate:delegate context:context datacenterId:datacenterId schemes:schemes proxySettings:proxySettings usageCalculationInfo:usageCalculationInfo getLogPrefix:getLogPrefix];
|
||||||
if (self != nil)
|
if (self != nil)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
@ -196,7 +196,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
|
|||||||
[self startConnectionWatchdogTimer:scheme];
|
[self startConnectionWatchdogTimer:scheme];
|
||||||
[self startSleepWatchdogTimer];
|
[self startSleepWatchdogTimer];
|
||||||
|
|
||||||
transportContext.connection = [[MTTcpConnection alloc] initWithContext:context datacenterId:_datacenterId scheme:scheme interface:nil usageCalculationInfo:_usageCalculationInfo];
|
transportContext.connection = [[MTTcpConnection alloc] initWithContext:context datacenterId:_datacenterId scheme:scheme interface:nil usageCalculationInfo:_usageCalculationInfo getLogPrefix:self.getLogPrefix];
|
||||||
transportContext.connection.delegate = self;
|
transportContext.connection.delegate = self;
|
||||||
[transportContext.connection start];
|
[transportContext.connection start];
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
@implementation MTTransport
|
@implementation MTTransport
|
||||||
|
|
||||||
- (instancetype)initWithDelegate:(id<MTTransportDelegate>)delegate context:(MTContext *)context datacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes proxySettings:(MTSocksProxySettings *)proxySettings usageCalculationInfo:(MTNetworkUsageCalculationInfo *)__unused usageCalculationInfo
|
- (instancetype)initWithDelegate:(id<MTTransportDelegate>)delegate context:(MTContext *)context datacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes proxySettings:(MTSocksProxySettings *)proxySettings usageCalculationInfo:(MTNetworkUsageCalculationInfo *)__unused usageCalculationInfo getLogPrefix:(NSString * _Nullable (^ _Nullable)())getLogPrefix
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
NSAssert(context != nil, @"context should not be nil");
|
NSAssert(context != nil, @"context should not be nil");
|
||||||
@ -25,6 +25,7 @@
|
|||||||
_context = context;
|
_context = context;
|
||||||
_datacenterId = datacenterId;
|
_datacenterId = datacenterId;
|
||||||
_proxySettings = proxySettings;
|
_proxySettings = proxySettings;
|
||||||
|
_getLogPrefix = [getLogPrefix copy];
|
||||||
|
|
||||||
_networkAvailability = [[MTNetworkAvailability alloc] initWithDelegate:self];
|
_networkAvailability = [[MTNetworkAvailability alloc] initWithDelegate:self];
|
||||||
|
|
||||||
|
@ -13,15 +13,15 @@ void setBridgingShortTraceFunction(void (*f)(NSString *, NSString *)) {
|
|||||||
bridgingShortTrace = f;
|
bridgingShortTrace = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TGTelegramLoggingFunction(NSString *format, va_list args) {
|
static void TGTelegramLoggingFunction(NSString *format) {
|
||||||
if (bridgingTrace) {
|
if (bridgingTrace) {
|
||||||
bridgingTrace(@"MT", [[NSString alloc] initWithFormat:format arguments:args]);
|
bridgingTrace(@"MT", format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TGTelegramShortLoggingFunction(NSString *format, va_list args) {
|
static void TGTelegramShortLoggingFunction(NSString *format) {
|
||||||
if (bridgingShortTrace) {
|
if (bridgingShortTrace) {
|
||||||
bridgingShortTrace(@"MT", [[NSString alloc] initWithFormat:format arguments:args]);
|
bridgingShortTrace(@"MT", format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1768,7 +1768,7 @@ public func chatMessageWebFileCancelInteractiveFetch(account: Account, image: Te
|
|||||||
return account.postbox.mediaBox.cancelInteractiveResourceFetch(image.resource)
|
return account.postbox.mediaBox.cancelInteractiveResourceFetch(image.resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func chatWebpageSnippetFileData(account: Account, fileReference: FileMediaReference, resource: MediaResource) -> Signal<Data?, NoError> {
|
public func chatWebpageSnippetFileData(account: Account, mediaReference: AnyMediaReference, resource: MediaResource) -> Signal<Data?, NoError> {
|
||||||
let resourceData = account.postbox.mediaBox.resourceData(resource)
|
let resourceData = account.postbox.mediaBox.resourceData(resource)
|
||||||
|> map { next in
|
|> map { next in
|
||||||
return next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe)
|
return next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe)
|
||||||
@ -1782,7 +1782,7 @@ public func chatWebpageSnippetFileData(account: Account, fileReference: FileMedi
|
|||||||
}, completed: {
|
}, completed: {
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}))
|
}))
|
||||||
disposable.add(fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: fileReference.resourceReference(resource)).start())
|
disposable.add(fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: mediaReference.resourceReference(resource)).start())
|
||||||
return disposable
|
return disposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1810,8 +1810,8 @@ public func chatWebpageSnippetPhotoData(account: Account, photoReference: ImageM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func chatWebpageSnippetFile(account: Account, fileReference: FileMediaReference, representation: TelegramMediaImageRepresentation) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
public func chatWebpageSnippetFile(account: Account, mediaReference: AnyMediaReference, representation: TelegramMediaImageRepresentation) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
let signal = chatWebpageSnippetFileData(account: account, fileReference: fileReference, resource: representation.resource)
|
let signal = chatWebpageSnippetFileData(account: account, mediaReference: mediaReference, resource: representation.resource)
|
||||||
|
|
||||||
return signal |> map { fullSizeData in
|
return signal |> map { fullSizeData in
|
||||||
return { arguments in
|
return { arguments in
|
||||||
|
@ -97,6 +97,8 @@ swift_library(
|
|||||||
"//submodules/QrCodeUI:QrCodeUI",
|
"//submodules/QrCodeUI:QrCodeUI",
|
||||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||||
|
"//submodules/FetchManagerImpl:FetchManagerImpl",
|
||||||
|
"//submodules/ListMessageItem:ListMessageItem",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -0,0 +1,337 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import ItemListUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import AccountContext
|
||||||
|
import ReactionImageComponent
|
||||||
|
import WebPBinding
|
||||||
|
import FetchManagerImpl
|
||||||
|
import ListMessageItem
|
||||||
|
import ListSectionHeaderNode
|
||||||
|
|
||||||
|
private struct DownloadItem: Equatable {
|
||||||
|
let resourceId: MediaResourceId
|
||||||
|
let message: Message
|
||||||
|
let priority: FetchManagerPriorityKey
|
||||||
|
|
||||||
|
static func ==(lhs: DownloadItem, rhs: DownloadItem) -> Bool {
|
||||||
|
if lhs.resourceId != rhs.resourceId {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.message.id != rhs.message.id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.priority != rhs.priority {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class DownloadsControllerArguments {
|
||||||
|
let context: AccountContext
|
||||||
|
|
||||||
|
init(
|
||||||
|
context: AccountContext
|
||||||
|
) {
|
||||||
|
self.context = context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DownloadsControllerSection: Int32 {
|
||||||
|
case items
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DownloadsItemHeader: ListViewItemHeader {
|
||||||
|
public let id: ListViewItemNode.HeaderId
|
||||||
|
public let title: String
|
||||||
|
public let stickDirection: ListViewItemHeaderStickDirection = .top
|
||||||
|
public let stickOverInsets: Bool = true
|
||||||
|
public let theme: PresentationTheme
|
||||||
|
|
||||||
|
public let height: CGFloat = 28.0
|
||||||
|
|
||||||
|
public init(id: ListViewItemNode.HeaderId, title: String, theme: PresentationTheme) {
|
||||||
|
self.id = id
|
||||||
|
self.title = title
|
||||||
|
self.theme = theme
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combinesWith(other: ListViewItemHeader) -> Bool {
|
||||||
|
if let other = other as? DownloadsItemHeader, other.id == self.id {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
|
||||||
|
return DownloadsItemHeaderNode(title: self.title, theme: self.theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
|
||||||
|
(node as? DownloadsItemHeaderNode)?.update(title: self.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DownloadsItemHeaderNode: ListViewItemHeaderNode {
|
||||||
|
private var title: String
|
||||||
|
private var theme: PresentationTheme
|
||||||
|
|
||||||
|
private var validLayout: (size: CGSize, leftInset: CGFloat, rightInset: CGFloat)?
|
||||||
|
|
||||||
|
private let sectionHeaderNode: ListSectionHeaderNode
|
||||||
|
|
||||||
|
public init(title: String, theme: PresentationTheme) {
|
||||||
|
self.title = title
|
||||||
|
self.theme = theme
|
||||||
|
|
||||||
|
self.sectionHeaderNode = ListSectionHeaderNode(theme: theme)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.sectionHeaderNode.title = title
|
||||||
|
self.sectionHeaderNode.action = nil
|
||||||
|
|
||||||
|
self.addSubnode(self.sectionHeaderNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateTheme(theme: PresentationTheme) {
|
||||||
|
self.theme = theme
|
||||||
|
self.sectionHeaderNode.updateTheme(theme: theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(title: String) {
|
||||||
|
self.sectionHeaderNode.title = title
|
||||||
|
self.sectionHeaderNode.action = nil
|
||||||
|
|
||||||
|
if let (size, leftInset, rightInset) = self.validLayout {
|
||||||
|
self.sectionHeaderNode.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) {
|
||||||
|
self.validLayout = (size, leftInset, rightInset)
|
||||||
|
self.sectionHeaderNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
|
self.sectionHeaderNode.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset)
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func animateRemoved(duration: Double) {
|
||||||
|
self.alpha = 0.0
|
||||||
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DownloadsControllerEntry: ItemListNodeEntry {
|
||||||
|
enum StableId: Hashable {
|
||||||
|
case item(MediaResourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
case item(item: DownloadItem)
|
||||||
|
|
||||||
|
var section: ItemListSectionId {
|
||||||
|
switch self {
|
||||||
|
case .item:
|
||||||
|
return DownloadsControllerSection.items.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stableId: StableId {
|
||||||
|
switch self {
|
||||||
|
case let .item(item):
|
||||||
|
return .item(item.resourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sortId: FetchManagerPriorityKey {
|
||||||
|
switch self {
|
||||||
|
case let .item(item):
|
||||||
|
return item.priority
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: DownloadsControllerEntry, rhs: DownloadsControllerEntry) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .item(item):
|
||||||
|
if case .item(item) = rhs {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func <(lhs: DownloadsControllerEntry, rhs: DownloadsControllerEntry) -> Bool {
|
||||||
|
return lhs.sortId < rhs.sortId
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
|
let arguments = arguments as! DownloadsControllerArguments
|
||||||
|
let _ = arguments
|
||||||
|
switch self {
|
||||||
|
case let .item(item):
|
||||||
|
let listInteraction = ListMessageItemInteraction(openMessage: { message, mode -> Bool in
|
||||||
|
return false
|
||||||
|
}, openMessageContextMenu: { message, _, node, rect, gesture in
|
||||||
|
}, toggleMessagesSelection: { messageId, selected in
|
||||||
|
}, openUrl: { url, _, _, message in
|
||||||
|
}, openInstantPage: { message, data in
|
||||||
|
}, longTap: { action, message in
|
||||||
|
}, getHiddenMedia: {
|
||||||
|
return [:]
|
||||||
|
})
|
||||||
|
|
||||||
|
let presentationData = arguments.context.sharedContext.currentPresentationData.with({ $0 })
|
||||||
|
|
||||||
|
return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: arguments.context, chatLocation: .peer(item.message.id.peerId), interaction: listInteraction, message: item.message, selection: .none, displayHeader: false, customHeader: nil/*DownloadsItemHeader(id: ListViewItemNode.HeaderId(space: 0, id: item.message.id.peerId), title: item.message.peers[item.message.id.peerId].flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? "", theme: presentationData.theme)*/, hintIsLink: false, isGlobalSearchResult: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct DownloadsControllerState: Equatable {
|
||||||
|
var hasReaction: Bool = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func downloadsControllerEntries(
|
||||||
|
presentationData: PresentationData,
|
||||||
|
items: [DownloadItem],
|
||||||
|
state: DownloadsControllerState
|
||||||
|
) -> [DownloadsControllerEntry] {
|
||||||
|
var entries: [DownloadsControllerEntry] = []
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
for item in items {
|
||||||
|
entries.append(.item(
|
||||||
|
item: item
|
||||||
|
))
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
public func downloadsController(
|
||||||
|
context: AccountContext,
|
||||||
|
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil
|
||||||
|
) -> ViewController {
|
||||||
|
let statePromise = ValuePromise(DownloadsControllerState(), ignoreRepeated: true)
|
||||||
|
let stateValue = Atomic(value: DownloadsControllerState())
|
||||||
|
let updateState: ((DownloadsControllerState) -> DownloadsControllerState) -> Void = { f in
|
||||||
|
statePromise.set(stateValue.modify { f($0) })
|
||||||
|
}
|
||||||
|
let _ = updateState
|
||||||
|
|
||||||
|
var dismissImpl: (() -> Void)?
|
||||||
|
let _ = dismissImpl
|
||||||
|
|
||||||
|
let actionsDisposable = DisposableSet()
|
||||||
|
|
||||||
|
let arguments = DownloadsControllerArguments(
|
||||||
|
context: context
|
||||||
|
)
|
||||||
|
|
||||||
|
let settings = context.account.postbox.preferencesView(keys: [PreferencesKeys.reactionSettings])
|
||||||
|
|> map { preferencesView -> ReactionSettings in
|
||||||
|
let reactionSettings: ReactionSettings
|
||||||
|
if let entry = preferencesView.values[PreferencesKeys.reactionSettings], let value = entry.get(ReactionSettings.self) {
|
||||||
|
reactionSettings = value
|
||||||
|
} else {
|
||||||
|
reactionSettings = .default
|
||||||
|
}
|
||||||
|
return reactionSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
let downloadItems: Signal<[DownloadItem], NoError> = (context.fetchManager as! FetchManagerImpl).entriesSummary
|
||||||
|
|> mapToSignal { entries -> Signal<[DownloadItem], NoError> in
|
||||||
|
var itemSignals: [Signal<DownloadItem?, NoError>] = []
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
switch entry.id.locationKey {
|
||||||
|
case let .messageId(id):
|
||||||
|
itemSignals.append(context.account.postbox.transaction { transaction -> DownloadItem? in
|
||||||
|
if let message = transaction.getMessage(id) {
|
||||||
|
return DownloadItem(resourceId: entry.resourceReference.resource.id, message: message, priority: entry.priority)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return combineLatest(queue: .mainQueue(), itemSignals)
|
||||||
|
|> map { items -> [DownloadItem] in
|
||||||
|
return items.compactMap { $0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||||
|
let signal = combineLatest(queue: .mainQueue(),
|
||||||
|
presentationData,
|
||||||
|
statePromise.get(),
|
||||||
|
context.engine.stickers.availableReactions(),
|
||||||
|
settings,
|
||||||
|
downloadItems
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue
|
||||||
|
|> map { presentationData, state, availableReactions, settings, downloadItems -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
|
//TODO:localize
|
||||||
|
let title: String = "Downloads"
|
||||||
|
|
||||||
|
let entries = downloadsControllerEntries(
|
||||||
|
presentationData: presentationData,
|
||||||
|
items: downloadItems,
|
||||||
|
state: state
|
||||||
|
)
|
||||||
|
|
||||||
|
let controllerState = ItemListControllerState(
|
||||||
|
presentationData: ItemListPresentationData(presentationData),
|
||||||
|
title: .text(title),
|
||||||
|
leftNavigationButton: nil,
|
||||||
|
rightNavigationButton: nil,
|
||||||
|
backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back),
|
||||||
|
animateChanges: false
|
||||||
|
)
|
||||||
|
let listState = ItemListNodeState(
|
||||||
|
presentationData: ItemListPresentationData(presentationData),
|
||||||
|
entries: entries,
|
||||||
|
style: .plain,
|
||||||
|
animateChanges: true
|
||||||
|
)
|
||||||
|
|
||||||
|
return (controllerState, (listState, arguments))
|
||||||
|
}
|
||||||
|
|> afterDisposed {
|
||||||
|
actionsDisposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = ItemListController(context: context, state: signal)
|
||||||
|
|
||||||
|
controller.didScrollWithOffset = { [weak controller] offset, transition, _ in
|
||||||
|
guard let controller = controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
controller.forEachItemNode { itemNode in
|
||||||
|
if let itemNode = itemNode as? ReactionChatPreviewItemNode {
|
||||||
|
itemNode.standaloneReactionAnimation?.addRelativeContentOffset(CGPoint(x: 0.0, y: offset), transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dismissImpl = { [weak controller] in
|
||||||
|
guard let controller = controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
controller.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,17 @@ protocol TelegramCloudMediaResource: TelegramMediaResource {
|
|||||||
func apiInputLocation(fileReference: Data?) -> Api.InputFileLocation?
|
func apiInputLocation(fileReference: Data?) -> Api.InputFileLocation?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func extractMediaResourceDebugInfo(resource: MediaResource) -> String? {
|
||||||
|
if let resource = resource as? TelegramCloudMediaResource {
|
||||||
|
guard let inputLocation = resource.apiInputLocation(fileReference: nil) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return String(describing: inputLocation)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public protocol TelegramMultipartFetchableResource: TelegramMediaResource {
|
public protocol TelegramMultipartFetchableResource: TelegramMediaResource {
|
||||||
var datacenterId: Int { get }
|
var datacenterId: Int { get }
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
|||||||
let context: MTContext
|
let context: MTContext
|
||||||
let mtProto: MTProto
|
let mtProto: MTProto
|
||||||
let requestService: MTRequestMessageService
|
let requestService: MTRequestMessageService
|
||||||
|
private let logPrefix = Atomic<String?>(value: nil)
|
||||||
|
|
||||||
private var shouldKeepConnectionDisposable: Disposable?
|
private var shouldKeepConnectionDisposable: Disposable?
|
||||||
|
|
||||||
@ -59,6 +60,10 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.mtProto = MTProto(context: self.context, datacenterId: datacenterId, usageCalculationInfo: usageInfo, requiredAuthToken: requiredAuthToken, authTokenMasterDatacenterId: authTokenMasterDatacenterId)
|
self.mtProto = MTProto(context: self.context, datacenterId: datacenterId, usageCalculationInfo: usageInfo, requiredAuthToken: requiredAuthToken, authTokenMasterDatacenterId: authTokenMasterDatacenterId)
|
||||||
|
let logPrefix = self.logPrefix
|
||||||
|
self.mtProto.getLogPrefix = {
|
||||||
|
return logPrefix.with { $0 }
|
||||||
|
}
|
||||||
self.mtProto.cdn = isCdn
|
self.mtProto.cdn = isCdn
|
||||||
self.mtProto.useTempAuthKeys = self.context.useTempAuthKeys && !isCdn
|
self.mtProto.useTempAuthKeys = self.context.useTempAuthKeys && !isCdn
|
||||||
self.mtProto.media = isMedia
|
self.mtProto.media = isMedia
|
||||||
@ -375,7 +380,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true, failOnServerErrors: Bool = false) -> Signal<(Any, Double), (MTRpcError, Double)> {
|
func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true, failOnServerErrors: Bool = false, logPrefix: String = "") -> Signal<(Any, Double), (MTRpcError, Double)> {
|
||||||
let requestService = self.requestService
|
let requestService = self.requestService
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let request = MTRequest()
|
let request = MTRequest()
|
||||||
|
@ -256,6 +256,7 @@ swift_library(
|
|||||||
"//submodules/TabBarUI:TabBarUI",
|
"//submodules/TabBarUI:TabBarUI",
|
||||||
"//submodules/SoftwareVideo:SoftwareVideo",
|
"//submodules/SoftwareVideo:SoftwareVideo",
|
||||||
"//submodules/ManagedFile:ManagedFile",
|
"//submodules/ManagedFile:ManagedFile",
|
||||||
|
"//submodules/FetchManagerImpl:FetchManagerImpl",
|
||||||
] + select({
|
] + select({
|
||||||
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
||||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||||
|
@ -17,6 +17,7 @@ import TelegramBaseController
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import MeshAnimationCache
|
import MeshAnimationCache
|
||||||
|
import FetchManagerImpl
|
||||||
|
|
||||||
private final class DeviceSpecificContactImportContext {
|
private final class DeviceSpecificContactImportContext {
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
@ -26,6 +26,7 @@ import TelegramNotices
|
|||||||
import ReactionListContextMenuContent
|
import ReactionListContextMenuContent
|
||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
import Translate
|
import Translate
|
||||||
|
import DebugSettingsUI
|
||||||
|
|
||||||
private struct MessageContextMenuData {
|
private struct MessageContextMenuData {
|
||||||
let starStatus: Bool?
|
let starStatus: Bool?
|
||||||
@ -565,7 +566,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
return transaction.getCombinedPeerReadState(messages[0].id.peerId)
|
return transaction.getCombinedPeerReadState(messages[0].id.peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32, AvailableReactions?, TranslationSettings), NoError> = combineLatest(
|
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32, AvailableReactions?, TranslationSettings, LoggingSettings), NoError> = combineLatest(
|
||||||
loadLimits,
|
loadLimits,
|
||||||
loadStickerSaveStatusSignal,
|
loadStickerSaveStatusSignal,
|
||||||
loadResourceStatusSignal,
|
loadResourceStatusSignal,
|
||||||
@ -576,9 +577,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
readState,
|
readState,
|
||||||
ApplicationSpecificNotice.getMessageViewsPrivacyTips(accountManager: context.sharedContext.accountManager),
|
ApplicationSpecificNotice.getMessageViewsPrivacyTips(accountManager: context.sharedContext.accountManager),
|
||||||
context.engine.stickers.availableReactions(),
|
context.engine.stickers.availableReactions(),
|
||||||
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings, SharedDataKeys.loggingSettings])
|
||||||
)
|
)
|
||||||
|> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData, readState, messageViewsPrivacyTips, availableReactions, sharedData -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32, AvailableReactions?, TranslationSettings) in
|
|> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData, readState, messageViewsPrivacyTips, availableReactions, sharedData -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32, AvailableReactions?, TranslationSettings, LoggingSettings) in
|
||||||
let (limitsConfiguration, appConfig) = limitsAndAppConfig
|
let (limitsConfiguration, appConfig) = limitsAndAppConfig
|
||||||
var canEdit = false
|
var canEdit = false
|
||||||
if !isAction {
|
if !isAction {
|
||||||
@ -598,12 +599,19 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
translationSettings = TranslationSettings.defaultSettings
|
translationSettings = TranslationSettings.defaultSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions, translationSettings)
|
let loggingSettings: LoggingSettings
|
||||||
|
if let current = sharedData.entries[SharedDataKeys.loggingSettings]?.get(LoggingSettings.self) {
|
||||||
|
loggingSettings = current
|
||||||
|
} else {
|
||||||
|
loggingSettings = LoggingSettings.defaultSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions, translationSettings, loggingSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataSignal
|
return dataSignal
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions, translationSettings -> ContextController.Items in
|
|> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions, translationSettings, loggingSettings -> ContextController.Items in
|
||||||
var actions: [ContextMenuItem] = []
|
var actions: [ContextMenuItem] = []
|
||||||
|
|
||||||
var isPinnedMessages = false
|
var isPinnedMessages = false
|
||||||
@ -867,6 +875,32 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var downloadableMediaResourceInfos: [String] = []
|
||||||
|
for media in message.media {
|
||||||
|
if let file = media as? TelegramMediaFile {
|
||||||
|
if let info = extractMediaResourceDebugInfo(resource: file.resource) {
|
||||||
|
downloadableMediaResourceInfos.append(info)
|
||||||
|
}
|
||||||
|
} else if let image = media as? TelegramMediaImage {
|
||||||
|
for representation in image.representations {
|
||||||
|
if let info = extractMediaResourceDebugInfo(resource: representation.resource) {
|
||||||
|
downloadableMediaResourceInfos.append(info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loggingSettings.logToFile || loggingSettings.logToConsole) && !downloadableMediaResourceInfos.isEmpty {
|
||||||
|
actions.append(.action(ContextMenuActionItem(text: "Send Logs", icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor)
|
||||||
|
}, action: { _, f in
|
||||||
|
triggerDebugSendLogsUI(context: context, additionalInfo: "User has requested download logs for \(downloadableMediaResourceInfos)", pushController: { c in
|
||||||
|
controllerInteraction.navigationController()?.pushViewController(c)
|
||||||
|
})
|
||||||
|
f(.default)
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
var threadId: Int64?
|
var threadId: Int64?
|
||||||
var threadMessageCount: Int = 0
|
var threadMessageCount: Int = 0
|
||||||
if case .peer = chatPresentationInterfaceState.chatLocation, let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .group = channel.info {
|
if case .peer = chatPresentationInterfaceState.chatLocation, let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .group = channel.info {
|
||||||
|
@ -189,7 +189,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
if fileReference.media.isVideo {
|
if fileReference.media.isVideo {
|
||||||
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||||
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
||||||
updateImageSignal = chatWebpageSnippetFile(account: context.account, fileReference: fileReference, representation: iconImageRepresentation)
|
updateImageSignal = chatWebpageSnippetFile(account: context.account, mediaReference: fileReference.abstract, representation: iconImageRepresentation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,7 +457,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
} else if fileReference.media.isVideo {
|
} else if fileReference.media.isVideo {
|
||||||
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||||
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
||||||
updateImageSignal = chatWebpageSnippetFile(account: context.account, fileReference: fileReference, representation: iconImageRepresentation)
|
updateImageSignal = chatWebpageSnippetFile(account: context.account, mediaReference: fileReference.abstract, representation: iconImageRepresentation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -226,7 +226,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
if fileReference.media.isVideo {
|
if fileReference.media.isVideo {
|
||||||
updateImageSignal = chatMessageVideoThumbnail(account: self.context.account, fileReference: fileReference)
|
updateImageSignal = chatMessageVideoThumbnail(account: self.context.account, fileReference: fileReference)
|
||||||
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
||||||
updateImageSignal = chatWebpageSnippetFile(account: self.context.account, fileReference: fileReference, representation: iconImageRepresentation)
|
updateImageSignal = chatWebpageSnippetFile(account: self.context.account, mediaReference: fileReference.abstract, representation: iconImageRepresentation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import Postbox
|
|
@ -403,6 +403,7 @@ private enum PeerInfoSettingsSection {
|
|||||||
case avatar
|
case avatar
|
||||||
case edit
|
case edit
|
||||||
case proxy
|
case proxy
|
||||||
|
case downloads
|
||||||
case savedMessages
|
case savedMessages
|
||||||
case recentCalls
|
case recentCalls
|
||||||
case devices
|
case devices
|
||||||
@ -662,10 +663,14 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_SavedMessages, icon: PresentationResourcesSettings.savedMessages, action: {
|
//TODO:localize
|
||||||
|
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 0, text: "Downloads", icon: PresentationResourcesSettings.savedMessages, action: {
|
||||||
|
interaction.openSettings(.downloads)
|
||||||
|
}))
|
||||||
|
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_SavedMessages, icon: PresentationResourcesSettings.savedMessages, action: {
|
||||||
interaction.openSettings(.savedMessages)
|
interaction.openSettings(.savedMessages)
|
||||||
}))
|
}))
|
||||||
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.CallSettings_RecentCalls, icon: PresentationResourcesSettings.recentCalls, action: {
|
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 2, text: presentationData.strings.CallSettings_RecentCalls, icon: PresentationResourcesSettings.recentCalls, action: {
|
||||||
interaction.openSettings(.recentCalls)
|
interaction.openSettings(.recentCalls)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -680,10 +685,10 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
|||||||
devicesLabel = ""
|
devicesLabel = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 2, label: .text(devicesLabel), text: presentationData.strings.Settings_Devices, icon: PresentationResourcesSettings.devices, action: {
|
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 3, label: .text(devicesLabel), text: presentationData.strings.Settings_Devices, icon: PresentationResourcesSettings.devices, action: {
|
||||||
interaction.openSettings(.devices)
|
interaction.openSettings(.devices)
|
||||||
}))
|
}))
|
||||||
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 3, text: presentationData.strings.Settings_ChatFolders, icon: PresentationResourcesSettings.chatFolders, action: {
|
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 4, text: presentationData.strings.Settings_ChatFolders, icon: PresentationResourcesSettings.chatFolders, action: {
|
||||||
interaction.openSettings(.chatFolders)
|
interaction.openSettings(.chatFolders)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -5527,6 +5532,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil)
|
self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil)
|
||||||
case .proxy:
|
case .proxy:
|
||||||
self.controller?.push(proxySettingsController(context: self.context))
|
self.controller?.push(proxySettingsController(context: self.context))
|
||||||
|
case .downloads:
|
||||||
|
self.controller?.push(downloadsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData))
|
||||||
case .savedMessages:
|
case .savedMessages:
|
||||||
if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController {
|
if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController {
|
||||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.context.account.peerId)))
|
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.context.account.peerId)))
|
||||||
|
@ -196,7 +196,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
if fileReference.media.isVideo {
|
if fileReference.media.isVideo {
|
||||||
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||||
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
||||||
updateImageSignal = chatWebpageSnippetFile(account: context.account, fileReference: fileReference, representation: iconImageRepresentation)
|
updateImageSignal = chatWebpageSnippetFile(account: context.account, mediaReference: fileReference.abstract, representation: iconImageRepresentation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -556,7 +556,7 @@ final class WatchMediaHandler: WatchRequestHandler {
|
|||||||
imageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
imageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||||
roundVideo = fileReference.media.isInstantVideo
|
roundVideo = fileReference.media.isInstantVideo
|
||||||
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
||||||
imageSignal = chatWebpageSnippetFile(account: context.account, fileReference: fileReference, representation: iconImageRepresentation)
|
imageSignal = chatWebpageSnippetFile(account: context.account, mediaReference: fileReference.abstract, representation: iconImageRepresentation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user