Local changes

This commit is contained in:
Ali 2020-05-19 23:15:49 +04:00
parent f8d0f6a233
commit 4f9c73dc7a
34 changed files with 663 additions and 241 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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