From cd6d2a3b9d6df9092f78b2cbc9005870cafa7494 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 2 Aug 2022 21:40:15 +0400 Subject: [PATCH] Emoji improvents --- .../Sources/InstantPageMediaPlaylist.swift | 2 +- .../Sources/ListMessageFileItemNode.swift | 4 +- .../ExternalMusicAlbumArtResources.swift | 15 ++- .../Sources/PhotoResources.swift | 10 +- .../Postbox/Sources/MediaResource.swift | 2 +- submodules/TelegramApi/Sources/Api0.swift | 1 + submodules/TelegramApi/Sources/Api10.swift | 34 ++++++ .../ApiUtils/CloudFileMediaResource.swift | 6 - .../Sources/Network/MultipartFetch.swift | 13 ++- .../TelegramCore/Sources/State/Fetch.swift | 2 +- .../SyncCore_CloudFileMediaResource.swift | 70 +++++++++++- .../Resources/TelegramEngineResources.swift | 106 ++++++++++++++++++ .../Sources/EmojiSuggestionsComponent.swift | 20 +++- .../ChatMessageInteractiveFileNode.swift | 2 +- .../Sources/PeerMessagesMediaPlaylist.swift | 2 +- 15 files changed, 260 insertions(+), 29 deletions(-) diff --git a/submodules/InstantPageUI/Sources/InstantPageMediaPlaylist.swift b/submodules/InstantPageUI/Sources/InstantPageMediaPlaylist.swift index c7e42e2916..a984bdb152 100644 --- a/submodules/InstantPageUI/Sources/InstantPageMediaPlaylist.swift +++ b/submodules/InstantPageUI/Sources/InstantPageMediaPlaylist.swift @@ -91,7 +91,7 @@ final class InstantPageMediaPlaylistItem: SharedMediaPlaylistItem { if file.fileName?.lowercased().hasSuffix(".ogg") == true { albumArt = nil } else { - albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) + albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .standalone(media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .standalone(media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) } return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: false) diff --git a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift index 65f4e3c93b..7de263d94b 100644 --- a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift @@ -473,9 +473,9 @@ public final class ListMessageFileItemNode: ListMessageNode { if !voice { if file.fileName?.lowercased().hasSuffix(".ogg") == true { - iconImage = .albumArt(file, SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(title: "", performer: "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: "", performer: "", isThumbnail: false))) + iconImage = .albumArt(file, SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: "", performer: "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: "", performer: "", isThumbnail: false))) } else { - iconImage = .albumArt(file, SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false))) + iconImage = .albumArt(file, SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: title ?? "", performer: performer ?? "", isThumbnail: false))) } } else { titleText = NSAttributedString(string: " ", font: audioTitleFont, textColor: item.presentationData.theme.theme.list.itemPrimaryTextColor) diff --git a/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift b/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift index 4848c7b623..e991414ae5 100644 --- a/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift +++ b/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift @@ -25,11 +25,13 @@ public struct ExternalMusicAlbumArtResourceId { } public class ExternalMusicAlbumArtResource: Equatable { + public let file: FileMediaReference? public let title: String public let performer: String public let isThumbnail: Bool - public init(title: String, performer: String, isThumbnail: Bool) { + public init(file: FileMediaReference?, title: String, performer: String, isThumbnail: Bool) { + self.file = file self.title = title self.performer = performer self.isThumbnail = isThumbnail @@ -40,6 +42,9 @@ public class ExternalMusicAlbumArtResource: Equatable { } public static func ==(lhs: ExternalMusicAlbumArtResource, rhs: ExternalMusicAlbumArtResource) -> Bool { + if lhs.file?.media.fileId != rhs.file?.media.fileId { + return false + } if lhs.title != rhs.title { return false } @@ -53,8 +58,10 @@ public class ExternalMusicAlbumArtResource: Equatable { } } -public func fetchExternalMusicAlbumArtResource(engine: TelegramEngine, resource: ExternalMusicAlbumArtResource) -> Signal { - return Signal { subscriber in +public func fetchExternalMusicAlbumArtResource(engine: TelegramEngine, file: FileMediaReference?, resource: ExternalMusicAlbumArtResource) -> Signal { + return engine.resources.fetchAlbumCover(file: file, title: resource.title, performer: resource.performer) + + /*return Signal { subscriber in if resource.performer.isEmpty || resource.performer.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) == "unknown artist" || resource.title.isEmpty { subscriber.putError(.generic) return EmptyDisposable @@ -151,5 +158,5 @@ public func fetchExternalMusicAlbumArtResource(engine: TelegramEngine, resource: fetchDisposable.dispose() } } - } + }*/ } diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 78ea869c99..f058c95093 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -2592,7 +2592,7 @@ public func albumArtThumbnailData(engine: TelegramEngine, thumbnail: ExternalMus return engine.resources.custom( id: thumbnail.id.stringRepresentation, fetch: EngineMediaResource.Fetch { - return fetchExternalMusicAlbumArtResource(engine: engine, resource: thumbnail) + return fetchExternalMusicAlbumArtResource(engine: engine, file: thumbnail.file, resource: thumbnail) }, attemptSynchronously: attemptSynchronously ) @@ -2613,7 +2613,7 @@ public func albumArtThumbnailData(engine: TelegramEngine, thumbnail: ExternalMus }) } -private func albumArtFullSizeDatas(engine: TelegramEngine, thumbnail: ExternalMusicAlbumArtResource, fullSize: ExternalMusicAlbumArtResource, autoFetchFullSize: Bool = true) -> Signal, NoError> { +private func albumArtFullSizeDatas(engine: TelegramEngine, file: FileMediaReference?, thumbnail: ExternalMusicAlbumArtResource, fullSize: ExternalMusicAlbumArtResource, autoFetchFullSize: Bool = true) -> Signal, NoError> { return engine.resources.custom( id: fullSize.id.stringRepresentation, fetch: nil, @@ -2629,14 +2629,14 @@ private func albumArtFullSizeDatas(engine: TelegramEngine, thumbnail: ExternalMu engine.resources.custom( id: thumbnail.id.stringRepresentation, fetch: EngineMediaResource.Fetch { - return fetchExternalMusicAlbumArtResource(engine: engine, resource: thumbnail) + return fetchExternalMusicAlbumArtResource(engine: engine, file: file, resource: thumbnail) }, attemptSynchronously: false ), engine.resources.custom( id: fullSize.id.stringRepresentation, fetch: autoFetchFullSize ? EngineMediaResource.Fetch { - return fetchExternalMusicAlbumArtResource(engine: engine, resource: fullSize) + return fetchExternalMusicAlbumArtResource(engine: engine, file: file, resource: fullSize) } : nil, attemptSynchronously: false ) @@ -2749,7 +2749,7 @@ public func playerAlbumArt(postbox: Postbox, engine: TelegramEngine, fileReferen return Tuple(thumbnailData, nil, false) } } else { - immediateArtworkData = albumArtFullSizeDatas(engine: engine, thumbnail: albumArt.thumbnailResource, fullSize: albumArt.fullSizeResource) + immediateArtworkData = albumArtFullSizeDatas(engine: engine, file: fileReference, thumbnail: albumArt.thumbnailResource, fullSize: albumArt.fullSizeResource) } } diff --git a/submodules/Postbox/Sources/MediaResource.swift b/submodules/Postbox/Sources/MediaResource.swift index 35bec2c4ad..e4d4f2d773 100644 --- a/submodules/Postbox/Sources/MediaResource.swift +++ b/submodules/Postbox/Sources/MediaResource.swift @@ -8,7 +8,7 @@ public struct MediaResourceId: Equatable, Hashable { } } -public protocol MediaResource { +public protocol MediaResource: AnyObject { var id: MediaResourceId { get } var size: Int64? { get } var streamable: Bool { get } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 1adb4032bc..873ab3a645 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -363,6 +363,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1770371538] = { return Api.InputWallPaper.parse_inputWallPaperNoFile($0) } dict[1913199744] = { return Api.InputWallPaper.parse_inputWallPaperSlug($0) } dict[-1678949555] = { return Api.InputWebDocument.parse_inputWebDocument($0) } + dict[-193992412] = { return Api.InputWebFileLocation.parse_inputWebFileAudioAlbumThumbLocation($0) } dict[-1625153079] = { return Api.InputWebFileLocation.parse_inputWebFileGeoPointLocation($0) } dict[-1036396922] = { return Api.InputWebFileLocation.parse_inputWebFileLocation($0) } dict[1048946971] = { return Api.Invoice.parse_invoice($0) } diff --git a/submodules/TelegramApi/Sources/Api10.swift b/submodules/TelegramApi/Sources/Api10.swift index 089a3b18e6..26ac5e851b 100644 --- a/submodules/TelegramApi/Sources/Api10.swift +++ b/submodules/TelegramApi/Sources/Api10.swift @@ -54,11 +54,21 @@ public extension Api { } public extension Api { enum InputWebFileLocation: TypeConstructorDescription { + case inputWebFileAudioAlbumThumbLocation(flags: Int32, document: Api.InputDocument?, title: String?, performer: String?) case inputWebFileGeoPointLocation(geoPoint: Api.InputGeoPoint, accessHash: Int64, w: Int32, h: Int32, zoom: Int32, scale: Int32) case inputWebFileLocation(url: String, accessHash: Int64) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { + case .inputWebFileAudioAlbumThumbLocation(let flags, let document, let title, let performer): + if boxed { + buffer.appendInt32(-193992412) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {document!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(performer!, buffer: buffer, boxed: false)} + break case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale): if boxed { buffer.appendInt32(-1625153079) @@ -82,6 +92,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { + case .inputWebFileAudioAlbumThumbLocation(let flags, let document, let title, let performer): + return ("inputWebFileAudioAlbumThumbLocation", [("flags", String(describing: flags)), ("document", String(describing: document)), ("title", String(describing: title)), ("performer", String(describing: performer))]) case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale): return ("inputWebFileGeoPointLocation", [("geoPoint", String(describing: geoPoint)), ("accessHash", String(describing: accessHash)), ("w", String(describing: w)), ("h", String(describing: h)), ("zoom", String(describing: zoom)), ("scale", String(describing: scale))]) case .inputWebFileLocation(let url, let accessHash): @@ -89,6 +101,28 @@ public extension Api { } } + public static func parse_inputWebFileAudioAlbumThumbLocation(_ reader: BufferReader) -> InputWebFileLocation? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.InputDocument? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.InputDocument + } } + var _3: String? + if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } + var _4: String? + if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.InputWebFileLocation.inputWebFileAudioAlbumThumbLocation(flags: _1!, document: _2, title: _3, performer: _4) + } + else { + return nil + } + } public static func parse_inputWebFileGeoPointLocation(_ reader: BufferReader) -> InputWebFileLocation? { var _1: Api.InputGeoPoint? if let signature = reader.readInt32() { diff --git a/submodules/TelegramCore/Sources/ApiUtils/CloudFileMediaResource.swift b/submodules/TelegramCore/Sources/ApiUtils/CloudFileMediaResource.swift index 4bc6d79da2..4f57b1eff5 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/CloudFileMediaResource.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/CloudFileMediaResource.swift @@ -82,9 +82,3 @@ extension SecretFileMediaResource: TelegramCloudMediaResource, TelegramMultipart return .inputEncryptedFileLocation(id: self.fileId, accessHash: self.accessHash) } } - -extension WebFileReferenceMediaResource { - var apiInputLocation: Api.InputWebFileLocation { - return .inputWebFileLocation(url: self.url, accessHash: self.accessHash) - } -} diff --git a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift index 52387c90db..1eb94cff1f 100644 --- a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift +++ b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift @@ -56,6 +56,7 @@ private enum MultipartFetchDownloadError { case reuploadToCdn(masterDatacenterId: Int32, token: Data) case revalidateMediaReference case hashesMissing + case fatal } private enum MultipartFetchGenericLocationResult { @@ -347,7 +348,7 @@ private enum MultipartFetchSource { case let .web(_, location): return download.request(Api.functions.upload.getWebFile(location: location, offset: Int32(offset), limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground) |> mapError { error -> MultipartFetchDownloadError in - return .generic + return .fatal } |> mapToSignal { result -> Signal in switch result { @@ -448,6 +449,7 @@ private final class MultipartFetchManager { let continueInBackground: Bool let partReady: (Int64, Data) -> Void let reportCompleteSize: (Int64) -> Void + let finishWithError: (MediaResourceDataFetchError) -> Void private let useMainConnection: Bool private var source: MultipartFetchSource @@ -472,7 +474,7 @@ private final class MultipartFetchManager { private var fetchSpeedRecords: [FetchSpeedRecord] = [] private var totalFetchedByteCount: Int = 0 - init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int64?, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int64?, location: MultipartFetchMasterLocation, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext?, partReady: @escaping (Int64, Data) -> Void, reportCompleteSize: @escaping (Int64) -> Void, useMainConnection: Bool) { + init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int64?, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int64?, location: MultipartFetchMasterLocation, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext?, partReady: @escaping (Int64, Data) -> Void, reportCompleteSize: @escaping (Int64) -> Void, finishWithError: @escaping (MediaResourceDataFetchError) -> Void, useMainConnection: Bool) { self.resource = resource self.parameters = parameters self.consumerId = Int64.random(in: Int64.min ... Int64.max) @@ -528,6 +530,7 @@ private final class MultipartFetchManager { self.source = .master(location: location, download: DownloadWrapper(consumerId: self.consumerId, datacenterId: location.datacenterId, isCdn: false, network: network, useMainConnection: self.useMainConnection)) self.partReady = partReady self.reportCompleteSize = reportCompleteSize + self.finishWithError = finishWithError self.rangesDisposable = (intervals |> deliverOn(self.queue)).start(next: { [weak self] intervals in @@ -734,6 +737,8 @@ private final class MultipartFetchManager { switch error { case .generic: break + case .fatal: + strongSelf.finishWithError(.generic) case .revalidateMediaReference: if !strongSelf.revalidatingMediaReference && !strongSelf.revalidatedMediaReference { strongSelf.revalidatingMediaReference = true @@ -821,7 +826,7 @@ public func resourceFetchInfo(resource: TelegramMediaResource) -> MediaResourceF func multipartFetch(postbox: Postbox, network: Network, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?, resource: TelegramMediaResource, datacenterId: Int, size: Int64?, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int64? = nil, continueInBackground: Bool = false, useMainConnection: Bool = false) -> Signal { return Signal { subscriber in let location: MultipartFetchMasterLocation - if let resource = resource as? WebFileReferenceMediaResource { + if let resource = resource as? MediaResourceWithWebFileReference { location = .web(Int32(datacenterId), resource.apiInputLocation) } else { location = .generic(Int32(datacenterId), { resource, resourceReference, fileReference in @@ -876,6 +881,8 @@ func multipartFetch(postbox: Postbox, network: Network, mediaReferenceRevalidati }, reportCompleteSize: { size in subscriber.putNext(.resourceSizeUpdated(size)) subscriber.putCompletion() + }, finishWithError: { error in + subscriber.putError(error) }, useMainConnection: useMainConnection) manager.start() diff --git a/submodules/TelegramCore/Sources/State/Fetch.swift b/submodules/TelegramCore/Sources/State/Fetch.swift index ca27985871..b6bced6a66 100644 --- a/submodules/TelegramCore/Sources/State/Fetch.swift +++ b/submodules/TelegramCore/Sources/State/Fetch.swift @@ -49,7 +49,7 @@ func fetchResource(account: Account, resource: MediaResource, intervals: Signal< } else if let cloudResource = resource as? TelegramMultipartFetchableResource { return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, datacenterId: cloudResource.datacenterId, size: resource.size == 0 ? nil : resource.size, intervals: intervals, parameters: parameters)) - } else if let webFileResource = resource as? WebFileReferenceMediaResource { + } else if let webFileResource = resource as? MediaResourceWithWebFileReference { return currentWebDocumentsHostDatacenterId(postbox: account.postbox, isTestingEnvironment: account.testingEnvironment) |> castError(MediaResourceDataFetchError.self) |> mapToSignal { datacenterId -> Signal in diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CloudFileMediaResource.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CloudFileMediaResource.swift index b71d6b44bc..0e64807653 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CloudFileMediaResource.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CloudFileMediaResource.swift @@ -1,5 +1,6 @@ import Foundation import Postbox +import TelegramApi public struct CloudFileMediaResourceId: Hashable, Equatable { let datacenterId: Int @@ -664,7 +665,11 @@ public struct WebFileReferenceMediaResourceId: Hashable, Equatable { } } -public final class WebFileReferenceMediaResource: TelegramMediaResource { +protocol MediaResourceWithWebFileReference: TelegramMediaResource { + var apiInputLocation: Api.InputWebFileLocation { get } +} + +public final class WebFileReferenceMediaResource: TelegramMediaResource, MediaResourceWithWebFileReference { public let url: String public let actualSize: Int64 public var size: Int64? { @@ -705,8 +710,71 @@ public final class WebFileReferenceMediaResource: TelegramMediaResource { return false } } + + var apiInputLocation: Api.InputWebFileLocation { + return .inputWebFileLocation(url: self.url, accessHash: self.accessHash) + } } +final class AlbumCoverResource: TelegramMediaResource, MediaResourceWithWebFileReference { + var id: MediaResourceId { + return MediaResourceId("AlbumCoverResource-\(self.datacenterId)-\(self.title)-\(self.performer)") + } + + let datacenterId: Int + let size: Int64? = nil + + func isEqual(to: MediaResource) -> Bool { + return self === to + } + + var fileReference: Data? { + if let file = self.file, let resource = file.media.resource as? CloudDocumentMediaResource { + return resource.fileReference + } else { + return nil + } + } + + let file: FileMediaReference? + let title: String + let performer: String + + init(datacenterId: Int, file: FileMediaReference?, title: String, performer: String) { + self.datacenterId = datacenterId + self.file = file + self.title = title + self.performer = performer + } + + init(decoder: PostboxDecoder) { + preconditionFailure() + } + + func encode(_ encoder: PostboxEncoder) { + } + + var apiInputLocation: Api.InputWebFileLocation { + var flags: Int32 = 0 + var document: Api.InputDocument? + if let file = self.file, let resource = file.media.resource as? CloudDocumentMediaResource { + document = .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())) + flags |= 1 << 0 + } + if !self.title.isEmpty { + flags |= 1 << 1 + } + if !self.performer.isEmpty { + flags |= 1 << 1 + } + return .inputWebFileAudioAlbumThumbLocation( + flags: flags, + document: document, + title: self.title.isEmpty ? nil : self.title, + performer: self.performer.isEmpty ? nil : self.performer + ) + } +} public struct SecretFileMediaResourceId: Hashable, Equatable { public let fileId: Int64 diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift index 105d5551ae..90038abd12 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift @@ -1,10 +1,59 @@ import Foundation import SwiftSignalKit import Postbox +import TelegramApi public typealias EngineTempBox = TempBox public typealias EngineTempBoxFile = TempBoxFile +func bufferedFetch(_ signal: Signal) -> Signal { + return Signal { subscriber in + final class State { + var data = Data() + var isCompleted: Bool = false + + init() { + } + } + + let state = Atomic(value: State()) + return signal.start(next: { value in + switch value { + case let .dataPart(_, data, _, _): + let _ = state.with { state in + state.data.append(data) + } + case let .moveTempFile(file): + let _ = state.with { state in + state.isCompleted = true + } + subscriber.putNext(.moveTempFile(file: file)) + case .resourceSizeUpdated: + break + default: + assert(false) + break + } + }, error: { error in + subscriber.putError(error) + }, completed: { + let tempFile = state.with { state -> TempBoxFile? in + if state.isCompleted { + return nil + } else { + let tempFile = TempBox.shared.tempFile(fileName: "data") + let _ = try? state.data.write(to: URL(fileURLWithPath: tempFile.path), options: .atomic) + return tempFile + } + } + if let tempFile = tempFile { + subscriber.putNext(.moveTempFile(file: tempFile)) + } + subscriber.putCompletion() + }) + } +} + public final class EngineMediaResource: Equatable { public enum CacheTimeout { case `default` @@ -29,7 +78,14 @@ public final class EngineMediaResource: Equatable { public final class Fetch { public enum Result { + case dataPart(resourceOffset: Int64, data: Data, range: Range, complete: Bool) + case resourceSizeUpdated(Int64) + case progressUpdated(Float) + case replaceHeader(data: Data, range: Range) + case moveLocalFile(path: String) case moveTempFile(file: TempBoxFile) + case copyLocalItem(MediaResourceDataFetchCopyLocalItem) + case reset } public enum Error { @@ -189,6 +245,9 @@ public extension TelegramEngine { switch result { case let .moveTempFile(file): mappedResult = .tempFile(file) + default: + assert(false) + return } subscriber.putNext(mappedResult) }, completed: { @@ -219,6 +278,53 @@ public extension TelegramEngine { } } } + + public func fetchAlbumCover(file: FileMediaReference?, title: String, performer: String) -> Signal { + let datacenterId: Int + if let resource = file?.media.resource as? CloudDocumentMediaResource { + datacenterId = resource.datacenterId + } else { + datacenterId = self.account.network.datacenterId + } + + let resource = AlbumCoverResource(datacenterId: datacenterId, file: file, title: title, performer: performer) + + return bufferedFetch(multipartFetch(postbox: self.account.postbox, network: self.account.network, mediaReferenceRevalidationContext: self.account.mediaReferenceRevalidationContext, resource: resource, datacenterId: datacenterId, size: nil, intervals: .single([(0 ..< Int64.max, .default)]), parameters: MediaResourceFetchParameters( + tag: nil, + info: TelegramCloudMediaResourceFetchInfo( + reference: MediaResourceReference.standalone(resource: resource), + preferBackgroundReferenceRevalidation: false, + continueInBackground: false + ), + isRandomAccessAllowed: true + )) + |> map { result -> EngineMediaResource.Fetch.Result in + switch result { + case let .dataPart(resourceOffset, data, range, complete): + return .dataPart(resourceOffset: resourceOffset, data: data, range: range, complete: complete) + case let .resourceSizeUpdated(size): + return .resourceSizeUpdated(size) + case let .progressUpdated(value): + return .progressUpdated(value) + case let .replaceHeader(data, range): + return .replaceHeader(data: data, range: range) + case let .moveLocalFile(path): + return .moveLocalFile(path: path) + case let .moveTempFile(file): + return .moveTempFile(file: file) + case let .copyLocalItem(item): + return .copyLocalItem(item) + case .reset: + return .reset + } + } + |> mapError { error -> EngineMediaResource.Fetch.Error in + switch error { + case .generic: + return .generic + } + }) + } public func cancelAllFetches(id: String) { preconditionFailure() diff --git a/submodules/TelegramUI/Components/EmojiSuggestionsComponent/Sources/EmojiSuggestionsComponent.swift b/submodules/TelegramUI/Components/EmojiSuggestionsComponent/Sources/EmojiSuggestionsComponent.swift index 76637cb11e..1376e0ebf4 100644 --- a/submodules/TelegramUI/Components/EmojiSuggestionsComponent/Sources/EmojiSuggestionsComponent.swift +++ b/submodules/TelegramUI/Components/EmojiSuggestionsComponent/Sources/EmojiSuggestionsComponent.swift @@ -158,6 +158,7 @@ public final class EmojiSuggestionsComponent: Component { } } + private let blurView: BlurredBackgroundView private let backgroundLayer: SimpleShapeLayer private let scrollView: UIScrollView @@ -168,11 +169,19 @@ public final class EmojiSuggestionsComponent: Component { private var visibleLayers: [MediaId: InlineStickerItemLayer] = [:] override init(frame: CGRect) { + self.blurView = BlurredBackgroundView(color: .clear, enableBlur: true) + self.blurView.layer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor + self.blurView.layer.shadowOffset = CGSize(width: 0.0, height: 2.0) + self.blurView.layer.shadowRadius = 15.0 + self.blurView.layer.shadowOpacity = 0.15 + self.backgroundLayer = SimpleShapeLayer() - self.backgroundLayer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor + /*self.backgroundLayer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor self.backgroundLayer.shadowOffset = CGSize(width: 0.0, height: 2.0) self.backgroundLayer.shadowRadius = 15.0 - self.backgroundLayer.shadowOpacity = 0.15 + self.backgroundLayer.shadowOpacity = 0.15*/ + + self.blurView.layer.mask = self.backgroundLayer self.scrollView = UIScrollView() @@ -197,6 +206,7 @@ public final class EmojiSuggestionsComponent: Component { self.scrollView.delegate = self self.scrollView.clipsToBounds = true + self.addSubview(self.blurView) self.layer.addSublayer(self.backgroundLayer) self.addSubview(self.scrollView) @@ -317,6 +327,8 @@ public final class EmojiSuggestionsComponent: Component { path.addArc(tangent1End: CGPoint(x: size.width, y: 0.0), tangent2End: CGPoint(x: size.width - radius, y: 0.0), radius: radius) path.addLine(to: CGPoint(x: radius, y: 0.0)) + self.blurView.frame = CGRect(origin: CGPoint(), size: size) + self.blurView.update(size: size, transition: .immediate) self.backgroundLayer.frame = CGRect(origin: CGPoint(), size: size) self.backgroundLayer.path = path self.backgroundLayer.shadowPath = path @@ -326,7 +338,9 @@ public final class EmojiSuggestionsComponent: Component { let height: CGFloat = 54.0 if self.component?.theme !== component.theme { - self.backgroundLayer.fillColor = component.theme.list.plainBackgroundColor.cgColor + //self.backgroundLayer.fillColor = component.theme.list.plainBackgroundColor.cgColor + self.backgroundLayer.fillColor = UIColor.black.cgColor + self.blurView.updateColor(color: component.theme.list.plainBackgroundColor.withMultipliedAlpha(0.6), transition: .immediate) } var resetScrollingPosition = false if self.component?.files != component.files { diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index c7d82877f9..22fb75a9c9 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -1496,7 +1496,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } } - image = playerAlbumArt(postbox: context.account.postbox, engine: context.engine, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false, attemptSynchronously: !animated) + image = playerAlbumArt(postbox: context.account.postbox, engine: context.engine, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(message), media: file), title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false, attemptSynchronously: !animated) } } let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor) diff --git a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift index afd04a1d17..c0d0edd16a 100644 --- a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift @@ -111,7 +111,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { if file.fileName?.lowercased().hasSuffix(".ogg") == true { albumArt = nil } else { - albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) + albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) } return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0)