Initial downloads list implementation

This commit is contained in:
Ali 2022-02-11 23:49:38 +04:00
parent e87cf3710f
commit c8bc4b7f12
39 changed files with 816 additions and 139 deletions

View File

@ -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)
}

View File

@ -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",

View File

@ -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)
})
}

View 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",
],
)

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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()

View File

@ -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
}
}
}

View File

@ -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()

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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 {

View File

@ -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 = ^
{

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 = ^ {

View File

@ -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;

View File

@ -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;

View File

@ -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];
}

View File

@ -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];

View File

@ -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);
}
}

View File

@ -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

View File

@ -97,6 +97,8 @@ swift_library(
"//submodules/QrCodeUI:QrCodeUI",
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/FetchManagerImpl:FetchManagerImpl",
"//submodules/ListMessageItem:ListMessageItem",
],
visibility = [
"//visibility:public",

View File

@ -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
}

View File

@ -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 }
}

View File

@ -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()

View File

@ -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,

View File

@ -17,6 +17,7 @@ import TelegramBaseController
import AsyncDisplayKit
import PresentationDataUtils
import MeshAnimationCache
import FetchManagerImpl
private final class DeviceSpecificContactImportContext {
let disposable = MetaDisposable()

View File

@ -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 {

View File

@ -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)
}
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -1,2 +0,0 @@
import Foundation
import Postbox

View File

@ -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)))

View File

@ -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 {

View File

@ -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)
}
}
}