mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Local changes
This commit is contained in:
parent
f8d0f6a233
commit
4f9c73dc7a
@ -73,7 +73,7 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa
|
||||
#endif*/
|
||||
|
||||
let resourceSize: Int = resourceReference.resource.size ?? Int(Int32.max - 1)
|
||||
let readCount = min(resourceSize - context.readingOffset, Int(bufferSize))
|
||||
let readCount = max(0, min(resourceSize - context.readingOffset, Int(bufferSize)))
|
||||
let requestRange: Range<Int> = context.readingOffset ..< (context.readingOffset + readCount)
|
||||
|
||||
assert(readCount < 16 * 1024 * 1024)
|
||||
|
@ -74,6 +74,8 @@ public final class MediaPlayerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public var hasSentFramesToDisplay: (() -> Void)?
|
||||
|
||||
var takeFrameAndQueue: (Queue, () -> MediaTrackFrameResult)?
|
||||
var timer: SwiftSignalKit.Timer?
|
||||
var polling = false
|
||||
@ -198,6 +200,7 @@ public final class MediaPlayerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
videoLayer.enqueue(frame.sampleBuffer)
|
||||
strongSelf.hasSentFramesToDisplay?()
|
||||
}
|
||||
}
|
||||
Queue.mainQueue().async {
|
||||
@ -226,6 +229,7 @@ public final class MediaPlayerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
videoLayer.enqueue(frame.sampleBuffer)
|
||||
strongSelf.hasSentFramesToDisplay?()
|
||||
}
|
||||
|
||||
Queue.mainQueue().async {
|
||||
|
@ -324,12 +324,20 @@ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference:
|
||||
|
||||
private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaReference, thumbnailSize: Bool = false, onlyFullSize: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false) -> Signal<Tuple3<Data?, Tuple2<Data, String>?, Bool>, NoError> {
|
||||
let fullSizeResource = fileReference.media.resource
|
||||
var reducedSizeResource: MediaResource?
|
||||
if let videoThumbnail = fileReference.media.videoThumbnails.first {
|
||||
reducedSizeResource = videoThumbnail.resource
|
||||
}
|
||||
|
||||
let thumbnailRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations)
|
||||
let thumbnailResource = thumbnailRepresentation?.resource
|
||||
|
||||
let maybeFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: false, attemptSynchronously: synchronousLoad)
|
||||
let fetchedFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: true, attemptSynchronously: synchronousLoad)
|
||||
var fetchedReducedSize: Signal<MediaResourceData, NoError> = .single(MediaResourceData(path: "", offset: 0, size: 0, complete: false))
|
||||
if let reducedSizeResource = reducedSizeResource {
|
||||
fetchedReducedSize = postbox.mediaBox.cachedResourceRepresentation(reducedSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: true, attemptSynchronously: synchronousLoad)
|
||||
}
|
||||
|
||||
let signal = maybeFullSize
|
||||
|> take(1)
|
||||
@ -391,11 +399,29 @@ private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaRef
|
||||
return Tuple(data == nil ? nil : Tuple(data!, next.path), next.complete)
|
||||
}
|
||||
|
||||
let reducedSizeDataAndPath = Signal<MediaResourceData, NoError> { subscriber in
|
||||
let dataDisposable = fetchedReducedSize.start(next: { next in
|
||||
subscriber.putNext(next)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
return ActionDisposable {
|
||||
dataDisposable.dispose()
|
||||
}
|
||||
}
|
||||
|> map { next -> Tuple2<Tuple2<Data, String>?, Bool> in
|
||||
let data = next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe)
|
||||
return Tuple(data == nil ? nil : Tuple(data!, next.path), next.complete)
|
||||
}
|
||||
|
||||
return thumbnail
|
||||
|> mapToSignal { thumbnailData in
|
||||
return fullSizeDataAndPath
|
||||
|> map { value in
|
||||
return Tuple(thumbnailData, value._0, value._1)
|
||||
return combineLatest(fullSizeDataAndPath, reducedSizeDataAndPath)
|
||||
|> map { fullSize, reducedSize in
|
||||
if !fullSize._1 && reducedSize._1 {
|
||||
return Tuple(thumbnailData, reducedSize._0, false)
|
||||
}
|
||||
return Tuple(thumbnailData, fullSize._0, fullSize._1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
public struct MediaId: Hashable, PostboxCoding, CustomStringConvertible {
|
||||
public struct MediaId: Hashable, PostboxCoding, CustomStringConvertible, Codable {
|
||||
public typealias Namespace = Int32
|
||||
public typealias Id = Int64
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
public struct PeerId: Hashable, CustomStringConvertible, Comparable {
|
||||
public struct PeerId: Hashable, CustomStringConvertible, Comparable, Codable {
|
||||
public typealias Namespace = Int32
|
||||
public typealias Id = Int32
|
||||
|
||||
|
@ -86,12 +86,12 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE
|
||||
case let .contextResult(result):
|
||||
var imageResource: TelegramMediaResource?
|
||||
switch result {
|
||||
case let .externalReference(_, _, _, _, _, _, content, _, _):
|
||||
if let content = content {
|
||||
case let .externalReference(externalReference):
|
||||
if let content = externalReference.content {
|
||||
imageResource = content.resource
|
||||
}
|
||||
case let .internalReference(_, _, _, _, _, image, _, _):
|
||||
if let image = image {
|
||||
case let .internalReference(internalReference):
|
||||
if let image = internalReference.image {
|
||||
if let imageRepresentation = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 1000, height: 800)) {
|
||||
imageResource = imageRepresentation.resource
|
||||
}
|
||||
|
@ -482,7 +482,10 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode {
|
||||
if let searchContext = searchContext {
|
||||
if let _ = searchContext.loadMoreIndex, let nextOffset = searchContext.result.nextOffset {
|
||||
let collection = searchContext.result.collection
|
||||
return requestChatContextResults(account: self.context.account, botId: collection.botId, peerId: collection.peerId, query: searchContext.result.query, location: .single(collection.geoPoint), offset: nextOffset)
|
||||
let geoPoint = collection.geoPoint.flatMap { geoPoint -> (Double, Double) in
|
||||
return (geoPoint.latitude, geoPoint.longitude)
|
||||
}
|
||||
return requestChatContextResults(account: self.context.account, botId: collection.botId, peerId: collection.peerId, query: searchContext.result.query, location: .single(geoPoint), offset: nextOffset)
|
||||
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|
@ -71,21 +71,21 @@ final class ThemeGridSearchItemNode: GridItemNode {
|
||||
var imageDimensions: CGSize?
|
||||
var immediateThumbnailData: Data?
|
||||
switch item.result {
|
||||
case let .externalReference(_, _, type, _, _, _, content, thumbnail, _):
|
||||
if let content = content, type != "gif" {
|
||||
case let .externalReference(externalReference):
|
||||
if let content = externalReference.content, externalReference.type != "gif" {
|
||||
imageResource = content.resource
|
||||
} else if let thumbnail = thumbnail {
|
||||
} else if let thumbnail = externalReference.thumbnail {
|
||||
imageResource = thumbnail.resource
|
||||
}
|
||||
imageDimensions = content?.dimensions?.cgSize
|
||||
case let .internalReference(_, _, _, _, _, image, file, _):
|
||||
if let image = image {
|
||||
imageDimensions = externalReference.content?.dimensions?.cgSize
|
||||
case let .internalReference(internalReference):
|
||||
if let image = internalReference.image {
|
||||
immediateThumbnailData = image.immediateThumbnailData
|
||||
if let representation = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 321, height: 321)) {
|
||||
imageResource = representation.resource
|
||||
imageDimensions = representation.dimensions.cgSize
|
||||
}
|
||||
if let file = file {
|
||||
if let file = internalReference.file {
|
||||
if let thumbnailRepresentation = smallestImageRepresentation(file.previewRepresentations) {
|
||||
thumbnailDimensions = thumbnailRepresentation.dimensions.cgSize
|
||||
thumbnailResource = thumbnailRepresentation.resource
|
||||
@ -96,7 +96,7 @@ final class ThemeGridSearchItemNode: GridItemNode {
|
||||
thumbnailResource = thumbnailRepresentation.resource
|
||||
}
|
||||
}
|
||||
} else if let file = file {
|
||||
} else if let file = internalReference.file {
|
||||
immediateThumbnailData = file.immediateThumbnailData
|
||||
if let dimensions = file.dimensions {
|
||||
imageDimensions = dimensions.cgSize
|
||||
|
@ -418,19 +418,19 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
var thumbnailDimensions: CGSize?
|
||||
var thumbnailResource: TelegramMediaResource?
|
||||
switch result {
|
||||
case let .externalReference(_, _, _, _, _, _, content, thumbnail, _):
|
||||
if let content = content {
|
||||
case let .externalReference(externalReference):
|
||||
if let content = externalReference.content {
|
||||
imageResource = content.resource
|
||||
}
|
||||
if let thumbnail = thumbnail {
|
||||
if let thumbnail = externalReference.thumbnail {
|
||||
thumbnailResource = thumbnail.resource
|
||||
thumbnailDimensions = thumbnail.dimensions?.cgSize
|
||||
}
|
||||
if let dimensions = content?.dimensions {
|
||||
if let dimensions = externalReference.content?.dimensions {
|
||||
imageDimensions = dimensions.cgSize
|
||||
}
|
||||
case let .internalReference(_, _, _, _, _, image, _, _):
|
||||
if let image = image {
|
||||
case let .internalReference(internalReference):
|
||||
if let image = internalReference.image {
|
||||
if let imageRepresentation = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 1000, height: 800)) {
|
||||
imageDimensions = imageRepresentation.dimensions.cgSize
|
||||
imageResource = imageRepresentation.resource
|
||||
|
@ -70,6 +70,7 @@ public struct Namespaces {
|
||||
public static let cachedWallpapersConfiguration: Int8 = 7
|
||||
public static let cachedThemesConfiguration: Int8 = 8
|
||||
public static let cachedPollResults: Int8 = 9
|
||||
public static let cachedContextResults: Int8 = 10
|
||||
}
|
||||
|
||||
public struct UnorderedItemList {
|
||||
|
@ -234,7 +234,15 @@ public enum TelegramMediaFileReference: PostboxCoding, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public final class TelegramMediaFile: Media, Equatable {
|
||||
public enum TelegramMediaFileDecodingError: Error {
|
||||
case generic
|
||||
}
|
||||
|
||||
public final class TelegramMediaFile: Media, Equatable, Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case data
|
||||
}
|
||||
|
||||
public final class VideoThumbnail: Equatable, PostboxCoding {
|
||||
public let dimensions: PixelDimensions
|
||||
public let resource: TelegramMediaResource
|
||||
@ -338,6 +346,32 @@ public final class TelegramMediaFile: Media, Equatable {
|
||||
encoder.encodeObjectArray(self.attributes, forKey: "at")
|
||||
}
|
||||
|
||||
public required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let data = try container.decode(Data.self, forKey: .data)
|
||||
let postboxDecoder = PostboxDecoder(buffer: MemoryBuffer(data: data))
|
||||
guard let object = postboxDecoder.decodeRootObject() as? TelegramMediaFile else {
|
||||
throw TelegramMediaFileDecodingError.generic
|
||||
}
|
||||
self.fileId = object.fileId
|
||||
self.partialReference = object.partialReference
|
||||
self.resource = object.resource
|
||||
self.previewRepresentations = object.previewRepresentations
|
||||
self.videoThumbnails = object.videoThumbnails
|
||||
self.immediateThumbnailData = object.immediateThumbnailData
|
||||
self.mimeType = object.mimeType
|
||||
self.size = object.size
|
||||
self.attributes = object.attributes
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
let postboxEncoder = PostboxEncoder()
|
||||
postboxEncoder.encodeRootObject(self)
|
||||
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(postboxEncoder.makeData(), forKey: .data)
|
||||
}
|
||||
|
||||
public var fileName: String? {
|
||||
get {
|
||||
for attribute in self.attributes {
|
||||
|
@ -1,7 +1,15 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
|
||||
public enum TelegramMediaImageReferenceDecodingError: Error {
|
||||
case generic
|
||||
}
|
||||
|
||||
public enum TelegramMediaImageReference: PostboxCoding, Equatable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case data
|
||||
}
|
||||
|
||||
case cloud(imageId: Int64, accessHash: Int64, fileReference: Data?)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -28,6 +36,24 @@ public enum TelegramMediaImageReference: PostboxCoding, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let data = try container.decode(Data.self, forKey: .data)
|
||||
let postboxDecoder = PostboxDecoder(buffer: MemoryBuffer(data: data))
|
||||
guard let object = postboxDecoder.decodeRootObject() as? TelegramMediaImageReference else {
|
||||
throw TelegramMediaImageReferenceDecodingError.generic
|
||||
}
|
||||
self = object
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
let postboxEncoder = PostboxEncoder()
|
||||
postboxEncoder.encodeRootObject(self)
|
||||
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(postboxEncoder.makeData(), forKey: .data)
|
||||
}
|
||||
|
||||
public static func ==(lhs: TelegramMediaImageReference, rhs: TelegramMediaImageReference) -> Bool {
|
||||
switch lhs {
|
||||
case let .cloud(imageId, accessHash, fileReference):
|
||||
@ -50,7 +76,15 @@ public struct TelegramMediaImageFlags: OptionSet {
|
||||
public static let hasStickers = TelegramMediaImageFlags(rawValue: 1 << 0)
|
||||
}
|
||||
|
||||
public final class TelegramMediaImage: Media, Equatable {
|
||||
public enum TelegramMediaImageDecodingError: Error {
|
||||
case generic
|
||||
}
|
||||
|
||||
public final class TelegramMediaImage: Media, Equatable, Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case data
|
||||
}
|
||||
|
||||
public let imageId: MediaId
|
||||
public let representations: [TelegramMediaImageRepresentation]
|
||||
public let immediateThumbnailData: Data?
|
||||
@ -104,6 +138,29 @@ public final class TelegramMediaImage: Media, Equatable {
|
||||
encoder.encodeInt32(self.flags.rawValue, forKey: "fl")
|
||||
}
|
||||
|
||||
public required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let data = try container.decode(Data.self, forKey: .data)
|
||||
let postboxDecoder = PostboxDecoder(buffer: MemoryBuffer(data: data))
|
||||
guard let object = postboxDecoder.decodeRootObject() as? TelegramMediaImage else {
|
||||
throw TelegramMediaImageDecodingError.generic
|
||||
}
|
||||
self.imageId = object.imageId
|
||||
self.representations = object.representations
|
||||
self.immediateThumbnailData = object.immediateThumbnailData
|
||||
self.reference = object.reference
|
||||
self.partialReference = object.partialReference
|
||||
self.flags = object.flags
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
let postboxEncoder = PostboxEncoder()
|
||||
postboxEncoder.encodeRootObject(self)
|
||||
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(postboxEncoder.makeData(), forKey: .data)
|
||||
}
|
||||
|
||||
public func representationForDisplayAtSize(_ size: PixelDimensions) -> TelegramMediaImageRepresentation? {
|
||||
if self.representations.count == 0 {
|
||||
return nil
|
||||
|
@ -1,6 +1,15 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
|
||||
public class TelegramMediaWebFile: Media {
|
||||
public enum TelegramMediaWebFileDecodingError: Error {
|
||||
case generic
|
||||
}
|
||||
|
||||
public class TelegramMediaWebFile: Media, Codable, Equatable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case data
|
||||
}
|
||||
|
||||
public let resource: TelegramMediaResource
|
||||
public let mimeType: String
|
||||
public let size: Int32
|
||||
@ -32,23 +41,45 @@ public class TelegramMediaWebFile: Media {
|
||||
encoder.encodeObjectArray(self.attributes, forKey: "at")
|
||||
}
|
||||
|
||||
public required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let data = try container.decode(Data.self, forKey: .data)
|
||||
let postboxDecoder = PostboxDecoder(buffer: MemoryBuffer(data: data))
|
||||
guard let object = postboxDecoder.decodeRootObject() as? TelegramMediaWebFile else {
|
||||
throw TelegramMediaWebFileDecodingError.generic
|
||||
}
|
||||
self.resource = object.resource
|
||||
self.mimeType = object.mimeType
|
||||
self.size = object.size
|
||||
self.attributes = object.attributes
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
let postboxEncoder = PostboxEncoder()
|
||||
postboxEncoder.encodeRootObject(self)
|
||||
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(postboxEncoder.makeData(), forKey: .data)
|
||||
}
|
||||
|
||||
public func isEqual(to other: Media) -> Bool {
|
||||
guard let other = other as? TelegramMediaWebFile else {
|
||||
return false
|
||||
}
|
||||
|
||||
if !self.resource.isEqual(to: other.resource) {
|
||||
return self == other
|
||||
}
|
||||
|
||||
public static func ==(lhs: TelegramMediaWebFile, rhs: TelegramMediaWebFile) -> Bool {
|
||||
if !lhs.resource.isEqual(to: rhs.resource) {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.size != other.size {
|
||||
if lhs.size != rhs.size {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.mimeType != other.mimeType {
|
||||
if lhs.mimeType != rhs.mimeType {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -159,6 +159,8 @@ private var declaredEncodables: Void = {
|
||||
declareEncodable(SynchronizeChatListFiltersOperation.self, f: { SynchronizeChatListFiltersOperation(decoder: $0) })
|
||||
declareEncodable(PromoChatListItem.self, f: { PromoChatListItem(decoder: $0) })
|
||||
declareEncodable(TelegramMediaFile.VideoThumbnail.self, f: { TelegramMediaFile.VideoThumbnail(decoder: $0) })
|
||||
declareEncodable(CachedChatContextResult.self, f: { CachedChatContextResult(decoder: $0) })
|
||||
declareEncodable(PeerAccessRestrictionInfo.self, f: { PeerAccessRestrictionInfo(decoder: $0) })
|
||||
|
||||
return
|
||||
}()
|
||||
|
@ -23,6 +23,9 @@ func applyMediaResourceChanges(from: Media, to: Media, postbox: Postbox, force:
|
||||
if let fromPreview = smallestImageRepresentation(fromFile.previewRepresentations), let toPreview = smallestImageRepresentation(toFile.previewRepresentations) {
|
||||
postbox.mediaBox.moveResourceData(from: fromPreview.resource.id, to: toPreview.resource.id)
|
||||
}
|
||||
if let fromVideoThumbnail = fromFile.videoThumbnails.first, let toVideoThumbnail = toFile.videoThumbnails.first, fromVideoThumbnail.resource.id.uniqueId != toVideoThumbnail.resource.id.uniqueId {
|
||||
postbox.mediaBox.moveResourceData(from: fromVideoThumbnail.resource.id, to: toVideoThumbnail.resource.id)
|
||||
}
|
||||
if (force || fromFile.size == toFile.size || fromFile.resource.size == toFile.resource.size) && fromFile.mimeType == toFile.mimeType {
|
||||
postbox.mediaBox.moveResourceData(from: fromFile.resource.id, to: toFile.resource.id)
|
||||
}
|
||||
|
@ -6,7 +6,15 @@ import MtProtoKit
|
||||
|
||||
import SyncCore
|
||||
|
||||
public enum ChatContextResultMessage: PostboxCoding, Equatable {
|
||||
public enum ChatContextResultMessageDecodingError: Error {
|
||||
case generic
|
||||
}
|
||||
|
||||
public enum ChatContextResultMessage: PostboxCoding, Equatable, Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case data
|
||||
}
|
||||
|
||||
case auto(caption: String, entities: TextEntitiesMessageAttribute?, replyMarkup: ReplyMarkupMessageAttribute?)
|
||||
case text(text: String, entities: TextEntitiesMessageAttribute?, disableUrlPreview: Bool, replyMarkup: ReplyMarkupMessageAttribute?)
|
||||
case mapLocation(media: TelegramMediaMap, replyMarkup: ReplyMarkupMessageAttribute?)
|
||||
@ -75,6 +83,21 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let data = try container.decode(Data.self, forKey: .data)
|
||||
let postboxDecoder = PostboxDecoder(buffer: MemoryBuffer(data: data))
|
||||
self = ChatContextResultMessage(decoder: postboxDecoder)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
let postboxEncoder = PostboxEncoder()
|
||||
self.encode(postboxEncoder)
|
||||
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(postboxEncoder.makeData(), forKey: .data)
|
||||
}
|
||||
|
||||
public static func ==(lhs: ChatContextResultMessage, rhs: ChatContextResultMessage) -> Bool {
|
||||
switch lhs {
|
||||
case let .auto(lhsCaption, lhsEntities, lhsReplyMarkup):
|
||||
@ -138,156 +161,167 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChatContextResult: Equatable {
|
||||
case externalReference(queryId: Int64, id: String, type: String, title: String?, description: String?, url: String?, content: TelegramMediaWebFile?, thumbnail: TelegramMediaWebFile?, message: ChatContextResultMessage)
|
||||
case internalReference(queryId: Int64, id: String, type: String, title: String?, description: String?, image: TelegramMediaImage?, file: TelegramMediaFile?, message: ChatContextResultMessage)
|
||||
public enum ChatContextResultDecodingError: Error {
|
||||
case generic
|
||||
}
|
||||
|
||||
public enum ChatContextResult: Equatable, Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case externalReference
|
||||
case internalReference
|
||||
}
|
||||
|
||||
public struct ExternalReference: Equatable, Codable {
|
||||
public let queryId: Int64
|
||||
public let id: String
|
||||
public let type: String
|
||||
public let title: String?
|
||||
public let description: String?
|
||||
public let url: String?
|
||||
public let content: TelegramMediaWebFile?
|
||||
public let thumbnail: TelegramMediaWebFile?
|
||||
public let message: ChatContextResultMessage
|
||||
|
||||
public init(
|
||||
queryId: Int64,
|
||||
id: String,
|
||||
type: String,
|
||||
title: String?,
|
||||
description: String?,
|
||||
url: String?,
|
||||
content: TelegramMediaWebFile?,
|
||||
thumbnail: TelegramMediaWebFile?,
|
||||
message: ChatContextResultMessage
|
||||
) {
|
||||
self.queryId = queryId
|
||||
self.id = id
|
||||
self.type = type
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.url = url
|
||||
self.content = content
|
||||
self.thumbnail = thumbnail
|
||||
self.message = message
|
||||
}
|
||||
}
|
||||
|
||||
public struct InternalReference: Equatable, Codable {
|
||||
public let queryId: Int64
|
||||
public let id: String
|
||||
public let type: String
|
||||
public let title: String?
|
||||
public let description: String?
|
||||
public let image: TelegramMediaImage?
|
||||
public let file: TelegramMediaFile?
|
||||
public let message: ChatContextResultMessage
|
||||
|
||||
public init(
|
||||
queryId: Int64,
|
||||
id: String,
|
||||
type: String,
|
||||
title: String?,
|
||||
description: String?,
|
||||
image: TelegramMediaImage?,
|
||||
file: TelegramMediaFile?,
|
||||
message: ChatContextResultMessage
|
||||
) {
|
||||
self.queryId = queryId
|
||||
self.id = id
|
||||
self.type = type
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.image = image
|
||||
self.file = file
|
||||
self.message = message
|
||||
}
|
||||
}
|
||||
|
||||
case externalReference(ExternalReference)
|
||||
case internalReference(InternalReference)
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
if let externalReference = try? container.decode(ExternalReference.self, forKey: .externalReference) {
|
||||
self = .externalReference(externalReference)
|
||||
} else if let internalReference = try? container.decode(InternalReference.self, forKey: .internalReference) {
|
||||
self = .internalReference(internalReference)
|
||||
} else {
|
||||
throw ChatContextResultDecodingError.generic
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
switch self {
|
||||
case let .internalReference(internalReference):
|
||||
try container.encode(internalReference, forKey: .internalReference)
|
||||
case let .externalReference(externalReference):
|
||||
try container.encode(externalReference, forKey: .externalReference)
|
||||
}
|
||||
}
|
||||
|
||||
public var queryId: Int64 {
|
||||
switch self {
|
||||
case let .externalReference(queryId, _, _, _, _, _, _, _, _):
|
||||
return queryId
|
||||
case let .internalReference(queryId, _, _, _, _, _, _, _):
|
||||
return queryId
|
||||
case let .externalReference(externalReference):
|
||||
return externalReference.queryId
|
||||
case let .internalReference(internalReference):
|
||||
return internalReference.queryId
|
||||
}
|
||||
}
|
||||
|
||||
public var id: String {
|
||||
switch self {
|
||||
case let .externalReference(_, id, _, _, _, _, _, _, _):
|
||||
return id
|
||||
case let .internalReference(_, id, _, _, _, _, _, _):
|
||||
return id
|
||||
case let .externalReference(externalReference):
|
||||
return externalReference.id
|
||||
case let .internalReference(internalReference):
|
||||
return internalReference.id
|
||||
}
|
||||
}
|
||||
|
||||
public var type: String {
|
||||
switch self {
|
||||
case let .externalReference(_, _, type, _, _, _, _, _, _):
|
||||
return type
|
||||
case let .internalReference(_, _, type, _, _, _, _, _):
|
||||
return type
|
||||
case let .externalReference(externalReference):
|
||||
return externalReference.type
|
||||
case let .internalReference(internalReference):
|
||||
return internalReference.type
|
||||
}
|
||||
}
|
||||
|
||||
public var title: String? {
|
||||
switch self {
|
||||
case let .externalReference(_, _, _, title, _, _, _, _, _):
|
||||
return title
|
||||
case let .internalReference(_, _, _, title, _, _, _, _):
|
||||
return title
|
||||
case let .externalReference(externalReference):
|
||||
return externalReference.title
|
||||
case let .internalReference(internalReference):
|
||||
return internalReference.title
|
||||
}
|
||||
}
|
||||
|
||||
public var description: String? {
|
||||
switch self {
|
||||
case let .externalReference(_, _, _, _, description, _, _, _, _):
|
||||
return description
|
||||
case let .internalReference(_, _, _, _, description, _, _, _):
|
||||
return description
|
||||
case let .externalReference(externalReference):
|
||||
return externalReference.description
|
||||
case let .internalReference(internalReference):
|
||||
return internalReference.description
|
||||
}
|
||||
}
|
||||
|
||||
public var message: ChatContextResultMessage {
|
||||
switch self {
|
||||
case let .externalReference(_, _, _, _, _, _, _, _, message):
|
||||
return message
|
||||
case let .internalReference(_, _, _, _, _, _, _, message):
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: ChatContextResult, rhs: ChatContextResult) -> Bool {
|
||||
switch lhs {
|
||||
//id: String, type: String, title: String?, description: String?, url: String?, content: TelegramMediaWebFile?, thumbnail: TelegramMediaWebFile?, message: ChatContextResultMessage
|
||||
case let .externalReference(lhsQueryId, lhsId, lhsType, lhsTitle, lhsDescription, lhsUrl, lhsContent, lhsThumbnail, lhsMessage):
|
||||
if case let .externalReference(rhsQueryId, rhsId, rhsType, rhsTitle, rhsDescription, rhsUrl, rhsContent, rhsThumbnail, rhsMessage) = rhs {
|
||||
if lhsQueryId != rhsQueryId {
|
||||
return false
|
||||
}
|
||||
if lhsId != rhsId {
|
||||
return false
|
||||
}
|
||||
if lhsType != rhsType {
|
||||
return false
|
||||
}
|
||||
if lhsTitle != rhsTitle {
|
||||
return false
|
||||
}
|
||||
if lhsDescription != rhsDescription {
|
||||
return false
|
||||
}
|
||||
if lhsUrl != rhsUrl {
|
||||
return false
|
||||
}
|
||||
if let lhsContent = lhsContent, let rhsContent = rhsContent {
|
||||
if !lhsContent.isEqual(to: rhsContent) {
|
||||
return false
|
||||
}
|
||||
} else if (lhsContent != nil) != (rhsContent != nil) {
|
||||
return false
|
||||
}
|
||||
if let lhsThumbnail = lhsThumbnail, let rhsThumbnail = rhsThumbnail {
|
||||
if !lhsThumbnail.isEqual(to: rhsThumbnail) {
|
||||
return false
|
||||
}
|
||||
} else if (lhsThumbnail != nil) != (rhsThumbnail != nil) {
|
||||
return false
|
||||
}
|
||||
if lhsMessage != rhsMessage {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .internalReference(lhsQueryId, lhsId, lhsType, lhsTitle, lhsDescription, lhsImage, lhsFile, lhsMessage):
|
||||
if case let .internalReference(rhsQueryId, rhsId, rhsType, rhsTitle, rhsDescription, rhsImage, rhsFile, rhsMessage) = rhs {
|
||||
if lhsQueryId != rhsQueryId {
|
||||
return false
|
||||
}
|
||||
if lhsId != rhsId {
|
||||
return false
|
||||
}
|
||||
if lhsType != rhsType {
|
||||
return false
|
||||
}
|
||||
if lhsTitle != rhsTitle {
|
||||
return false
|
||||
}
|
||||
if lhsDescription != rhsDescription {
|
||||
return false
|
||||
}
|
||||
if let lhsImage = lhsImage, let rhsImage = rhsImage {
|
||||
if !lhsImage.isEqual(to: rhsImage) {
|
||||
return false
|
||||
}
|
||||
} else if (lhsImage != nil) != (rhsImage != nil) {
|
||||
return false
|
||||
}
|
||||
if let lhsFile = lhsFile, let rhsFile = rhsFile {
|
||||
if !lhsFile.isEqual(to: rhsFile) {
|
||||
return false
|
||||
}
|
||||
} else if (lhsFile != nil) != (rhsFile != nil) {
|
||||
return false
|
||||
}
|
||||
if lhsMessage != rhsMessage {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .externalReference(externalReference):
|
||||
return externalReference.message
|
||||
case let .internalReference(internalReference):
|
||||
return internalReference.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChatContextResultCollectionPresentation {
|
||||
public enum ChatContextResultCollectionPresentation: Int32, Codable {
|
||||
case media
|
||||
case list
|
||||
}
|
||||
|
||||
public struct ChatContextResultSwitchPeer: Equatable {
|
||||
public struct ChatContextResultSwitchPeer: Equatable, Codable {
|
||||
public let text: String
|
||||
public let startParam: String
|
||||
|
||||
@ -296,11 +330,21 @@ public struct ChatContextResultSwitchPeer: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public final class ChatContextResultCollection: Equatable {
|
||||
public final class ChatContextResultCollection: Equatable, Codable {
|
||||
public struct GeoPoint: Equatable, Codable {
|
||||
public let latitude: Double
|
||||
public let longitude: Double
|
||||
|
||||
public init(latitude: Double, longitude: Double) {
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
}
|
||||
}
|
||||
|
||||
public let botId: PeerId
|
||||
public let peerId: PeerId
|
||||
public let query: String
|
||||
public let geoPoint: (Double, Double)?
|
||||
public let geoPoint: ChatContextResultCollection.GeoPoint?
|
||||
public let queryId: Int64
|
||||
public let nextOffset: String?
|
||||
public let presentation: ChatContextResultCollectionPresentation
|
||||
@ -308,7 +352,7 @@ public final class ChatContextResultCollection: Equatable {
|
||||
public let results: [ChatContextResult]
|
||||
public let cacheTimeout: Int32
|
||||
|
||||
public init(botId: PeerId, peerId: PeerId, query: String, geoPoint: (Double, Double)?, queryId: Int64, nextOffset: String?, presentation: ChatContextResultCollectionPresentation, switchPeer: ChatContextResultSwitchPeer?, results: [ChatContextResult], cacheTimeout: Int32) {
|
||||
public init(botId: PeerId, peerId: PeerId, query: String, geoPoint: ChatContextResultCollection.GeoPoint?, queryId: Int64, nextOffset: String?, presentation: ChatContextResultCollectionPresentation, switchPeer: ChatContextResultSwitchPeer?, results: [ChatContextResult], cacheTimeout: Int32) {
|
||||
self.botId = botId
|
||||
self.peerId = peerId
|
||||
self.query = query
|
||||
@ -334,7 +378,7 @@ public final class ChatContextResultCollection: Equatable {
|
||||
if lhs.query != rhs.query {
|
||||
return false
|
||||
}
|
||||
if lhs.geoPoint?.0 != rhs.geoPoint?.0 || lhs.geoPoint?.1 != rhs.geoPoint?.1 {
|
||||
if lhs.geoPoint != rhs.geoPoint {
|
||||
return false
|
||||
}
|
||||
if lhs.nextOffset != rhs.nextOffset {
|
||||
@ -408,7 +452,7 @@ extension ChatContextResult {
|
||||
init(apiResult: Api.BotInlineResult, queryId: Int64) {
|
||||
switch apiResult {
|
||||
case let .botInlineResult(_, id, type, title, description, url, thumb, content, sendMessage):
|
||||
self = .externalReference(queryId: queryId, id: id, type: type, title: title, description: description, url: url, content: content.flatMap(TelegramMediaWebFile.init), thumbnail: thumb.flatMap(TelegramMediaWebFile.init), message: ChatContextResultMessage(apiMessage: sendMessage))
|
||||
self = .externalReference(ChatContextResult.ExternalReference(queryId: queryId, id: id, type: type, title: title, description: description, url: url, content: content.flatMap(TelegramMediaWebFile.init), thumbnail: thumb.flatMap(TelegramMediaWebFile.init), message: ChatContextResultMessage(apiMessage: sendMessage)))
|
||||
case let .botInlineMediaResult(_, id, type, photo, document, title, description, sendMessage):
|
||||
var image: TelegramMediaImage?
|
||||
var file: TelegramMediaFile?
|
||||
@ -418,7 +462,7 @@ extension ChatContextResult {
|
||||
if let document = document, let parsedFile = telegramMediaFileFromApiDocument(document) {
|
||||
file = parsedFile
|
||||
}
|
||||
self = .internalReference(queryId: queryId, id: id, type: type, title: title, description: description, image: image, file: file, message: ChatContextResultMessage(apiMessage: sendMessage))
|
||||
self = .internalReference(ChatContextResult.InternalReference(queryId: queryId, id: id, type: type, title: title, description: description, image: image, file: file, message: ChatContextResultMessage(apiMessage: sendMessage)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -449,7 +493,10 @@ extension ChatContextResultCollection {
|
||||
return true
|
||||
}
|
||||
})*/
|
||||
self.init(botId: botId, peerId: peerId, query: query, geoPoint: geoPoint, queryId: queryId, nextOffset: nextOffset, presentation: (flags & (1 << 0) != 0) ? .media : .list, switchPeer: switchPeer, results: parsedResults, cacheTimeout: cacheTime)
|
||||
let mappedGeoPoint = geoPoint.flatMap { geoPoint -> ChatContextResultCollection.GeoPoint in
|
||||
return ChatContextResultCollection.GeoPoint(latitude: geoPoint.0, longitude: geoPoint.1)
|
||||
}
|
||||
self.init(botId: botId, peerId: peerId, query: query, geoPoint: mappedGeoPoint, queryId: queryId, nextOffset: nextOffset, presentation: (flags & (1 << 0) != 0) ? .media : .list, switchPeer: switchPeer, results: parsedResults, cacheTimeout: cacheTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
|
||||
attributes.append(replyMarkup)
|
||||
}
|
||||
switch result {
|
||||
case let .internalReference(_, id, type, title, description, image, file, message):
|
||||
if type == "game" {
|
||||
case let .internalReference(internalReference):
|
||||
if internalReference.type == "game" {
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
let filteredAttributes = attributes.filter { attribute in
|
||||
if let _ = attribute as? ReplyMarkupMessageAttribute {
|
||||
@ -31,26 +31,26 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
|
||||
}
|
||||
return true
|
||||
}
|
||||
if let media: Media = file ?? image {
|
||||
if let media: Media = internalReference.file ?? internalReference.image {
|
||||
return .message(text: caption, attributes: filteredAttributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil)
|
||||
} else {
|
||||
return .message(text: caption, attributes: filteredAttributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)
|
||||
}
|
||||
} else {
|
||||
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: title ?? "", description: description ?? "", image: image, file: file)), replyToMessageId: nil, localGroupingKey: nil)
|
||||
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: internalReference.title ?? "", description: internalReference.description ?? "", image: internalReference.image, file: internalReference.file)), replyToMessageId: nil, localGroupingKey: nil)
|
||||
}
|
||||
} else if let file = file, type == "gif" {
|
||||
} else if let file = internalReference.file, internalReference.type == "gif" {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
||||
} else if let image = image {
|
||||
} else if let image = internalReference.image {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: image), replyToMessageId: nil, localGroupingKey: nil)
|
||||
} else if let file = file {
|
||||
} else if let file = internalReference.file {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case let .externalReference(_, id, type, title, description, url, content, thumbnail, message):
|
||||
if type == "photo" {
|
||||
if let thumbnail = thumbnail {
|
||||
case let .externalReference(externalReference):
|
||||
if externalReference.type == "photo" {
|
||||
if let thumbnail = externalReference.thumbnail {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let thumbnailResource = thumbnail.resource
|
||||
@ -60,16 +60,22 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
|
||||
} else {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)
|
||||
}
|
||||
} else if type == "document" || type == "gif" || type == "audio" || type == "voice" {
|
||||
} else if externalReference.type == "document" || externalReference.type == "gif" || externalReference.type == "audio" || externalReference.type == "voice" {
|
||||
var videoThumbnails: [TelegramMediaFile.VideoThumbnail] = []
|
||||
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||
if let thumbnail = thumbnail {
|
||||
if let thumbnail = externalReference.thumbnail {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let thumbnailResource = thumbnail.resource
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource))
|
||||
|
||||
if thumbnail.mimeType.hasPrefix("video/") {
|
||||
videoThumbnails.append(TelegramMediaFile.VideoThumbnail(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource))
|
||||
} else {
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource))
|
||||
}
|
||||
}
|
||||
var fileName = "file"
|
||||
if let content = content {
|
||||
if let content = externalReference.content {
|
||||
var contentUrl: String?
|
||||
if let resource = content.resource as? HttpReferenceMediaResource {
|
||||
contentUrl = resource.url
|
||||
@ -86,32 +92,32 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
|
||||
var fileAttributes: [TelegramMediaFileAttribute] = []
|
||||
fileAttributes.append(.FileName(fileName: fileName))
|
||||
|
||||
if type == "gif" {
|
||||
if externalReference.type == "gif" {
|
||||
fileAttributes.append(.Animated)
|
||||
}
|
||||
|
||||
if let dimensions = content?.dimensions {
|
||||
if let dimensions = externalReference.content?.dimensions {
|
||||
fileAttributes.append(.ImageSize(size: dimensions))
|
||||
if type == "gif" {
|
||||
fileAttributes.append(.Video(duration: Int(Int32(content?.duration ?? 0)), size: dimensions, flags: []))
|
||||
if externalReference.type == "gif" {
|
||||
fileAttributes.append(.Video(duration: Int(Int32(externalReference.content?.duration ?? 0)), size: dimensions, flags: []))
|
||||
}
|
||||
}
|
||||
|
||||
if type == "audio" || type == "voice" {
|
||||
fileAttributes.append(.Audio(isVoice: type == "voice", duration: Int(Int32(content?.duration ?? 0)), title: title, performer: description, waveform: nil))
|
||||
if externalReference.type == "audio" || externalReference.type == "voice" {
|
||||
fileAttributes.append(.Audio(isVoice: externalReference.type == "voice", duration: Int(Int32(externalReference.content?.duration ?? 0)), title: externalReference.title, performer: externalReference.description, waveform: nil))
|
||||
}
|
||||
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
|
||||
let resource: TelegramMediaResource
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat, let webResource = content?.resource as? WebFileReferenceMediaResource {
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat, let webResource = externalReference.content?.resource as? WebFileReferenceMediaResource {
|
||||
resource = webResource
|
||||
} else {
|
||||
resource = EmptyMediaResource()
|
||||
}
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: content?.mimeType ?? "application/binary", size: nil, attributes: fileAttributes)
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: videoThumbnails, immediateThumbnailData: nil, mimeType: externalReference.content?.mimeType ?? "application/binary", size: nil, attributes: fileAttributes)
|
||||
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
||||
} else {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)
|
||||
|
@ -10,6 +10,33 @@ public enum RequestChatContextResultsError {
|
||||
case locationRequired
|
||||
}
|
||||
|
||||
public final class CachedChatContextResult: PostboxCoding {
|
||||
public let data: Data
|
||||
|
||||
public init(data: Data) {
|
||||
self.data = data
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.data = decoder.decodeDataForKey("data") ?? Data()
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeData(self.data, forKey: "data")
|
||||
}
|
||||
}
|
||||
|
||||
private let collectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 40, highWaterItemCount: 60)
|
||||
|
||||
private struct RequestData: Codable {
|
||||
let version: String
|
||||
let botId: PeerId
|
||||
let peerId: PeerId
|
||||
let query: String
|
||||
}
|
||||
|
||||
private let requestVersion = "1"
|
||||
|
||||
public func requestChatContextResults(account: Account, botId: PeerId, peerId: PeerId, query: String, location: Signal<(Double, Double)?, NoError> = .single(nil), offset: String) -> Signal<ChatContextResultCollection?, RequestChatContextResultsError> {
|
||||
return account.postbox.transaction { transaction -> (bot: Peer, peer: Peer)? in
|
||||
if let bot = transaction.getPeer(botId), let peer = transaction.getPeer(peerId) {
|
||||
@ -31,7 +58,23 @@ public func requestChatContextResults(account: Account, botId: PeerId, peerId: P
|
||||
}
|
||||
|> castError(RequestChatContextResultsError.self)
|
||||
|> mapToSignal { botAndPeer, location -> Signal<ChatContextResultCollection?, RequestChatContextResultsError> in
|
||||
if let (bot, peer) = botAndPeer, let inputBot = apiInputUser(bot) {
|
||||
guard let (bot, peer) = botAndPeer, let inputBot = apiInputUser(bot) else {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> Signal<ChatContextResultCollection?, RequestChatContextResultsError> in
|
||||
if offset.isEmpty && location == nil {
|
||||
let requestData = RequestData(version: requestVersion, botId: botId, peerId: peerId, query: query)
|
||||
if let keyData = try? JSONEncoder().encode(requestData) {
|
||||
let key = ValueBoxKey(MemoryBuffer(data: keyData))
|
||||
if let cachedEntry = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedContextResults, key: key)) as? CachedChatContextResult {
|
||||
if let cachedResult = try? JSONDecoder().decode(ChatContextResultCollection.self, from: cachedEntry.data) {
|
||||
return .single(cachedResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var flags: Int32 = 0
|
||||
var inputPeer: Api.InputPeer = .inputPeerEmpty
|
||||
var geoPoint: Api.InputGeoPoint?
|
||||
@ -53,8 +96,27 @@ public func requestChatContextResults(account: Account, botId: PeerId, peerId: P
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
|> mapToSignal { result -> Signal<ChatContextResultCollection?, RequestChatContextResultsError> in
|
||||
guard let result = result else {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> ChatContextResultCollection? in
|
||||
if result.cacheTimeout > 10 {
|
||||
if let resultData = try? JSONEncoder().encode(result) {
|
||||
let requestData = RequestData(version: requestVersion, botId: botId, peerId: peerId, query: query)
|
||||
if let keyData = try? JSONEncoder().encode(requestData) {
|
||||
let key = ValueBoxKey(MemoryBuffer(data: keyData))
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedContextResults, key: key), entry: CachedChatContextResult(data: resultData), collectionSpec: collectionSpec)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|> castError(RequestChatContextResultsError.self)
|
||||
}
|
||||
}
|
||||
|> castError(RequestChatContextResultsError.self)
|
||||
|> switchToLatest
|
||||
}
|
||||
}
|
||||
|
@ -153,25 +153,25 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont
|
||||
var videoFileReference: FileMediaReference?
|
||||
var imageDimensions: CGSize?
|
||||
switch self.contextResult {
|
||||
case let .externalReference(_, _, type, title, _, url, content, thumbnail, _):
|
||||
if let content = content {
|
||||
case let .externalReference(externalReference):
|
||||
if let content = externalReference.content {
|
||||
imageResource = content.resource
|
||||
} else if let thumbnail = thumbnail {
|
||||
} else if let thumbnail = externalReference.thumbnail {
|
||||
imageResource = thumbnail.resource
|
||||
}
|
||||
imageDimensions = content?.dimensions?.cgSize
|
||||
if let content = content, type == "gif", let thumbnailResource = imageResource
|
||||
imageDimensions = externalReference.content?.dimensions?.cgSize
|
||||
if let content = externalReference.content, externalReference.type == "gif", let thumbnailResource = imageResource
|
||||
, let dimensions = content.dimensions {
|
||||
videoFileReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
|
||||
imageResource = nil
|
||||
}
|
||||
case let .internalReference(_, _, _, title, _, image, file, _):
|
||||
if let image = image {
|
||||
case let .internalReference(internalReference):
|
||||
if let image = internalReference.image {
|
||||
if let largestRepresentation = largestImageRepresentation(image.representations) {
|
||||
imageDimensions = largestRepresentation.dimensions.cgSize
|
||||
}
|
||||
imageResource = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 200, height: 100))?.resource
|
||||
} else if let file = file {
|
||||
} else if let file = internalReference.file {
|
||||
if let dimensions = file.dimensions {
|
||||
imageDimensions = dimensions.cgSize
|
||||
} else if let largestRepresentation = largestImageRepresentation(file.previewRepresentations) {
|
||||
@ -180,7 +180,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont
|
||||
imageResource = smallestImageRepresentation(file.previewRepresentations)?.resource
|
||||
}
|
||||
|
||||
if let file = file {
|
||||
if let file = internalReference.file {
|
||||
if file.isVideo && file.isAnimated {
|
||||
videoFileReference = .standalone(media: file)
|
||||
imageResource = nil
|
||||
|
@ -478,7 +478,13 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
currentBackgroundNode.image = backgroundImage
|
||||
}
|
||||
}
|
||||
strongSelf.backgroundNode?.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||
if let backgroundNode = strongSelf.backgroundNode {
|
||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .spring) : .immediate
|
||||
if let previousLayoutSize = previousLayoutSize {
|
||||
backgroundNode.frame = backgroundNode.frame.offsetBy(dx: layoutSize.width - previousLayoutSize.width, dy: 0.0)
|
||||
}
|
||||
transition.updateFrame(node: backgroundNode, frame: CGRect(origin: CGPoint(), size: layoutSize))
|
||||
}
|
||||
} else {
|
||||
if let backgroundNode = strongSelf.backgroundNode {
|
||||
backgroundNode.removeFromSupernode()
|
||||
|
@ -1113,7 +1113,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, decimalSeparator: decimalSeparator)) / \(dataSizeString(size, forceDecimal: true, decimalSeparator: decimalSeparator))", size: nil, muted: false, active: false)
|
||||
}
|
||||
} else if let _ = file.duration {
|
||||
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: strings.Conversation_Processing, size: nil, muted: false, active: active)
|
||||
if file.isAnimated {
|
||||
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: "\(gifTitle)", size: nil, muted: false, active: false)
|
||||
} else {
|
||||
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: strings.Conversation_Processing, size: nil, muted: false, active: active)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if isMediaStreamable(message: message, media: file), let size = file.size {
|
||||
|
@ -71,24 +71,24 @@ func paneGifSearchForQuery(account: Account, query: String, updateActivity: ((Bo
|
||||
var references: [FileMediaReference] = []
|
||||
for result in results {
|
||||
switch result {
|
||||
case let .externalReference(_, _, type, _, _, _, content, thumbnail, _):
|
||||
case let .externalReference(externalReference):
|
||||
var imageResource: TelegramMediaResource?
|
||||
var uniqueId: Int64?
|
||||
if let content = content {
|
||||
if let content = externalReference.content {
|
||||
imageResource = content.resource
|
||||
if let resource = content.resource as? WebFileReferenceMediaResource {
|
||||
uniqueId = Int64(HashFunctions.murMurHash32(resource.url))
|
||||
}
|
||||
} else if let thumbnail = thumbnail {
|
||||
} else if let thumbnail = externalReference.thumbnail {
|
||||
imageResource = thumbnail.resource
|
||||
}
|
||||
|
||||
if type == "gif", let thumbnailResource = imageResource, let content = content, let dimensions = content.dimensions {
|
||||
if externalReference.type == "gif", let thumbnailResource = imageResource, let content = externalReference.content, let dimensions = content.dimensions {
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: uniqueId ?? 0), partialReference: nil, resource: thumbnailResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])])
|
||||
references.append(FileMediaReference.standalone(media: file))
|
||||
}
|
||||
case let .internalReference(_, _, _, _, _, _, file, _):
|
||||
if let file = file {
|
||||
case let .internalReference(internalReference):
|
||||
if let file = internalReference.file {
|
||||
references.append(FileMediaReference.standalone(media: file))
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +213,10 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
|
||||
return
|
||||
}
|
||||
self.isLoadingMore = true
|
||||
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(currentProcessedResults.geoPoint), offset: nextOffset)
|
||||
let geoPoint = currentProcessedResults.geoPoint.flatMap { geoPoint -> (Double, Double) in
|
||||
return (geoPoint.latitude, geoPoint.longitude)
|
||||
}
|
||||
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(geoPoint), offset: nextOffset)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] nextResults in
|
||||
guard let strongSelf = self, let nextResults = nextResults else {
|
||||
return
|
||||
|
@ -212,14 +212,14 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
var videoFile: TelegramMediaFile?
|
||||
var imageDimensions: CGSize?
|
||||
switch item.result {
|
||||
case let .externalReference(_, _, type, _, _, _, content, thumbnail, _):
|
||||
if let content = content {
|
||||
case let .externalReference(externalReference):
|
||||
if let content = externalReference.content {
|
||||
imageResource = content.resource
|
||||
} else if let thumbnail = thumbnail {
|
||||
} else if let thumbnail = externalReference.thumbnail {
|
||||
imageResource = thumbnail.resource
|
||||
}
|
||||
imageDimensions = content?.dimensions?.cgSize
|
||||
if type == "gif", let thumbnailResource = thumbnail?.resource, let content = content, let dimensions = content.dimensions {
|
||||
imageDimensions = externalReference.content?.dimensions?.cgSize
|
||||
if externalReference.type == "gif", let thumbnailResource = externalReference.thumbnail?.resource, let content = externalReference.content, let dimensions = content.dimensions {
|
||||
videoFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: thumbnailResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])])
|
||||
imageResource = nil
|
||||
}
|
||||
@ -229,13 +229,13 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
} else if let imageResource = imageResource {
|
||||
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource)
|
||||
}
|
||||
case let .internalReference(_, _, _, _, _, image, file, _):
|
||||
if let image = image {
|
||||
case let .internalReference(internalReference):
|
||||
if let image = internalReference.image {
|
||||
if let largestRepresentation = largestImageRepresentation(image.representations) {
|
||||
imageDimensions = largestRepresentation.dimensions.cgSize
|
||||
}
|
||||
imageResource = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 200, height: 100))?.resource
|
||||
} else if let file = file {
|
||||
} else if let file = internalReference.file {
|
||||
if let dimensions = file.dimensions {
|
||||
imageDimensions = dimensions.cgSize
|
||||
} else if let largestRepresentation = largestImageRepresentation(file.previewRepresentations) {
|
||||
@ -252,7 +252,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
if let file = file {
|
||||
if let file = internalReference.file {
|
||||
if file.isVideo && file.isAnimated {
|
||||
videoFile = file
|
||||
imageResource = nil
|
||||
|
@ -1176,7 +1176,7 @@ private func calculateItemFrames(items: [VisualMediaItem], containerWidth: CGFlo
|
||||
weights.append(Int(item.aspectRatio * 100))
|
||||
}
|
||||
|
||||
let preferredRowSize: CGFloat = 160.0
|
||||
let preferredRowSize: CGFloat = 100.0
|
||||
let idealHeight: CGFloat = preferredRowSize
|
||||
|
||||
var totalItemSize: CGFloat = 0.0
|
||||
|
@ -23,6 +23,7 @@ final class SoftwareVideoLayerFrameManager {
|
||||
|
||||
private let account: Account
|
||||
private let resource: MediaResource
|
||||
private let secondaryResource: MediaResource?
|
||||
private let queue: ThreadPoolQueue
|
||||
private let layerHolder: SampleBufferLayer
|
||||
|
||||
@ -33,10 +34,12 @@ final class SoftwareVideoLayerFrameManager {
|
||||
|
||||
init(account: Account, fileReference: FileMediaReference, layerHolder: SampleBufferLayer) {
|
||||
var resource = fileReference.media.resource
|
||||
var secondaryResource: MediaResource?
|
||||
for attribute in fileReference.media.attributes {
|
||||
if case .Video = attribute {
|
||||
if let thumbnail = fileReference.media.videoThumbnails.first {
|
||||
resource = thumbnail.resource
|
||||
secondaryResource = fileReference.media.resource
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -44,6 +47,7 @@ final class SoftwareVideoLayerFrameManager {
|
||||
nextWorker += 1
|
||||
self.account = account
|
||||
self.resource = resource
|
||||
self.secondaryResource = secondaryResource
|
||||
self.queue = ThreadPoolQueue(threadPool: workers)
|
||||
self.layerHolder = layerHolder
|
||||
layerHolder.layer.videoGravity = .resizeAspectFill
|
||||
@ -57,9 +61,38 @@ final class SoftwareVideoLayerFrameManager {
|
||||
}
|
||||
|
||||
func start() {
|
||||
self.dataDisposable.set((self.account.postbox.mediaBox.resourceData(self.resource, option: .complete(waitUntilFetchStatus: false)) |> deliverOn(applyQueue)).start(next: { [weak self] data in
|
||||
if let strongSelf = self, data.complete {
|
||||
let _ = strongSelf.source.swap(SoftwareVideoSource(path: data.path))
|
||||
let secondarySignal: Signal<String?, NoError>
|
||||
if let secondaryResource = self.secondaryResource {
|
||||
secondarySignal = self.account.postbox.mediaBox.resourceData(self.resource, option: .complete(waitUntilFetchStatus: false))
|
||||
|> map { data -> String? in
|
||||
if data.complete {
|
||||
return data.path
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
secondarySignal = .single(nil)
|
||||
}
|
||||
|
||||
let firstReady: Signal<String, NoError> = combineLatest(
|
||||
self.account.postbox.mediaBox.resourceData(self.resource, option: .complete(waitUntilFetchStatus: false)),
|
||||
secondarySignal
|
||||
)
|
||||
|> mapToSignal { first, second -> Signal<String, NoError> in
|
||||
if first.complete {
|
||||
return .single(first.path)
|
||||
} else if let second = second {
|
||||
return .single(second)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
|
||||
self.dataDisposable.set((firstReady |> deliverOn(applyQueue)).start(next: { [weak self] path in
|
||||
if let strongSelf = self {
|
||||
let _ = strongSelf.source.swap(SoftwareVideoSource(path: path))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -331,7 +331,10 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex
|
||||
return
|
||||
}
|
||||
self.isLoadingMore = true
|
||||
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(currentProcessedResults.geoPoint), offset: nextOffset)
|
||||
let geoPoint = currentProcessedResults.geoPoint.flatMap { geoPoint -> (Double, Double) in
|
||||
return (geoPoint.latitude, geoPoint.longitude)
|
||||
}
|
||||
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(geoPoint), offset: nextOffset)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] nextResults in
|
||||
guard let strongSelf = self, let nextResults = nextResults else {
|
||||
return
|
||||
|
@ -181,14 +181,14 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode {
|
||||
var imageResource: TelegramMediaResource?
|
||||
var stickerFile: TelegramMediaFile?
|
||||
switch item.result {
|
||||
case let .externalReference(_, _, _, _, _, url, content, thumbnail, _):
|
||||
if let thumbnail = thumbnail {
|
||||
case let .externalReference(externalReference):
|
||||
if let thumbnail = externalReference.thumbnail {
|
||||
imageResource = thumbnail.resource
|
||||
}
|
||||
var selectedUrl: String?
|
||||
if let url = url {
|
||||
if let url = externalReference.url {
|
||||
selectedUrl = url
|
||||
} else if let content = content {
|
||||
} else if let content = externalReference.content {
|
||||
if let resource = content.resource as? HttpReferenceMediaResource {
|
||||
selectedUrl = resource.url
|
||||
} else if let resource = content.resource as? WebFileReferenceMediaResource {
|
||||
@ -200,10 +200,10 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode {
|
||||
iconText = NSAttributedString(string: host.substring(to: host.index(after: host.startIndex)).uppercased(), font: iconFont, textColor: UIColor.white)
|
||||
}
|
||||
}
|
||||
case let .internalReference(_, _, _, _, _, image, file, _):
|
||||
if let image = image {
|
||||
case let .internalReference(internalReference):
|
||||
if let image = internalReference.image {
|
||||
imageResource = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 200, height: 200))?.resource
|
||||
} else if let file = file {
|
||||
} else if let file = internalReference.file {
|
||||
if file.isSticker {
|
||||
stickerFile = file
|
||||
imageResource = file.resource
|
||||
|
@ -357,6 +357,8 @@ public func shouldDownloadMediaAutomatically(settings: MediaAutoDownloadSettings
|
||||
return false
|
||||
}
|
||||
return size <= sizeLimit
|
||||
} else if media.id?.namespace == Namespaces.Media.LocalFile {
|
||||
return true
|
||||
} else if category.sizeLimit == Int32.max {
|
||||
return true
|
||||
} else {
|
||||
|
@ -85,9 +85,16 @@ public final class NativeVideoContent: UniversalVideoContent {
|
||||
private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContentNode {
|
||||
private let postbox: Postbox
|
||||
private let fileReference: FileMediaReference
|
||||
private let enableSound: Bool
|
||||
private let loopVideo: Bool
|
||||
private let baseRate: Double
|
||||
private let audioSessionManager: ManagedAudioSession
|
||||
|
||||
private let player: MediaPlayer
|
||||
private var thumbnailPlayer: MediaPlayer?
|
||||
private let imageNode: TransformImageNode
|
||||
private let playerNode: MediaPlayerNode
|
||||
private var thumbnailNode: MediaPlayerNode?
|
||||
private let playbackCompletedListeners = Bag<() -> Void>()
|
||||
|
||||
private let placeholderColor: UIColor
|
||||
@ -109,20 +116,28 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
||||
}
|
||||
|
||||
private let fetchDisposable = MetaDisposable()
|
||||
private let fetchStatusDisposable = MetaDisposable()
|
||||
|
||||
private var dimensions: CGSize?
|
||||
private let dimensionsPromise = ValuePromise<CGSize>(CGSize())
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
private var shouldPlay: Bool = false
|
||||
|
||||
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
|
||||
self.postbox = postbox
|
||||
self.fileReference = fileReference
|
||||
self.placeholderColor = placeholderColor
|
||||
self.enableSound = enableSound
|
||||
self.loopVideo = loopVideo
|
||||
self.baseRate = baseRate
|
||||
self.audioSessionManager = audioSessionManager
|
||||
|
||||
self.imageNode = TransformImageNode()
|
||||
|
||||
self.player = MediaPlayer(audioSessionManager: audioSessionManager, postbox: postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), tempFilePath: tempFilePath, streamable: streamVideo, video: true, preferSoftwareDecoding: false, playAutomatically: false, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, continuePlayingWithoutSoundOnLostAudioSession: continuePlayingWithoutSoundOnLostAudioSession)
|
||||
|
||||
var actionAtEndImpl: (() -> Void)?
|
||||
if enableSound && !loopVideo {
|
||||
self.player.actionAtEnd = .action({
|
||||
@ -165,10 +180,25 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
||||
self.addSubnode(self.imageNode)
|
||||
self.addSubnode(self.playerNode)
|
||||
self._status.set(combineLatest(self.dimensionsPromise.get(), self.player.status)
|
||||
|> map { dimensions, status in
|
||||
|> map { [weak self] dimensions, status in
|
||||
return MediaPlayerStatus(generationTimestamp: status.generationTimestamp, duration: status.duration, dimensions: dimensions, timestamp: status.timestamp, baseRate: status.baseRate, seekId: status.seekId, status: status.status, soundEnabled: status.soundEnabled)
|
||||
})
|
||||
|
||||
self.fetchStatusDisposable.set((postbox.mediaBox.resourceStatus(fileReference.media.resource)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch status {
|
||||
case .Local:
|
||||
break
|
||||
default:
|
||||
if strongSelf.thumbnailPlayer == nil {
|
||||
strongSelf.createThumbnailPlayer()
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
if let size = fileReference.media.size {
|
||||
self._bufferingStatus.set(postbox.mediaBox.resourceRangesStatus(fileReference.media.resource) |> map { ranges in
|
||||
return (ranges, size)
|
||||
@ -184,7 +214,60 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
||||
|
||||
deinit {
|
||||
self.player.pause()
|
||||
self.thumbnailPlayer?.pause()
|
||||
self.fetchDisposable.dispose()
|
||||
self.fetchStatusDisposable.dispose()
|
||||
}
|
||||
|
||||
private func createThumbnailPlayer() {
|
||||
guard let videoThumbnail = self.fileReference.media.videoThumbnails.first else {
|
||||
return
|
||||
}
|
||||
|
||||
let thumbnailPlayer = MediaPlayer(audioSessionManager: self.audioSessionManager, postbox: postbox, resourceReference: fileReference.resourceReference(videoThumbnail.resource), tempFilePath: nil, streamable: .none, video: true, preferSoftwareDecoding: false, playAutomatically: false, enableSound: false, baseRate: self.baseRate, fetchAutomatically: false, continuePlayingWithoutSoundOnLostAudioSession: false)
|
||||
self.thumbnailPlayer = thumbnailPlayer
|
||||
|
||||
var actionAtEndImpl: (() -> Void)?
|
||||
if self.enableSound && !self.loopVideo {
|
||||
thumbnailPlayer.actionAtEnd = .action({
|
||||
actionAtEndImpl?()
|
||||
})
|
||||
} else {
|
||||
thumbnailPlayer.actionAtEnd = .loop({
|
||||
actionAtEndImpl?()
|
||||
})
|
||||
}
|
||||
|
||||
actionAtEndImpl = { [weak self] in
|
||||
self?.performActionAtEnd()
|
||||
}
|
||||
|
||||
let thumbnailNode = MediaPlayerNode(backgroundThread: false)
|
||||
self.thumbnailNode = thumbnailNode
|
||||
thumbnailPlayer.attachPlayerNode(thumbnailNode)
|
||||
|
||||
self.addSubnode(thumbnailNode)
|
||||
|
||||
thumbnailNode.frame = self.playerNode.frame
|
||||
|
||||
if self.shouldPlay {
|
||||
thumbnailPlayer.play()
|
||||
}
|
||||
|
||||
var processedSentFramesToDisplay = false
|
||||
self.playerNode.hasSentFramesToDisplay = { [weak self] in
|
||||
guard !processedSentFramesToDisplay, let _ = self else {
|
||||
return
|
||||
}
|
||||
processedSentFramesToDisplay = true
|
||||
Queue.mainQueue().after(0.1, {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.thumbnailNode?.isHidden = true
|
||||
strongSelf.thumbnailPlayer?.pause()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func performActionAtEnd() {
|
||||
@ -205,21 +288,30 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
||||
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.playerNode.frame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)
|
||||
if let thumbnailNode = self.thumbnailNode {
|
||||
thumbnailNode.frame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)
|
||||
}
|
||||
}
|
||||
|
||||
func play() {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
self.player.play()
|
||||
self.shouldPlay = true
|
||||
self.thumbnailPlayer?.play()
|
||||
}
|
||||
|
||||
func pause() {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
self.player.pause()
|
||||
self.shouldPlay = false
|
||||
self.thumbnailPlayer?.pause()
|
||||
}
|
||||
|
||||
func togglePlayPause() {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
self.player.togglePlayPause()
|
||||
self.shouldPlay = !self.shouldPlay
|
||||
self.thumbnailPlayer?.togglePlayPause()
|
||||
}
|
||||
|
||||
func setSoundEnabled(_ value: Bool) {
|
||||
|
@ -221,20 +221,20 @@ func legacyWebSearchItem(account: Account, result: ChatContextResult) -> LegacyW
|
||||
let originalSignal: Signal<UIImage, NoError>
|
||||
|
||||
switch result {
|
||||
case let .externalReference(_, _, _, _, _, _, content, thumbnail, _):
|
||||
if let content = content {
|
||||
case let .externalReference(externalReference):
|
||||
if let content = externalReference.content {
|
||||
imageResource = content.resource
|
||||
}
|
||||
if let thumbnail = thumbnail {
|
||||
if let thumbnail = externalReference.thumbnail {
|
||||
thumbnailResource = thumbnail.resource
|
||||
thumbnailDimensions = thumbnail.dimensions?.cgSize
|
||||
}
|
||||
if let dimensions = content?.dimensions {
|
||||
if let dimensions = externalReference.content?.dimensions {
|
||||
imageDimensions = dimensions.cgSize
|
||||
}
|
||||
case let .internalReference(_, _, _, _, _, image, _, _):
|
||||
immediateThumbnailData = image?.immediateThumbnailData
|
||||
if let image = image {
|
||||
case let .internalReference(internalReference):
|
||||
immediateThumbnailData = internalReference.image?.immediateThumbnailData
|
||||
if let image = internalReference.image {
|
||||
if let imageRepresentation = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 1000, height: 800)) {
|
||||
imageDimensions = imageRepresentation.dimensions.cgSize
|
||||
imageResource = imageRepresentation.resource
|
||||
|
@ -544,7 +544,10 @@ class WebSearchControllerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
self.isLoadingMore = true
|
||||
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(currentProcessedResults.geoPoint), offset: nextOffset)
|
||||
let geoPoint = currentProcessedResults.geoPoint.flatMap { geoPoint -> (Double, Double) in
|
||||
return (geoPoint.latitude, geoPoint.longitude)
|
||||
}
|
||||
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(geoPoint), offset: nextOffset)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] nextResults in
|
||||
guard let strongSelf = self, let nextResults = nextResults else {
|
||||
return
|
||||
|
@ -37,13 +37,13 @@ struct WebSearchGalleryEntry: Equatable {
|
||||
|
||||
func item(context: AccountContext, presentationData: PresentationData, controllerInteraction: WebSearchGalleryControllerInteraction?) -> GalleryItem {
|
||||
switch self.result {
|
||||
case let .externalReference(_, _, type, _, _, _, content, thumbnail, _):
|
||||
if let content = content, type == "gif", let thumbnailResource = thumbnail?.resource, let dimensions = content.dimensions {
|
||||
case let .externalReference(externalReference):
|
||||
if let content = externalReference.content, externalReference.type == "gif", let thumbnailResource = externalReference.thumbnail?.resource, let dimensions = content.dimensions {
|
||||
let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
|
||||
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, index: self.index, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: fileReference, loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction)
|
||||
}
|
||||
case let .internalReference(_, _, _, _, _, _, file, _):
|
||||
if let file = file {
|
||||
case let .internalReference(internalReference):
|
||||
if let file = internalReference.file {
|
||||
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, index: self.index, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: .standalone(media: file), loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction)
|
||||
}
|
||||
}
|
||||
|
@ -94,21 +94,21 @@ final class WebSearchItemNode: GridItemNode {
|
||||
var immediateThumbnailData: Data?
|
||||
|
||||
switch item.result {
|
||||
case let .externalReference(_, _, type, _, _, _, content, thumbnail, _):
|
||||
if let content = content, type != "gif" {
|
||||
case let .externalReference(externalReference):
|
||||
if let content = externalReference.content, externalReference.type != "gif" {
|
||||
imageResource = content.resource
|
||||
} else if let thumbnail = thumbnail {
|
||||
} else if let thumbnail = externalReference.thumbnail {
|
||||
imageResource = thumbnail.resource
|
||||
}
|
||||
imageDimensions = content?.dimensions?.cgSize
|
||||
case let .internalReference(_, _, _, _, _, image, file, _):
|
||||
if let image = image {
|
||||
imageDimensions = externalReference.content?.dimensions?.cgSize
|
||||
case let .internalReference(internalReference):
|
||||
if let image = internalReference.image {
|
||||
immediateThumbnailData = image.immediateThumbnailData
|
||||
if let largestRepresentation = largestImageRepresentation(image.representations) {
|
||||
imageDimensions = largestRepresentation.dimensions.cgSize
|
||||
}
|
||||
imageResource = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 200, height: 100))?.resource
|
||||
if let file = file {
|
||||
if let file = internalReference.file {
|
||||
if let thumbnailRepresentation = smallestImageRepresentation(file.previewRepresentations) {
|
||||
thumbnailDimensions = thumbnailRepresentation.dimensions.cgSize
|
||||
thumbnailResource = thumbnailRepresentation.resource
|
||||
@ -119,7 +119,7 @@ final class WebSearchItemNode: GridItemNode {
|
||||
thumbnailResource = thumbnailRepresentation.resource
|
||||
}
|
||||
}
|
||||
} else if let file = file {
|
||||
} else if let file = internalReference.file {
|
||||
immediateThumbnailData = file.immediateThumbnailData
|
||||
if let dimensions = file.dimensions {
|
||||
imageDimensions = dimensions.cgSize
|
||||
|
Loading…
x
Reference in New Issue
Block a user