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
|
||||
}
|
||||
|
||||
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/AccountContext:AccountContext",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/GZip:GZip"
|
||||
"//submodules/GZip:GZip",
|
||||
"//third-party/ZipArchive:ZipArchive",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -13,7 +13,7 @@ import PresentationDataUtils
|
||||
import OverlayStatusController
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
import GZip
|
||||
import ZipArchive
|
||||
|
||||
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
||||
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 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 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 fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
||||
@ -1055,3 +1091,61 @@ public func debugController(sharedContext: SharedAccountContext, context: Accoun
|
||||
}
|
||||
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 UniversalMediaPlayer
|
||||
|
||||
private struct FetchManagerLocationEntryId: Hashable {
|
||||
let location: FetchManagerLocation
|
||||
let resourceId: MediaResourceId
|
||||
let locationKey: FetchManagerLocationKey
|
||||
public struct FetchManagerLocationEntryId: Hashable {
|
||||
public let location: FetchManagerLocation
|
||||
public let resourceId: MediaResourceId
|
||||
public let locationKey: FetchManagerLocationKey
|
||||
|
||||
static func ==(lhs: FetchManagerLocationEntryId, rhs: FetchManagerLocationEntryId) -> Bool {
|
||||
public static func ==(lhs: FetchManagerLocationEntryId, rhs: FetchManagerLocationEntryId) -> Bool {
|
||||
if lhs.location != rhs.location {
|
||||
return false
|
||||
}
|
||||
@ -25,7 +25,7 @@ private struct FetchManagerLocationEntryId: Hashable {
|
||||
return true
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(self.resourceId.hashValue)
|
||||
hasher.combine(self.locationKey)
|
||||
}
|
||||
@ -116,7 +116,7 @@ private final class FetchManagerCategoryContext {
|
||||
private let postbox: Postbox
|
||||
private let storeManager: DownloadedMediaStoreManager?
|
||||
private let entryCompleted: (FetchManagerLocationEntryId) -> Void
|
||||
private let activeEntriesUpdated: () -> Void
|
||||
private let activeEntriesUpdated: ([FetchManagerEntrySummary]) -> Void
|
||||
|
||||
private var topEntryIdAndPriority: (FetchManagerLocationEntryId, FetchManagerPriorityKey)?
|
||||
private var entries: [FetchManagerLocationEntryId: FetchManagerLocationEntry] = [:]
|
||||
@ -133,7 +133,7 @@ private final class FetchManagerCategoryContext {
|
||||
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.storeManager = storeManager
|
||||
self.entryCompleted = entryCompleted
|
||||
@ -182,6 +182,7 @@ private final class FetchManagerCategoryContext {
|
||||
}
|
||||
|
||||
var activeContextsUpdated = false
|
||||
let _ = activeContextsUpdated
|
||||
|
||||
if self.maybeFindAndActivateNewTopEntry() {
|
||||
activeContextsUpdated = true
|
||||
@ -285,9 +286,7 @@ private final class FetchManagerCategoryContext {
|
||||
}
|
||||
}
|
||||
|
||||
if activeContextsUpdated {
|
||||
self.activeEntriesUpdated()
|
||||
}
|
||||
self.activeEntriesUpdated(self.entries.values.compactMap(FetchManagerEntrySummary.init).sorted(by: { $0.priority < $1.priority }))
|
||||
}
|
||||
|
||||
func maybeFindAndActivateNewTopEntry() -> Bool {
|
||||
@ -406,8 +405,12 @@ private final class FetchManagerCategoryContext {
|
||||
}
|
||||
}
|
||||
|
||||
var entriesRemoved = false
|
||||
let _ = entriesRemoved
|
||||
|
||||
if let _ = self.entries[id] {
|
||||
self.entries.removeValue(forKey: id)
|
||||
entriesRemoved = true
|
||||
|
||||
if let statusContext = self.statusContexts[id] {
|
||||
if statusContext.hasEntry {
|
||||
@ -426,6 +429,7 @@ private final class FetchManagerCategoryContext {
|
||||
}
|
||||
|
||||
var activeContextsUpdated = false
|
||||
let _ = activeContextsUpdated
|
||||
|
||||
if let activeContext = self.activeContexts[id] {
|
||||
activeContext.disposable?.dispose()
|
||||
@ -442,9 +446,7 @@ private final class FetchManagerCategoryContext {
|
||||
activeContextsUpdated = true
|
||||
}
|
||||
|
||||
if activeContextsUpdated {
|
||||
self.activeEntriesUpdated()
|
||||
}
|
||||
self.activeEntriesUpdated(self.entries.values.compactMap(FetchManagerEntrySummary.init).sorted(by: { $0.priority < $1.priority }))
|
||||
}
|
||||
|
||||
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 let queue = Queue.mainQueue()
|
||||
private let postbox: Postbox
|
||||
@ -486,7 +507,12 @@ public final class FetchManagerImpl: FetchManager {
|
||||
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.storeManager = storeManager
|
||||
}
|
||||
@ -519,7 +545,7 @@ public final class FetchManagerImpl: FetchManager {
|
||||
context.cancelEntry(id, isCompleted: true)
|
||||
})
|
||||
}
|
||||
}, activeEntriesUpdated: { [weak self] in
|
||||
}, activeEntriesUpdated: { [weak self] entries in
|
||||
queue.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -532,6 +558,8 @@ public final class FetchManagerImpl: FetchManager {
|
||||
}
|
||||
}
|
||||
strongSelf.hasUserInitiatedEntriesValue.set(hasActiveUserInitiatedEntries)
|
||||
|
||||
strongSelf.entriesSummaryValue.set(entries)
|
||||
}
|
||||
})
|
||||
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):
|
||||
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 {
|
||||
return (.single({ _ in return nil }), CGSize(width: 128.0, height: 128.0))
|
||||
}
|
||||
|
@ -95,30 +95,30 @@ private struct FetchControls {
|
||||
}
|
||||
|
||||
private enum FileIconImage: Equatable {
|
||||
case imageRepresentation(TelegramMediaFile, TelegramMediaImageRepresentation)
|
||||
case imageRepresentation(Media, TelegramMediaImageRepresentation)
|
||||
case albumArt(TelegramMediaFile, SharedMediaPlaybackAlbumArt)
|
||||
case roundVideo(TelegramMediaFile)
|
||||
|
||||
static func ==(lhs: FileIconImage, rhs: FileIconImage) -> Bool {
|
||||
switch lhs {
|
||||
case let .imageRepresentation(file, value):
|
||||
if case .imageRepresentation(file, value) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .albumArt(file, value):
|
||||
if case .albumArt(file, value) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .roundVideo(file):
|
||||
if case .roundVideo(file) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .imageRepresentation(lhsMedia, lhsValue):
|
||||
if case let .imageRepresentation(rhsMedia, rhsValue) = rhs, lhsMedia.isEqual(to: rhsMedia), lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .albumArt(file, value):
|
||||
if case .albumArt(file, value) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .roundVideo(file):
|
||||
if case .roundVideo(file) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,6 +155,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
|
||||
private let offsetContainerNode: ASDisplayNode
|
||||
|
||||
private var backgroundNode: ASDisplayNode?
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
public let separatorNode: ASDisplayNode
|
||||
|
||||
@ -402,7 +403,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
|
||||
let message = item.message
|
||||
|
||||
var selectedMedia: TelegramMediaFile?
|
||||
var selectedMedia: Media?
|
||||
for media in message.media {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
selectedMedia = file
|
||||
@ -539,6 +540,34 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
}
|
||||
|
||||
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
|
||||
updatedFetchControls = FetchControls(fetch: { [weak self] in
|
||||
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: {
|
||||
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 {
|
||||
updatedStatusSignal = messageFileMediaResourceStatus(context: item.context, file: selectedMedia, 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)
|
||||
if let file = selectedMedia as? TelegramMediaFile {
|
||||
updatedStatusSignal = messageFileMediaResourceStatus(context: item.context, file: file, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isAudio || isInstantVideo {
|
||||
if let currentUpdatedStatusSignal = updatedStatusSignal {
|
||||
updatedStatusSignal = currentUpdatedStatusSignal
|
||||
|> map { status in
|
||||
switch status.mediaStatus {
|
||||
case .fetchStatus:
|
||||
return FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: status.fetchStatus)
|
||||
case .playbackStatus:
|
||||
return status
|
||||
|
||||
if isAudio || isInstantVideo {
|
||||
if let currentUpdatedStatusSignal = updatedStatusSignal {
|
||||
updatedStatusSignal = currentUpdatedStatusSignal
|
||||
|> map { status in
|
||||
switch status.mediaStatus {
|
||||
case .fetchStatus:
|
||||
return FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: status.fetchStatus)
|
||||
case .playbackStatus:
|
||||
return status
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if isVoice {
|
||||
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: item.context, file: selectedMedia, message: message, isRecentActions: false, isGlobalSearch: item.isGlobalSearchResult)
|
||||
if isVoice {
|
||||
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: item.context, file: file, 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 let iconImage = iconImage {
|
||||
switch iconImage {
|
||||
case let .imageRepresentation(file, representation):
|
||||
updateIconImageSignal = chatWebpageSnippetFile(account: item.context.account, fileReference: .message(message: MessageReference(message), media: file), representation: representation)
|
||||
case let .imageRepresentation(media, 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):
|
||||
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):
|
||||
@ -746,6 +800,18 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
strongSelf.currentLeftOffset = leftOffset
|
||||
|
||||
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.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.theme.list.itemHighlightedBackgroundColor
|
||||
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)))
|
||||
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))
|
||||
let _ = titleNodeApply()
|
||||
|
||||
|
@ -51,12 +51,13 @@ public final class ListMessageItem: ListViewItem {
|
||||
public let selection: ChatHistoryMessageSelection
|
||||
let hintIsLink: Bool
|
||||
let isGlobalSearchResult: Bool
|
||||
let displayBackground: Bool
|
||||
|
||||
let header: ListViewItemHeader?
|
||||
|
||||
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.context = context
|
||||
self.chatLocation = chatLocation
|
||||
@ -72,6 +73,7 @@ public final class ListMessageItem: ListViewItem {
|
||||
self.selection = selection
|
||||
self.hintIsLink = hintIsLink
|
||||
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) {
|
||||
@ -82,6 +84,9 @@ public final class ListMessageItem: ListViewItem {
|
||||
if let _ = media as? TelegramMediaFile {
|
||||
viewClassName = ListMessageFileItemNode.self
|
||||
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) {
|
||||
updateIconImageSignal = chatWebpageSnippetPhoto(account: item.context.account, photoReference: imageReference)
|
||||
} 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 {
|
||||
updateIconImageSignal = .complete()
|
||||
|
@ -11,9 +11,10 @@ extern "C" {
|
||||
|
||||
bool MTLogEnabled();
|
||||
void MTLog(NSString *format, ...);
|
||||
void MTLogWithPrefix(NSString *(^getLogPrefix)(), NSString *format, ...);
|
||||
void MTShortLog(NSString *format, ...);
|
||||
void MTLogSetLoggingFunction(void (*function)(NSString *, va_list args));
|
||||
void MTLogSetShortLoggingFunction(void (*function)(NSString *, va_list args));
|
||||
void MTLogSetLoggingFunction(void (*function)(NSString *));
|
||||
void MTLogSetShortLoggingFunction(void (*function)(NSString *));
|
||||
void MTLogSetEnabled(bool);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -53,6 +53,8 @@
|
||||
@property (nonatomic) id requiredAuthToken;
|
||||
@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;
|
||||
|
||||
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
|
||||
|
@ -43,8 +43,9 @@
|
||||
@property (nonatomic, strong, readonly) MTSocksProxySettings * _Nullable proxySettings;
|
||||
@property (nonatomic) bool simultaneousTransactionsEnabled;
|
||||
@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;
|
||||
|
||||
|
@ -151,6 +151,8 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
|
||||
@property (nonatomic) bool useTcpNodelay;
|
||||
@property (nonatomic, strong) MTNetworkUsageCalculationInfo *usageCalculationInfo;
|
||||
|
||||
@property (nonatomic, strong) NSString *(^getLogPrefix)();
|
||||
|
||||
/**
|
||||
* GCDAsyncSocket uses the standard delegate paradigm,
|
||||
* but executes all delegate callbacks on a given delegate dispatch queue.
|
||||
|
@ -2468,7 +2468,7 @@ enum GCDAsyncSocketConfig
|
||||
freeifaddrs(ifaddr);
|
||||
|
||||
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 {
|
||||
|
@ -95,7 +95,7 @@
|
||||
MTPayloadData payloadData;
|
||||
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;
|
||||
connection.connectionOpened = ^
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
#import <MtProtoKit/MTLogging.h>
|
||||
|
||||
static void (*loggingFunction)(NSString *, va_list args) = NULL;
|
||||
static void (*shortLoggingFunction)(NSString *, va_list args) = NULL;
|
||||
static void (*loggingFunction)(NSString *) = NULL;
|
||||
static void (*shortLoggingFunction)(NSString *) = NULL;
|
||||
static bool MTLogEnabledValue = true;
|
||||
|
||||
bool MTLogEnabled() {
|
||||
@ -12,7 +12,24 @@ void MTLog(NSString *format, ...) {
|
||||
va_list L;
|
||||
va_start(L, format);
|
||||
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);
|
||||
}
|
||||
@ -21,16 +38,17 @@ void MTShortLog(NSString *format, ...) {
|
||||
va_list L;
|
||||
va_start(L, format);
|
||||
if (shortLoggingFunction != NULL) {
|
||||
shortLoggingFunction(format, L);
|
||||
NSString *string = [[NSString alloc] initWithFormat:format arguments:L];
|
||||
shortLoggingFunction(string);
|
||||
}
|
||||
va_end(L);
|
||||
}
|
||||
|
||||
void MTLogSetLoggingFunction(void (*function)(NSString *, va_list args)) {
|
||||
void MTLogSetLoggingFunction(void (*function)(NSString *)) {
|
||||
loggingFunction = function;
|
||||
}
|
||||
|
||||
void MTLogSetShortLoggingFunction(void (*function)(NSString *, va_list args)) {
|
||||
void MTLogSetShortLoggingFunction(void (*function)(NSString *)) {
|
||||
shortLoggingFunction = function;
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
if ((_mtState & MTProtoStatePaused) == 0)
|
||||
{
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"[MTProto#%p@%p pause]", self, _context);
|
||||
MTLogWithPrefix(_getLogPrefix, @"[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 (MTLogEnabled()) {
|
||||
MTLog(@"[MTProto#%p@%p resume]", self, _context);
|
||||
MTLogWithPrefix(_getLogPrefix, @"[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:^
|
||||
{
|
||||
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];
|
||||
@ -331,7 +331,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
}
|
||||
} else {
|
||||
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];
|
||||
}
|
||||
@ -343,7 +343,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
[[MTProto managerQueue] dispatchOnQueue:^
|
||||
{
|
||||
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);
|
||||
|
||||
@ -383,9 +383,9 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
if (!alreadySyncing)
|
||||
{
|
||||
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];
|
||||
timeSyncService.delegate = self;
|
||||
@ -416,7 +416,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
@ -467,7 +467,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
if (notifyAboutServiceTask)
|
||||
{
|
||||
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");
|
||||
|
||||
@ -520,7 +520,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
if (!performingServiceTasks)
|
||||
{
|
||||
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");
|
||||
|
||||
@ -682,7 +682,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
return;
|
||||
|
||||
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)
|
||||
@ -713,7 +713,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
return;
|
||||
|
||||
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)
|
||||
@ -740,7 +740,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
return;
|
||||
|
||||
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)
|
||||
@ -1048,7 +1048,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
|
||||
if (MTLogEnabled()) {
|
||||
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];
|
||||
MTShortLog(@"[MTProto#%p@%p preparing %@]", self, _context, shortMessageDescription);
|
||||
@ -1094,7 +1094,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
if (monotonityViolated)
|
||||
{
|
||||
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);
|
||||
|
||||
@ -1283,7 +1283,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
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);
|
||||
|
||||
@ -1455,7 +1455,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
[idsString appendFormat:@"%lld", [nMessageId longLongValue]];
|
||||
}
|
||||
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);
|
||||
}
|
||||
@ -1931,7 +1931,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
||||
[data getBytes:&protocolErrorCode range:NSMakeRange(0, 4)];
|
||||
|
||||
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);
|
||||
|
||||
@ -1991,7 +1991,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
||||
MTRpcError *rpcError = maybeInternalMessage;
|
||||
if (rpcError.errorCode == 401 && [rpcError.errorDescription isEqualToString:@"AUTH_KEY_PERM_EMPTY"]) {
|
||||
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);
|
||||
[self handleMissingKey:scheme];
|
||||
@ -2004,7 +2004,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
||||
}
|
||||
if (parseError) {
|
||||
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));
|
||||
|
||||
@ -2026,7 +2026,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
|
||||
@ -2046,7 +2046,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
||||
|
||||
if (_useUnauthorizedMode) {
|
||||
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;
|
||||
}
|
||||
@ -2055,7 +2055,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
||||
[self getAuthKeyForCurrentScheme:scheme createIfNeeded:false authInfoSelector:&authInfoSelector];
|
||||
|
||||
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) {
|
||||
@ -2346,7 +2346,7 @@ static bool isDataEqualToDataConstTime(NSData *data1, NSData *data2) {
|
||||
if ([_sessionInfo messageProcessed:incomingMessage.messageId])
|
||||
{
|
||||
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);
|
||||
[_sessionInfo scheduleMessageConfirmation:incomingMessage.messageId size:incomingMessage.size];
|
||||
@ -2358,7 +2358,7 @@ static bool isDataEqualToDataConstTime(NSData *data1, NSData *data2) {
|
||||
}
|
||||
|
||||
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];
|
||||
@ -2473,7 +2473,7 @@ static bool isDataEqualToDataConstTime(NSData *data1, NSData *data2) {
|
||||
int64_t requestMessageId = ((MTMsgDetailedResponseInfoMessage *)detailedInfoMessage).requestMessageId;
|
||||
|
||||
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);
|
||||
|
||||
@ -2496,7 +2496,7 @@ static bool isDataEqualToDataConstTime(NSData *data1, NSData *data2) {
|
||||
{
|
||||
[self requestMessageWithId:detailedInfoMessage.responseMessageId];
|
||||
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);
|
||||
}
|
||||
|
@ -66,7 +66,7 @@
|
||||
|
||||
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;
|
||||
__block NSTimeInterval startTime = CFAbsoluteTimeGetCurrent();
|
||||
connection.connectionOpened = ^ {
|
||||
|
@ -32,9 +32,11 @@
|
||||
@property (nonatomic, strong, readonly) MTTransportScheme *scheme;
|
||||
@property (nonatomic, strong, readonly) NSString *interface;
|
||||
|
||||
@property (nonatomic, strong) NSString *(^getLogPrefix)();
|
||||
|
||||
+ (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;
|
||||
|
||||
|
@ -689,7 +689,7 @@ struct ctr_state {
|
||||
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
|
||||
NSAssert(scheme != nil, @"scheme should not be nil");
|
||||
@ -700,6 +700,8 @@ struct ctr_state {
|
||||
{
|
||||
_internalId = [[MTInternalId(MTTcpConnection) alloc] init];
|
||||
|
||||
_getLogPrefix = [getLogPrefix copy];
|
||||
|
||||
_encryptionProvider = context.encryptionProvider;
|
||||
|
||||
_scheme = scheme;
|
||||
@ -799,6 +801,7 @@ struct ctr_state {
|
||||
if (_socket == nil)
|
||||
{
|
||||
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:[[MTTcpConnection tcpQueue] nativeQueue]];
|
||||
_socket.getLogPrefix = _getLogPrefix;
|
||||
_socket.usageCalculationInfo = _usageCalculationInfo;
|
||||
|
||||
NSString *addressIp = _scheme.address.ip;
|
||||
|
@ -82,7 +82,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
|
||||
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
|
||||
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");
|
||||
#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)
|
||||
{
|
||||
_context = context;
|
||||
@ -196,7 +196,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
|
||||
[self startConnectionWatchdogTimer:scheme];
|
||||
[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 start];
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
@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
|
||||
NSAssert(context != nil, @"context should not be nil");
|
||||
@ -25,6 +25,7 @@
|
||||
_context = context;
|
||||
_datacenterId = datacenterId;
|
||||
_proxySettings = proxySettings;
|
||||
_getLogPrefix = [getLogPrefix copy];
|
||||
|
||||
_networkAvailability = [[MTNetworkAvailability alloc] initWithDelegate:self];
|
||||
|
||||
|
@ -13,15 +13,15 @@ void setBridgingShortTraceFunction(void (*f)(NSString *, NSString *)) {
|
||||
bridgingShortTrace = f;
|
||||
}
|
||||
|
||||
static void TGTelegramLoggingFunction(NSString *format, va_list args) {
|
||||
static void TGTelegramLoggingFunction(NSString *format) {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|> map { next in
|
||||
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: {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1810,8 +1810,8 @@ public func chatWebpageSnippetPhotoData(account: Account, photoReference: ImageM
|
||||
}
|
||||
}
|
||||
|
||||
public func chatWebpageSnippetFile(account: Account, fileReference: FileMediaReference, representation: TelegramMediaImageRepresentation) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let signal = chatWebpageSnippetFileData(account: account, fileReference: fileReference, resource: representation.resource)
|
||||
public func chatWebpageSnippetFile(account: Account, mediaReference: AnyMediaReference, representation: TelegramMediaImageRepresentation) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let signal = chatWebpageSnippetFileData(account: account, mediaReference: mediaReference, resource: representation.resource)
|
||||
|
||||
return signal |> map { fullSizeData in
|
||||
return { arguments in
|
||||
|
@ -97,6 +97,8 @@ swift_library(
|
||||
"//submodules/QrCodeUI:QrCodeUI",
|
||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/FetchManagerImpl:FetchManagerImpl",
|
||||
"//submodules/ListMessageItem:ListMessageItem",
|
||||
],
|
||||
visibility = [
|
||||
"//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?
|
||||
}
|
||||
|
||||
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 {
|
||||
var datacenterId: Int { get }
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
let context: MTContext
|
||||
let mtProto: MTProto
|
||||
let requestService: MTRequestMessageService
|
||||
private let logPrefix = Atomic<String?>(value: nil)
|
||||
|
||||
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)
|
||||
let logPrefix = self.logPrefix
|
||||
self.mtProto.getLogPrefix = {
|
||||
return logPrefix.with { $0 }
|
||||
}
|
||||
self.mtProto.cdn = isCdn
|
||||
self.mtProto.useTempAuthKeys = self.context.useTempAuthKeys && !isCdn
|
||||
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
|
||||
return Signal { subscriber in
|
||||
let request = MTRequest()
|
||||
|
@ -256,6 +256,7 @@ swift_library(
|
||||
"//submodules/TabBarUI:TabBarUI",
|
||||
"//submodules/SoftwareVideo:SoftwareVideo",
|
||||
"//submodules/ManagedFile:ManagedFile",
|
||||
"//submodules/FetchManagerImpl:FetchManagerImpl",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
|
@ -17,6 +17,7 @@ import TelegramBaseController
|
||||
import AsyncDisplayKit
|
||||
import PresentationDataUtils
|
||||
import MeshAnimationCache
|
||||
import FetchManagerImpl
|
||||
|
||||
private final class DeviceSpecificContactImportContext {
|
||||
let disposable = MetaDisposable()
|
||||
|
@ -26,6 +26,7 @@ import TelegramNotices
|
||||
import ReactionListContextMenuContent
|
||||
import TelegramUIPreferences
|
||||
import Translate
|
||||
import DebugSettingsUI
|
||||
|
||||
private struct MessageContextMenuData {
|
||||
let starStatus: Bool?
|
||||
@ -565,7 +566,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
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,
|
||||
loadStickerSaveStatusSignal,
|
||||
loadResourceStatusSignal,
|
||||
@ -576,9 +577,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
readState,
|
||||
ApplicationSpecificNotice.getMessageViewsPrivacyTips(accountManager: context.sharedContext.accountManager),
|
||||
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
|
||||
var canEdit = false
|
||||
if !isAction {
|
||||
@ -598,12 +599,19 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
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
|
||||
|> 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 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 threadMessageCount: Int = 0
|
||||
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 {
|
||||
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||
} 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 {
|
||||
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||
} 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 {
|
||||
|
@ -226,7 +226,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
||||
if fileReference.media.isVideo {
|
||||
updateImageSignal = chatMessageVideoThumbnail(account: self.context.account, fileReference: fileReference)
|
||||
} 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 {
|
||||
|
@ -1,2 +0,0 @@
|
||||
import Foundation
|
||||
import Postbox
|
@ -403,6 +403,7 @@ private enum PeerInfoSettingsSection {
|
||||
case avatar
|
||||
case edit
|
||||
case proxy
|
||||
case downloads
|
||||
case savedMessages
|
||||
case recentCalls
|
||||
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)
|
||||
}))
|
||||
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)
|
||||
}))
|
||||
|
||||
@ -680,10 +685,10 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
||||
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)
|
||||
}))
|
||||
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)
|
||||
}))
|
||||
|
||||
@ -5527,6 +5532,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil)
|
||||
case .proxy:
|
||||
self.controller?.push(proxySettingsController(context: self.context))
|
||||
case .downloads:
|
||||
self.controller?.push(downloadsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData))
|
||||
case .savedMessages:
|
||||
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)))
|
||||
|
@ -196,7 +196,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
||||
if fileReference.media.isVideo {
|
||||
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||
} 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 {
|
||||
|
@ -556,7 +556,7 @@ final class WatchMediaHandler: WatchRequestHandler {
|
||||
imageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||
roundVideo = fileReference.media.isInstantVideo
|
||||
} 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