This commit is contained in:
Ali 2020-05-15 20:35:00 +04:00
parent 1aa3a8b017
commit 150a176cbc
96 changed files with 1283 additions and 495 deletions

View File

@ -93,7 +93,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
private let source: ContextContentSource
private var items: Signal<[ContextMenuItem], NoError>
private let beginDismiss: (ContextMenuActionResult) -> Void
private let reactionSelected: (String) -> Void
private let reactionSelected: (ReactionContextItem.Reaction) -> Void
private let beganAnimatingOut: () -> Void
private let attemptTransitionControllerIntoNavigation: () -> Void
private let getController: () -> ContextController?
@ -129,7 +129,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
private var initialContinueGesturePoint: CGPoint?
private var didMoveFromInitialGesturePoint = false
private var highlightedActionNode: ContextActionNode?
private var highlightedReaction: String?
private var highlightedReaction: ReactionContextItem.Reaction?
private let hapticFeedback = HapticFeedback()
@ -137,7 +137,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
private let itemsDisposable = MetaDisposable()
init(account: Account, controller: ContextController, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, gesture: ContextGesture?, reactionSelected: @escaping (String) -> Void, beganAnimatingOut: @escaping () -> Void, attemptTransitionControllerIntoNavigation: @escaping () -> Void, displayTextSelectionTip: Bool) {
init(account: Account, controller: ContextController, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, gesture: ContextGesture?, reactionSelected: @escaping (ReactionContextItem.Reaction) -> Void, beganAnimatingOut: @escaping () -> Void, attemptTransitionControllerIntoNavigation: @escaping () -> Void, displayTextSelectionTip: Bool) {
self.presentationData = presentationData
self.source = source
self.items = items
@ -256,10 +256,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
if let reactionContextNode = strongSelf.reactionContextNode {
let highlightedReaction = reactionContextNode.reaction(at: strongSelf.view.convert(localPoint, to: reactionContextNode.view)).flatMap { value -> String? in
let highlightedReaction = reactionContextNode.reaction(at: strongSelf.view.convert(localPoint, to: reactionContextNode.view)).flatMap { value -> ReactionContextItem.Reaction? in
switch value {
case let .reaction(reaction, _, _):
return reaction
case .like:
return .like
case .unlike:
return .unlike
default:
return nil
}
@ -336,12 +338,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
if let reactionContextNode = strongSelf.reactionContextNode {
let highlightedReaction = reactionContextNode.reaction(at: strongSelf.view.convert(localPoint, to: reactionContextNode.view)).flatMap { value -> String? in
let highlightedReaction = reactionContextNode.reaction(at: strongSelf.view.convert(localPoint, to: reactionContextNode.view)).flatMap { value -> ReactionContextItem.Reaction? in
switch value {
case let .reaction(reaction, _, _):
return reaction
default:
return nil
case .like:
return .like
case .unlike:
return .unlike
}
}
if strongSelf.highlightedReaction != highlightedReaction {
@ -391,8 +393,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
return
}
switch reaction {
case let .reaction(value, _, _):
reactionSelected(value)
case .like:
reactionSelected(.like)
case .unlike:
reactionSelected(.unlike)
default:
break
}
@ -974,7 +978,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
}
func animateOutToReaction(value: String, into targetNode: ASDisplayNode, hideNode: Bool, completion: @escaping () -> Void) {
func animateOutToReaction(value: String, targetEmptyNode: ASDisplayNode, targetFilledNode: ASDisplayNode, hideNode: Bool, completion: @escaping () -> Void) {
guard let reactionContextNode = self.reactionContextNode else {
self.animateOut(result: .default, completion: completion)
return
@ -992,7 +996,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
contentCompleted = true
intermediateCompletion()
})
reactionContextNode.animateOutToReaction(value: value, targetNode: targetNode, hideNode: hideNode, completion: { [weak self] in
reactionContextNode.animateOutToReaction(value: value, targetEmptyNode: targetEmptyNode, targetFilledNode: targetFilledNode, hideNode: hideNode, completion: { [weak self] in
guard let strongSelf = self else {
return
}
@ -1510,7 +1514,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
return self.displayNode as! ContextControllerNode
}
public var reactionSelected: ((String) -> Void)?
public var reactionSelected: ((ReactionContextItem.Reaction) -> Void)?
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, displayTextSelectionTip: Bool = false) {
self.account = account
@ -1611,10 +1615,10 @@ public final class ContextController: ViewController, StandalonePresentableContr
self.dismiss(result: .default, completion: completion)
}
public func dismissWithReaction(value: String, into targetNode: ASDisplayNode, hideNode: Bool, completion: (() -> Void)?) {
public func dismissWithReaction(value: String, targetEmptyNode: ASDisplayNode, targetFilledNode: ASDisplayNode, hideNode: Bool, completion: (() -> Void)?) {
if !self.wasDismissed {
self.wasDismissed = true
self.controllerNode.animateOutToReaction(value: value, into: targetNode, hideNode: hideNode, completion: { [weak self] in
self.controllerNode.animateOutToReaction(value: value, targetEmptyNode: targetEmptyNode, targetFilledNode: targetFilledNode, hideNode: hideNode, completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
completion?()
})

View File

@ -451,7 +451,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
copyLocalFiles.append((resource, path))
}
}
parsedMedia.append(TelegramMediaFile(fileId: mediaId, partialReference: nil, resource: resource, previewRepresentations: representations, immediateThumbnailData: nil, mimeType: "video/mp4", size: size == 0 ? nil : Int(size), attributes: attributes))
parsedMedia.append(TelegramMediaFile(fileId: mediaId, partialReference: nil, resource: resource, previewRepresentations: representations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: size == 0 ? nil : Int(size), attributes: attributes))
} else if let item = item as? TGAudioMediaAttachment {
let mediaId = MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64())
let representations: [TelegramMediaImageRepresentation] = []
@ -472,7 +472,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
} else if audioUrl.hasPrefix("file://"), let path = URL(string: audioUrl)?.path {
copyLocalFiles.append((resource, path))
}
parsedMedia.append(TelegramMediaFile(fileId: mediaId, partialReference: nil, resource: resource, previewRepresentations: representations, immediateThumbnailData: nil, mimeType: "audio/ogg", size: size == 0 ? nil : Int(size), attributes: attributes))
parsedMedia.append(TelegramMediaFile(fileId: mediaId, partialReference: nil, resource: resource, previewRepresentations: representations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: size == 0 ? nil : Int(size), attributes: attributes))
} else if let item = item as? TGDocumentMediaAttachment {
let mediaId = MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64())
var representations: [TelegramMediaImageRepresentation] = []
@ -540,7 +540,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
} else if item.documentId != 0 {
copyLocalFiles.append((resource, pathFromLegacyFile(basePath: basePath, fileId: item.documentId, isLocal: false, fileName: TGDocumentMediaAttachment.safeFileName(forFileName: fileName) ?? "")))
}
parsedMedia.append(TelegramMediaFile(fileId: mediaId, partialReference: nil, resource: resource, previewRepresentations: representations, immediateThumbnailData: nil, mimeType: item.mimeType ?? "application/octet-stream", size: size == 0 ? nil : Int(size), attributes: attributes))
parsedMedia.append(TelegramMediaFile(fileId: mediaId, partialReference: nil, resource: resource, previewRepresentations: representations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: item.mimeType ?? "application/octet-stream", size: size == 0 ? nil : Int(size), attributes: attributes))
} else if let item = item as? TGActionMediaAttachment {
if item.actionType == TGMessageActionEncryptedChatMessageLifetime, let actionData = item.actionData, let timeout = actionData["messageLifetime"] as? Int32 {

View File

@ -264,7 +264,7 @@ public func legacyEnqueueGifMessage(account: Account, data: Data) -> Signal<Enqu
fileAttributes.append(.FileName(fileName: fileName))
fileAttributes.append(.Animated)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes)
subscriber.putNext(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil))
subscriber.putCompletion()
} else {
@ -311,7 +311,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
arc4random_buf(&randomId, 8)
let _ = try? heicData.write(to: URL(fileURLWithPath: tempFilePath + ".heic"))
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath + ".heic", randomId: randomId)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "image/heic", size: nil, attributes: [.FileName(fileName: "image.heic")])
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/heic", size: nil, attributes: [.FileName(fileName: "image.heic")])
var attributes: [MessageAttribute] = []
if let timer = item.timer, timer > 0 && timer <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
@ -372,13 +372,13 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let resource = LocalFileReferenceMediaResource(localFilePath: path, randomId: randomId)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)])
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)])
messages.append(.message(text: caption ?? "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId))
case let .asset(asset):
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: arc4random64())
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)])
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)])
messages.append(.message(text: caption ?? "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId))
default:
break
@ -460,7 +460,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
}
}
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes)
var attributes: [MessageAttribute] = []
if let timer = item.timer, timer > 0 && timer <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))

View File

@ -25,7 +25,7 @@ public func stickerFromLegacyDocument(_ documentAttachment: TGDocumentMediaAttac
fileReference = data
}
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentAttachment.documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(documentAttachment.datacenterId), fileId: documentAttachment.documentId, accessHash: documentAttachment.accessHash, size: Int(documentAttachment.size), fileReference: fileReference, fileName: documentAttachment.fileName()), previewRepresentations: [], immediateThumbnailData: nil, mimeType: documentAttachment.mimeType, size: Int(documentAttachment.size), attributes: attributes)
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentAttachment.documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(documentAttachment.datacenterId), fileId: documentAttachment.documentId, accessHash: documentAttachment.accessHash, size: Int(documentAttachment.size), fileReference: fileReference, fileName: documentAttachment.fileName()), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: documentAttachment.mimeType, size: Int(documentAttachment.size), attributes: attributes)
}
}
return nil
@ -202,7 +202,7 @@ final class LegacyStickerImageDataSource: TGImageDataSource {
}
}
return LegacyStickerImageDataTask(account: account, file: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: datacenterId, fileId: documentId, accessHash: accessHash, size: size, fileReference: nil, fileName: fileNameFromFileAttributes(attributes)), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: "image/webp", size: size, attributes: attributes), small: !highQuality, fitSize: fitSize, completion: { image in
return LegacyStickerImageDataTask(account: account, file: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: documentId), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: datacenterId, fileId: documentId, accessHash: accessHash, size: size, fileReference: nil, fileName: fileNameFromFileAttributes(attributes)), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: size, attributes: attributes), small: !highQuality, fitSize: fitSize, completion: { image in
if let image = image {
sharedImageCache.setImage(image, forKey: uri, attributes: nil)
completion?(TGDataResource(image: image, decoded: true))

View File

@ -91,7 +91,7 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa
}
if streamable {
let data: Signal<Data, NoError>
let data: Signal<(Data, Bool), NoError>
data = postbox.mediaBox.resourceData(resourceReference.resource, size: resourceSize, in: requestRange, mode: .complete)
if readCount == 0 {
fetchedData = Data()
@ -102,8 +102,9 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa
let semaphore = DispatchSemaphore(value: 0)
let _ = context.currentSemaphore.swap(semaphore)
var completedRequest = false
let disposable = data.start(next: { data in
if data.count == readCount {
let disposable = data.start(next: { result in
let (data, isComplete) = result
if data.count == readCount || isComplete{
fetchedData = data
completedRequest = true
semaphore.signal()

View File

@ -8,10 +8,10 @@ import FFMpegBinding
private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer<UInt8>?, bufferSize: Int32) -> Int32 {
let context = Unmanaged<UniversalSoftwareVideoSourceImpl>.fromOpaque(userData!).takeUnretainedValue()
let data: Signal<Data, NoError>
let data: Signal<(Data, Bool), NoError>
let resourceSize: Int = context.size
let readCount = min(resourceSize - context.readingOffset, Int(bufferSize))
let readCount = min(256 * 1024, Int(bufferSize))
let requestRange: Range<Int> = context.readingOffset ..< (context.readingOffset + readCount)
context.currentNumberOfReads += 1
@ -25,8 +25,9 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa
let isInitialized = context.videoStream != nil
let mediaBox = context.mediaBox
let reference = context.fileReference.resourceReference(context.fileReference.media.resource)
let disposable = data.start(next: { data in
if data.count == readCount {
let disposable = data.start(next: { result in
let (data, isComplete) = result
if data.count == readCount || isComplete {
fetchedData = data
semaphore.signal()
} else {

View File

@ -563,7 +563,7 @@ public final class MediaBox {
}
}
public func resourceData(_ resource: MediaResource, size: Int, in range: Range<Int>, mode: ResourceDataRangeMode = .complete) -> Signal<Data, NoError> {
public func resourceData(_ resource: MediaResource, size: Int, in range: Range<Int>, mode: ResourceDataRangeMode = .complete) -> Signal<(Data, Bool), NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
@ -582,7 +582,7 @@ public final class MediaBox {
if fileSize >= result.offset + result.size {
file.seek(position: Int64(result.offset))
let resultData = file.readData(count: result.size)
subscriber.putNext(resultData)
subscriber.putNext((resultData, true))
subscriber.putCompletion()
} else {
assertionFailure("data.count >= result.offset + result.size")
@ -597,7 +597,7 @@ public final class MediaBox {
case .incremental:
break
case .partial:
subscriber.putNext(Data())
subscriber.putNext((Data(), false))
}
}
}

View File

@ -884,7 +884,7 @@ final class MediaBoxFileContext {
func data(range: Range<Int32>, waitUntilAfterInitialFetch: Bool, next: @escaping (MediaResourceData) -> Void) -> Disposable {
switch self.content {
case let .complete(path, size):
next(MediaResourceData(path: path, offset: Int(range.lowerBound), size: min(Int(range.upperBound), size) - Int(range.lowerBound), complete: true))
next(MediaResourceData(path: path, offset: min(size, Int(range.lowerBound)), size: max(0, min(Int(range.upperBound), size) - Int(range.lowerBound)), complete: true))
return EmptyDisposable
case let .partial(file):
return file.data(range: range, waitUntilAfterInitialFetch: waitUntilAfterInitialFetch, next: next)

View File

@ -1,4 +1,4 @@
import Foundation
/*import Foundation
import AsyncDisplayKit
import AnimatedStickerNode
import Display
@ -321,3 +321,4 @@ final class ReactionAttachedNode: ASDisplayNode {
return nil
}
}
*/

View File

@ -7,14 +7,15 @@ import SyncCore
import TelegramPresentationData
public final class ReactionContextItem {
public let value: String
public let text: String
public let path: String
public enum Reaction {
case like
case unlike
}
public init(value: String, text: String, path: String) {
self.value = value
self.text = text
self.path = path
public let reaction: ReactionContextItem.Reaction
public init(reaction: ReactionContextItem.Reaction) {
self.reaction = reaction
}
}
@ -22,14 +23,11 @@ private let largeCircleSize: CGFloat = 16.0
private let smallCircleSize: CGFloat = 8.0
private func generateBackgroundImage(foreground: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter * 2.0 + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.copy)
context.setFillColor(foreground.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur + diameter, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.fill(CGRect(origin: CGPoint(x: shadowBlur + diameter / 2.0, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
})?.stretchableImage(withLeftCapWidth: Int(diameter + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0))
})?.stretchableImage(withLeftCapWidth: Int(shadowBlur + diameter / 2.0), topCapHeight: Int(shadowBlur + diameter / 2.0))
}
private func generateBackgroundShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
@ -91,8 +89,8 @@ public final class ReactionContextNode: ASDisplayNode {
private var itemNodes: [ReactionNode] = []
private let disclosureButton: HighlightTrackingButtonNode
private var isExpanded: Bool = false
private var highlightedReaction: String?
private var isExpanded: Bool = true
private var highlightedReaction: ReactionContextItem.Reaction?
private var validLayout: (CGSize, UIEdgeInsets, CGRect)?
public var reactionSelected: ((ReactionGestureItem) -> Void)?
@ -178,7 +176,14 @@ public final class ReactionContextNode: ASDisplayNode {
self.contentContainer.addSubnode(self.disclosureButton)
self.itemNodes = self.items.map { item in
return ReactionNode(account: account, theme: theme, reaction: .reaction(value: item.value, text: item.text, path: item.path), maximizedReactionSize: 30.0 - 18.0, loadFirstFrame: true)
let reactionItem: ReactionGestureItem
switch item.reaction {
case .like:
reactionItem = .like
case .unlike:
reactionItem = .unlike
}
return ReactionNode(account: account, theme: theme, reaction: reactionItem, maximizedReactionSize: 30.0, loadFirstFrame: true)
}
self.itemNodes.forEach(self.contentContainer.addSubnode)
@ -205,6 +210,10 @@ public final class ReactionContextNode: ASDisplayNode {
}
private func calculateBackgroundFrame(containerSize: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, contentSize: CGSize) -> (CGRect, Bool) {
var contentSize = contentSize
contentSize.width = max(52.0, contentSize.width)
contentSize.height = 52.0
let sideInset: CGFloat = 12.0
let backgroundOffset: CGPoint = CGPoint(x: 22.0, y: -7.0)
@ -214,7 +223,7 @@ public final class ReactionContextNode: ASDisplayNode {
rect = CGRect(origin: CGPoint(x: anchorRect.maxX - contentSize.width + backgroundOffset.x, y: anchorRect.minY - contentSize.height + backgroundOffset.y), size: contentSize)
isLeftAligned = true
} else {
rect = CGRect(origin: CGPoint(x: anchorRect.minX - backgroundOffset.x, y: anchorRect.minY - contentSize.height + backgroundOffset.y), size: contentSize)
rect = CGRect(origin: CGPoint(x: anchorRect.minX - backgroundOffset.x - 4.0, y: anchorRect.minY - contentSize.height + backgroundOffset.y), size: contentSize)
isLeftAligned = false
}
rect.origin.x = max(sideInset, rect.origin.x)
@ -226,12 +235,12 @@ public final class ReactionContextNode: ASDisplayNode {
private func updateLayout(size: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, transition: ContainedViewLayoutTransition, animateInFromAnchorRect: CGRect?, animateOutToAnchorRect: CGRect?, animateReactionHighlight: Bool = false) {
self.validLayout = (size, insets, anchorRect)
let sideInset: CGFloat = 10.0
let sideInset: CGFloat = 12.0
let itemSpacing: CGFloat = 6.0
let minimizedItemSize: CGFloat = 30.0
let maximizedItemSize: CGFloat = 30.0 - 18.0
let shadowBlur: CGFloat = 5.0
let verticalInset: CGFloat = 11.0
let verticalInset: CGFloat = 13.0
let rowHeight: CGFloat = 30.0
let rowSpacing: CGFloat = itemSpacing
@ -253,15 +262,7 @@ public final class ReactionContextNode: ASDisplayNode {
let row = CGFloat(rowIndex)
let column = CGFloat(columnIndex)
var reactionValue: String?
switch self.itemNodes[i].reaction {
case let .reaction(value, _, _):
reactionValue = value
default:
break
}
let isHighlighted = reactionValue != nil && self.highlightedReaction == reactionValue
let isHighlighted = false
var itemSize: CGFloat = minimizedItemSize
var itemOffset: CGFloat = 0.0
@ -326,10 +327,10 @@ public final class ReactionContextNode: ASDisplayNode {
let largeCircleFrame: CGRect
let smallCircleFrame: CGRect
if isLeftAligned {
largeCircleFrame = CGRect(origin: CGPoint(x: anchorRect.maxX + 16.0 - rowHeight + floor((rowHeight - largeCircleSize) / 2.0), y: backgroundFrame.maxY - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize))
largeCircleFrame = CGRect(origin: CGPoint(x: backgroundFrame.midX - floor(largeCircleSize / 2.0), y: backgroundFrame.maxY - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize))
smallCircleFrame = CGRect(origin: CGPoint(x: largeCircleFrame.maxX - 3.0, y: largeCircleFrame.maxY + 2.0), size: CGSize(width: smallCircleSize, height: smallCircleSize))
} else {
largeCircleFrame = CGRect(origin: CGPoint(x: anchorRect.minX - 18.0 + floor((rowHeight - largeCircleSize) / 2.0), y: backgroundFrame.maxY - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize))
largeCircleFrame = CGRect(origin: CGPoint(x: backgroundFrame.midX - floor(largeCircleSize / 2.0), y: backgroundFrame.maxY - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize))
smallCircleFrame = CGRect(origin: CGPoint(x: largeCircleFrame.minX + 3.0 - smallCircleSize, y: largeCircleFrame.maxY + 2.0), size: CGSize(width: smallCircleSize, height: smallCircleSize))
}
@ -353,11 +354,35 @@ public final class ReactionContextNode: ASDisplayNode {
}
public func animateIn(from sourceAnchorRect: CGRect) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
if let (size, insets, anchorRect) = self.validLayout {
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .immediate, animateInFromAnchorRect: sourceAnchorRect, animateOutToAnchorRect: nil)
}
let smallCircleDuration: Double = 0.5
let largeCircleDuration: Double = 0.5
let largeCircleDelay: Double = 0.08
let mainCircleDuration: Double = 0.5
let mainCircleDelay: Double = 0.1
self.smallCircleNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: smallCircleDuration)
self.smallCircleShadowNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: smallCircleDuration)
self.largeCircleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: largeCircleDelay)
self.largeCircleNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay)
self.largeCircleShadowNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay)
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: mainCircleDelay)
self.backgroundNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay)
self.backgroundShadowNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay)
if let itemNode = self.itemNodes.first {
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: mainCircleDelay)
itemNode.didAppear()
itemNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay, completion: { _ in
})
}
}
public func animateOut(to targetAnchorRect: CGRect?, animatingOutToReaction: Bool) {
@ -377,73 +402,71 @@ public final class ReactionContextNode: ASDisplayNode {
}
}
public func animateOutToReaction(value: String, targetNode: ASDisplayNode, hideNode: Bool, completion: @escaping () -> Void) {
public func animateOutToReaction(value: String, targetEmptyNode: ASDisplayNode, targetFilledNode: ASDisplayNode, hideNode: Bool, completion: @escaping () -> Void) {
for itemNode in self.itemNodes {
switch itemNode.reaction {
case let .reaction(itemValue, _, _):
if itemValue == value {
if let snapshotView = itemNode.view.snapshotContentTree(keepTransform: true), let targetSnapshotView = targetNode.view.snapshotContentTree() {
targetSnapshotView.frame = self.view.convert(targetNode.bounds, from: targetNode.view)
itemNode.isHidden = true
self.view.addSubview(targetSnapshotView)
self.view.addSubview(snapshotView)
var completedTarget = false
let intermediateCompletion: () -> Void = {
if completedTarget {
completion()
}
case .like:
if let snapshotView = itemNode.view.snapshotContentTree(keepTransform: true), let targetSnapshotView = targetFilledNode.view.snapshotContentTree() {
targetSnapshotView.frame = self.view.convert(targetFilledNode.bounds, from: targetFilledNode.view)
itemNode.isHidden = true
self.view.addSubview(targetSnapshotView)
self.view.addSubview(snapshotView)
snapshotView.frame = itemNode.view.convert(itemNode.view.bounds, to: self.view)
var completedTarget = false
let intermediateCompletion: () -> Void = {
if completedTarget {
completion()
}
let targetPosition = self.view.convert(targetNode.bounds.center, from: targetNode.view)
let duration: Double = 0.3
if hideNode {
targetNode.isHidden = true
}
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
targetSnapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
targetSnapshotView.layer.animateScale(from: snapshotView.bounds.width / targetSnapshotView.bounds.width, to: 0.5, duration: 0.3, removeOnCompletion: false)
let sourcePoint = snapshotView.center
let midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0, y: sourcePoint.y - 30.0)
let x1 = sourcePoint.x
let y1 = sourcePoint.y
let x2 = midPoint.x
let y2 = midPoint.y
let x3 = targetPosition.x
let y3 = targetPosition.y
let a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
let b = (x1 * x1 * (y2 - y3) + x3 * x3 * (y1 - y2) + x2 * x2 * (y3 - y1)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
let c = (x2 * x2 * (x3 * y1 - x1 * y3) + x2 * (x1 * x1 * y3 - x3 * x3 * y1) + x1 * x3 * (x3 - x1) * y2) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
var keyframes: [AnyObject] = []
for i in 0 ..< 10 {
let k = CGFloat(i) / CGFloat(10 - 1)
let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k
let y = a * x * x + b * x + c
keyframes.append(NSValue(cgPoint: CGPoint(x: x, y: y)))
}
snapshotView.layer.animateKeyframes(values: keyframes, duration: 0.3, keyPath: "position", removeOnCompletion: false, completion: { [weak self] _ in
if let strongSelf = self {
strongSelf.hapticFeedback.tap()
}
completedTarget = true
if hideNode {
targetNode.isHidden = false
targetNode.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, initialVelocity: 0.0, damping: 90.0)
}
intermediateCompletion()
})
targetSnapshotView.layer.animateKeyframes(values: keyframes, duration: 0.3, keyPath: "position", removeOnCompletion: false)
snapshotView.layer.animateScale(from: 1.0, to: (targetSnapshotView.bounds.width * 0.5) / snapshotView.bounds.width, duration: 0.3, removeOnCompletion: false)
} else {
completion()
}
let targetPosition = self.view.convert(targetFilledNode.bounds.center, from: targetFilledNode.view)
let duration: Double = 0.3
if hideNode {
targetFilledNode.isHidden = true
}
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
targetSnapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
targetSnapshotView.layer.animateScale(from: snapshotView.bounds.width / targetSnapshotView.bounds.width, to: 0.5, duration: 0.3, removeOnCompletion: false)
let sourcePoint = snapshotView.center
let midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0, y: sourcePoint.y - 30.0)
let x1 = sourcePoint.x
let y1 = sourcePoint.y
let x2 = midPoint.x
let y2 = midPoint.y
let x3 = targetPosition.x
let y3 = targetPosition.y
let a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
let b = (x1 * x1 * (y2 - y3) + x3 * x3 * (y1 - y2) + x2 * x2 * (y3 - y1)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
let c = (x2 * x2 * (x3 * y1 - x1 * y3) + x2 * (x1 * x1 * y3 - x3 * x3 * y1) + x1 * x3 * (x3 - x1) * y2) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
var keyframes: [AnyObject] = []
for i in 0 ..< 10 {
let k = CGFloat(i) / CGFloat(10 - 1)
let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k
let y = a * x * x + b * x + c
keyframes.append(NSValue(cgPoint: CGPoint(x: x, y: y)))
}
snapshotView.layer.animateKeyframes(values: keyframes, duration: 0.3, keyPath: "position", removeOnCompletion: false, completion: { [weak self] _ in
if let strongSelf = self {
strongSelf.hapticFeedback.tap()
}
completedTarget = true
if hideNode {
targetFilledNode.isHidden = false
targetFilledNode.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, initialVelocity: 0.0, damping: 90.0)
targetEmptyNode.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, initialVelocity: 0.0, damping: 90.0)
}
intermediateCompletion()
})
targetSnapshotView.layer.animateKeyframes(values: keyframes, duration: 0.3, keyPath: "position", removeOnCompletion: false)
snapshotView.layer.animateScale(from: 1.0, to: (targetSnapshotView.bounds.width * 0.5) / snapshotView.bounds.width, duration: 0.3, removeOnCompletion: false)
return
}
default:
@ -492,7 +515,7 @@ public final class ReactionContextNode: ASDisplayNode {
return nil
}
public func setHighlightedReaction(_ value: String?) {
public func setHighlightedReaction(_ value: ReactionContextItem.Reaction?) {
self.highlightedReaction = value
if let (size, insets, anchorRect) = self.validLayout {
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)

View File

@ -4,6 +4,6 @@ import TelegramCore
import SyncCore
public enum ReactionGestureItem {
case reaction(value: String, text: String, path: String)
case reply
case like
case unlike
}

View File

@ -39,6 +39,7 @@ final class ReactionNode: ASDisplayNode {
private let textNode: ImmediateTextNode
private let animationNode: AnimatedStickerNode
private let imageNode: ASImageNode
private let additionalImageNode: ASImageNode
var isMaximized: Bool?
private let intrinsicSize: CGSize
private let intrinsicOffset: CGPoint
@ -56,13 +57,7 @@ final class ReactionNode: ASDisplayNode {
self.textNode.displaysAsynchronously = false
self.textNode.isUserInteractionEnabled = false
let reactionText: String
switch reaction {
case let .reaction(_, text, _):
reactionText = text
case .reply:
reactionText = "Reply"
}
let reactionText: String = ""
self.textNode.attributedText = NSAttributedString(string: reactionText, font: font, textColor: theme.chat.serviceMessage.dateTextColor.withWallpaper)
let textSize = self.textNode.updateLayout(CGSize(width: 200.0, height: 100.0))
@ -80,67 +75,19 @@ final class ReactionNode: ASDisplayNode {
var intrinsicSize = CGSize(width: maximizedReactionSize + 14.0, height: maximizedReactionSize + 14.0)
self.imageNode = ASImageNode()
self.additionalImageNode = ASImageNode()
switch reaction {
case let .reaction(value, _, path):
switch value {
case "😔":
intrinsicSize.width *= 1.7
intrinsicSize.height *= 1.7
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
case "😳":
intrinsicSize.width *= 1.15
intrinsicSize.height *= 1.15
self.intrinsicOffset = CGPoint(x: 0.0, y: -0.05 * intrinsicSize.width)
case "😂":
intrinsicSize.width *= 1.2
intrinsicSize.height *= 1.2
self.intrinsicOffset = CGPoint(x: 0.0 * intrinsicSize.width, y: 0.0 * intrinsicSize.width)
case "👍":
intrinsicSize.width *= 1.256
intrinsicSize.height *= 1.256
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.05 * intrinsicSize.width)
case "🥳":
intrinsicSize.width *= 1.39
intrinsicSize.height *= 1.39
self.intrinsicOffset = CGPoint(x: 0.1 * intrinsicSize.width, y: -0.05 * intrinsicSize.width)
case "😭":
intrinsicSize.width *= 1.15
intrinsicSize.height *= 1.15
self.intrinsicOffset = CGPoint(x: 0.1 * intrinsicSize.width, y: 0.03 * intrinsicSize.width)
case "😒":
intrinsicSize.width *= 1.05
intrinsicSize.height *= 1.05
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
case "👌":
intrinsicSize.width *= 1.08
intrinsicSize.height *= 1.08
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
case "😐":
intrinsicSize.width *= 1.75
intrinsicSize.height *= 1.75
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.01 * intrinsicSize.width)
case "💩":
intrinsicSize.width *= 1.1
intrinsicSize.height *= 1.1
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
case "😊":
intrinsicSize.width *= 1.2
intrinsicSize.height *= 1.2
self.intrinsicOffset = CGPoint(x: 0.0, y: -0.01 * intrinsicSize.width)
default:
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
}
var renderSize: CGSize = CGSize(width: intrinsicSize.width * 2.0, height: intrinsicSize.height * 2.0)
if UIScreen.main.scale.isEqual(to: 3.0) {
if maximizedReactionSize < 40.0 {
renderSize = CGSize(width: intrinsicSize.width * 2.5, height: intrinsicSize.height * 2.5)
}
}
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: Int(renderSize.width), height: Int(renderSize.height), mode: .direct)
case .reply:
case .like:
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
self.imageNode.image = UIImage(named: "Chat/Context Menu/ReactionReply", in: getAppBundle(), compatibleWith: nil)
self.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Reactions/ContextHeartFilled"), color: UIColor(rgb: 0xfe1512))
case .unlike:
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
self.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Reactions/ContextHeartBrokenL"), color: UIColor(rgb: 0xfe1512))
self.additionalImageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Reactions/ContextHeartBrokenR"), color: UIColor(rgb: 0xfe1512))
}
if let image = self.imageNode.image {
intrinsicSize = image.size
}
self.intrinsicSize = intrinsicSize
@ -149,18 +96,26 @@ final class ReactionNode: ASDisplayNode {
//self.backgroundColor = .gray
self.textBackgroundNode.addSubnode(self.textNode)
self.addSubnode(self.textBackgroundNode)
//self.textBackgroundNode.addSubnode(self.textNode)
//self.addSubnode(self.textBackgroundNode)
self.addSubnode(self.animationNode)
//self.addSubnode(self.animationNode)
self.addSubnode(self.imageNode)
self.addSubnode(self.additionalImageNode)
self.animationNode.updateLayout(size: self.intrinsicSize)
self.animationNode.frame = CGRect(origin: CGPoint(), size: self.intrinsicSize)
self.imageNode.frame = CGRect(origin: CGPoint(), size: self.intrinsicSize)
switch reaction {
case .like:
self.imageNode.frame = CGRect(origin: CGPoint(x: -5.0, y: -5.0), size: self.intrinsicSize)
case .unlike:
self.imageNode.frame = CGRect(origin: CGPoint(x: -6.0, y: -5.0), size: self.intrinsicSize)
self.additionalImageNode.frame = CGRect(origin: CGPoint(x: -3.0, y: -5.0), size: self.intrinsicSize)
}
}
func updateLayout(size: CGSize, scale: CGFloat, transition: ContainedViewLayoutTransition, displayText: Bool) {
transition.updatePosition(node: self.animationNode, position: CGPoint(x: size.width / 2.0 + self.intrinsicOffset.x * scale, y: size.height / 2.0 + self.intrinsicOffset.y * scale), beginWithCurrentState: true)
/*transition.updatePosition(node: self.animationNode, position: CGPoint(x: size.width / 2.0 + self.intrinsicOffset.x * scale, y: size.height / 2.0 + self.intrinsicOffset.y * scale), beginWithCurrentState: true)
transition.updateTransformScale(node: self.animationNode, scale: scale, beginWithCurrentState: true)
transition.updatePosition(node: self.imageNode, position: CGPoint(x: size.width / 2.0 + self.intrinsicOffset.x * scale, y: size.height / 2.0 + self.intrinsicOffset.y * scale), beginWithCurrentState: true)
transition.updateTransformScale(node: self.imageNode, scale: scale, beginWithCurrentState: true)
@ -169,7 +124,7 @@ final class ReactionNode: ASDisplayNode {
transition.updateTransformScale(node: self.textBackgroundNode, scale: displayText ? 1.0 : 0.1, beginWithCurrentState: true)
transition.updateAlpha(node: self.textBackgroundNode, alpha: displayText ? 1.0 : 0.0, beginWithCurrentState: true)
transition.updateAlpha(node: self.textNode, alpha: displayText ? 1.0 : 0.0, beginWithCurrentState: true)
transition.updateAlpha(node: self.textNode, alpha: displayText ? 1.0 : 0.0, beginWithCurrentState: true)*/
}
func updateIsAnimating(_ isAnimating: Bool, animated: Bool) {
@ -179,6 +134,21 @@ final class ReactionNode: ASDisplayNode {
self.animationNode.visibility = false
}
}
func didAppear() {
switch self.reaction {
case .like:
self.imageNode.layer.animateScale(from: 1.0, to: 1.08, duration: 0.12, delay: 0.22, removeOnCompletion: false, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.imageNode.layer.animateScale(from: 1.08, to: 1.0, duration: 0.12)
})
case .unlike:
self.imageNode.layer.animatePosition(from: CGPoint(x: -2.5, y: 0.0), to: CGPoint(), duration: 0.2, delay: 0.15, additive: true)
self.additionalImageNode.layer.animatePosition(from: CGPoint(x: 2.5, y: 0.0), to: CGPoint(), duration: 0.2, delay: 0.15, additive: true)
}
}
}
final class ReactionSelectionNode: ASDisplayNode {
@ -268,9 +238,9 @@ final class ReactionSelectionNode: ASDisplayNode {
let anchorX = max(anchorMinX, min(anchorMaxX, offsetFromStart))
var maximizedIndex = -1
if let reaction = self.reactions.last, case .reply = reaction {
/*if let reaction = self.reactions.last, case .reply = reaction {
maximizedIndex = self.reactions.count - 1
}
}*/
if backgroundFrame.insetBy(dx: -10.0, dy: -10.0).offsetBy(dx: 0.0, dy: 10.0).contains(touchPoint) {
maximizedIndex = Int(((touchPoint.x - anchorMinX) / (anchorMaxX - anchorMinX)) * CGFloat(self.reactionNodes.count))
maximizedIndex = max(0, min(self.reactionNodes.count - 1, maximizedIndex))
@ -517,3 +487,4 @@ final class ReactionSelectionNode: ASDisplayNode {
return nil
}
}

View File

@ -83,3 +83,4 @@ public final class ReactionSelectionParentNode: ASDisplayNode {
return nil
}
}

View File

@ -190,3 +190,4 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer {
self.state = .cancelled
}
}

View File

@ -180,7 +180,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))

View File

@ -174,7 +174,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
let messages = logs.map { (name, path) -> EnqueueMessage in
let id = arc4random64()
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
}
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
@ -227,7 +227,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
let messages = updatedLogs.map { (name, path) -> EnqueueMessage in
let id = arc4random64()
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
}
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
@ -274,7 +274,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
let messages = logs.map { (name, path) -> EnqueueMessage in
let id = arc4random64()
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
}
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
@ -303,7 +303,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
let messages = logs.map { (name, path) -> EnqueueMessage in
let id = arc4random64()
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
}
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()

View File

@ -326,7 +326,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))

View File

@ -442,7 +442,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
let _ = (combineLatest(queue: Queue.mainQueue(), previewThemePromise.get(), settingsPromise.get())
|> take(1)).start(next: { previewTheme, settings in
let saveThemeTemplateFile: (String, LocalFileMediaResource, @escaping () -> Void) -> Void = { title, resource, completion in
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: resource.fileId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/x-tgtheme-ios", size: nil, attributes: [.FileName(fileName: "\(title).tgios-theme")])
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: resource.fileId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/x-tgtheme-ios", size: nil, attributes: [.FileName(fileName: "\(title).tgios-theme")])
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
let _ = enqueueMessages(account: context.account, peerId: context.account.peerId, messages: [message]).start()

View File

@ -870,7 +870,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
sampleMessages.append(message6)

View File

@ -4,7 +4,7 @@ import SyncCore
import TelegramUIPreferences
private func patternWallpaper(slug: String, topColor: UInt32, bottomColor: UInt32?, intensity: Int32?, rotation: Int32?) -> TelegramWallpaper {
return TelegramWallpaper.file(id: 0, accessHash: 0, isCreator: false, isDefault: true, isPattern: true, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(color: topColor, bottomColor: bottomColor, intensity: intensity ?? 50, rotation: rotation))
return TelegramWallpaper.file(id: 0, accessHash: 0, isCreator: false, isDefault: true, isPattern: true, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(color: topColor, bottomColor: bottomColor, intensity: intensity ?? 50, rotation: rotation))
}
var dayClassicColorPresets: [PresentationThemeAccentColor] = [

View File

@ -475,7 +475,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
sampleMessages.append(message6)

View File

@ -235,10 +235,45 @@ public enum TelegramMediaFileReference: PostboxCoding, Equatable {
}
public final class TelegramMediaFile: Media, Equatable {
public final class VideoThumbnail: Equatable, PostboxCoding {
public let dimensions: PixelDimensions
public let resource: TelegramMediaResource
public init(dimensions: PixelDimensions, resource: TelegramMediaResource) {
self.dimensions = dimensions
self.resource = resource
}
public init(decoder: PostboxDecoder) {
self.dimensions = PixelDimensions(width: decoder.decodeInt32ForKey("w", orElse: 0), height: decoder.decodeInt32ForKey("h", orElse: 0))
self.resource = decoder.decodeObjectForKey("r") as! TelegramMediaResource
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.dimensions.width, forKey: "w")
encoder.encodeInt32(self.dimensions.height, forKey: "h")
encoder.encodeObject(self.resource, forKey: "r")
}
public static func ==(lhs: VideoThumbnail, rhs: VideoThumbnail) -> Bool {
if lhs === rhs {
return true
}
if lhs.dimensions != rhs.dimensions {
return false
}
if !lhs.resource.isEqual(to: rhs.resource) {
return false
}
return true
}
}
public let fileId: MediaId
public let partialReference: PartialMediaReference?
public let resource: TelegramMediaResource
public let previewRepresentations: [TelegramMediaImageRepresentation]
public let videoThumbnails: [TelegramMediaFile.VideoThumbnail]
public let immediateThumbnailData: Data?
public let mimeType: String
public let size: Int?
@ -249,11 +284,12 @@ public final class TelegramMediaFile: Media, Equatable {
return self.fileId
}
public init(fileId: MediaId, partialReference: PartialMediaReference?, resource: TelegramMediaResource, previewRepresentations: [TelegramMediaImageRepresentation], immediateThumbnailData: Data?, mimeType: String, size: Int?, attributes: [TelegramMediaFileAttribute]) {
public init(fileId: MediaId, partialReference: PartialMediaReference?, resource: TelegramMediaResource, previewRepresentations: [TelegramMediaImageRepresentation], videoThumbnails: [TelegramMediaFile.VideoThumbnail], immediateThumbnailData: Data?, mimeType: String, size: Int?, attributes: [TelegramMediaFileAttribute]) {
self.fileId = fileId
self.partialReference = partialReference
self.resource = resource
self.previewRepresentations = previewRepresentations
self.videoThumbnails = videoThumbnails
self.immediateThumbnailData = immediateThumbnailData
self.mimeType = mimeType
self.size = size
@ -265,6 +301,7 @@ public final class TelegramMediaFile: Media, Equatable {
self.partialReference = decoder.decodeAnyObjectForKey("prf", decoder: { PartialMediaReference(decoder: $0) }) as? PartialMediaReference
self.resource = decoder.decodeObjectForKey("r") as? TelegramMediaResource ?? EmptyMediaResource()
self.previewRepresentations = decoder.decodeObjectArrayForKey("pr")
self.videoThumbnails = decoder.decodeObjectArrayForKey("vr")
self.immediateThumbnailData = decoder.decodeDataForKey("itd")
self.mimeType = decoder.decodeStringForKey("mt", orElse: "")
if let size = decoder.decodeOptionalInt32ForKey("s") {
@ -286,6 +323,7 @@ public final class TelegramMediaFile: Media, Equatable {
}
encoder.encodeObject(self.resource, forKey: "r")
encoder.encodeObjectArray(self.previewRepresentations, forKey: "pr")
encoder.encodeObjectArray(self.videoThumbnails, forKey: "vr")
if let immediateThumbnailData = self.immediateThumbnailData {
encoder.encodeData(immediateThumbnailData, forKey: "itd")
} else {
@ -449,6 +487,10 @@ public final class TelegramMediaFile: Media, Equatable {
}
}
if self.videoThumbnails.count != other.videoThumbnails.count {
return false
}
if self.immediateThumbnailData != other.immediateThumbnailData {
return false
}
@ -465,19 +507,19 @@ public final class TelegramMediaFile: Media, Equatable {
}
public func withUpdatedPartialReference(_ partialReference: PartialMediaReference?) -> TelegramMediaFile {
return TelegramMediaFile(fileId: self.fileId, partialReference: partialReference, resource: self.resource, previewRepresentations: self.previewRepresentations, immediateThumbnailData: self.immediateThumbnailData, mimeType: self.mimeType, size: self.size, attributes: self.attributes)
return TelegramMediaFile(fileId: self.fileId, partialReference: partialReference, resource: self.resource, previewRepresentations: self.previewRepresentations, videoThumbnails: self.videoThumbnails, immediateThumbnailData: self.immediateThumbnailData, mimeType: self.mimeType, size: self.size, attributes: self.attributes)
}
public func withUpdatedSize(_ size: Int?) -> TelegramMediaFile {
return TelegramMediaFile(fileId: self.fileId, partialReference: self.partialReference, resource: self.resource, previewRepresentations: self.previewRepresentations, immediateThumbnailData: self.immediateThumbnailData, mimeType: self.mimeType, size: size, attributes: self.attributes)
return TelegramMediaFile(fileId: self.fileId, partialReference: self.partialReference, resource: self.resource, previewRepresentations: self.previewRepresentations, videoThumbnails: self.videoThumbnails, immediateThumbnailData: self.immediateThumbnailData, mimeType: self.mimeType, size: size, attributes: self.attributes)
}
public func withUpdatedPreviewRepresentations(_ previewRepresentations: [TelegramMediaImageRepresentation]) -> TelegramMediaFile {
return TelegramMediaFile(fileId: self.fileId, partialReference: self.partialReference, resource: self.resource, previewRepresentations: previewRepresentations, immediateThumbnailData: self.immediateThumbnailData, mimeType: self.mimeType, size: self.size, attributes: self.attributes)
return TelegramMediaFile(fileId: self.fileId, partialReference: self.partialReference, resource: self.resource, previewRepresentations: previewRepresentations, videoThumbnails: self.videoThumbnails, immediateThumbnailData: self.immediateThumbnailData, mimeType: self.mimeType, size: self.size, attributes: self.attributes)
}
public func withUpdatedAttributes(_ attributes: [TelegramMediaFileAttribute]) -> TelegramMediaFile {
return TelegramMediaFile(fileId: self.fileId, partialReference: self.partialReference, resource: self.resource, previewRepresentations: self.previewRepresentations, immediateThumbnailData: self.immediateThumbnailData, mimeType: self.mimeType, size: self.size, attributes: attributes)
return TelegramMediaFile(fileId: self.fileId, partialReference: self.partialReference, resource: self.resource, previewRepresentations: self.previewRepresentations, videoThumbnails: self.videoThumbnails, immediateThumbnailData: self.immediateThumbnailData, mimeType: self.mimeType, size: self.size, attributes: attributes)
}
}

View File

@ -105,6 +105,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[997055186] = { return Api.PollAnswerVoters.parse_pollAnswerVoters($0) }
dict[-1705233435] = { return Api.account.PasswordSettings.parse_passwordSettings($0) }
dict[-288727837] = { return Api.LangPackLanguage.parse_langPackLanguage($0) }
dict[1130084743] = { return Api.VideoSize.parse_videoSize($0) }
dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) }
dict[497489295] = { return Api.help.AppUpdate.parse_appUpdate($0) }
dict[-209337866] = { return Api.LangPackDifference.parse_langPackDifference($0) }
@ -251,6 +252,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1512627963] = { return Api.Update.parse_updateDialogFilterOrder($0) }
dict[889491791] = { return Api.Update.parse_updateDialogFilters($0) }
dict[643940105] = { return Api.Update.parse_updatePhoneCallSignalingData($0) }
dict[357013699] = { return Api.Update.parse_updateMessageReactions($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
@ -297,6 +299,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
dict[1158290442] = { return Api.messages.FoundGifs.parse_foundGifs($0) }
dict[-1199954735] = { return Api.MessageReactions.parse_messageReactions($0) }
dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) }
dict[-2032041631] = { return Api.Poll.parse_poll($0) }
dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) }
@ -441,6 +444,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-524237339] = { return Api.PageTableRow.parse_pageTableRow($0) }
dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) }
dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) }
dict[-1553558980] = { return Api.messages.MessageReactionsList.parse_messageReactionsList($0) }
dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) }
dict[-1038136962] = { return Api.EncryptedFile.parse_encryptedFileEmpty($0) }
dict[1248893260] = { return Api.EncryptedFile.parse_encryptedFile($0) }
@ -590,7 +594,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1820043071] = { return Api.User.parse_user($0) }
dict[-2082087340] = { return Api.Message.parse_messageEmpty($0) }
dict[-1642487306] = { return Api.Message.parse_messageService($0) }
dict[1160515173] = { return Api.Message.parse_message($0) }
dict[-1752573244] = { return Api.Message.parse_message($0) }
dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) }
dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) }
dict[-182231723] = { return Api.InputFileLocation.parse_inputEncryptedFileLocation($0) }
@ -653,6 +657,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1834973166] = { return Api.BaseTheme.parse_baseThemeTinted($0) }
dict[1527845466] = { return Api.BaseTheme.parse_baseThemeArctic($0) }
dict[398898678] = { return Api.help.Support.parse_support($0) }
dict[1873957073] = { return Api.ReactionCount.parse_reactionCount($0) }
dict[1474492012] = { return Api.MessagesFilter.parse_inputMessagesFilterEmpty($0) }
dict[-1777752804] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotos($0) }
dict[-1614803355] = { return Api.MessagesFilter.parse_inputMessagesFilterVideo($0) }
@ -680,6 +685,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1449145777] = { return Api.upload.CdnFile.parse_cdnFile($0) }
dict[1984136919] = { return Api.wallet.LiteResponse.parse_liteResponse($0) }
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
dict[-764945220] = { return Api.MessageUserReaction.parse_messageUserReaction($0) }
dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) }
dict[982505656] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) }
dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) }
@ -820,7 +826,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-901375139] = { return Api.PeerLocated.parse_peerLocated($0) }
dict[-118740917] = { return Api.PeerLocated.parse_peerSelfLocated($0) }
dict[922273905] = { return Api.Document.parse_documentEmpty($0) }
dict[-1683841855] = { return Api.Document.parse_document($0) }
dict[512177195] = { return Api.Document.parse_document($0) }
dict[-1707344487] = { return Api.messages.HighScores.parse_highScores($0) }
dict[-892779534] = { return Api.WebAuthorization.parse_webAuthorization($0) }
dict[-805141448] = { return Api.ImportedContact.parse_importedContact($0) }
@ -842,7 +848,7 @@ public struct Api {
return parser(reader)
}
else {
telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found")
telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found")
return nil
}
}
@ -942,6 +948,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.LangPackLanguage:
_1.serialize(buffer, boxed)
case let _1 as Api.VideoSize:
_1.serialize(buffer, boxed)
case let _1 as Api.help.AppUpdate:
_1.serialize(buffer, boxed)
case let _1 as Api.LangPackDifference:
@ -1024,6 +1032,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.FoundGifs:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageReactions:
_1.serialize(buffer, boxed)
case let _1 as Api.FileLocation:
_1.serialize(buffer, boxed)
case let _1 as Api.Poll:
@ -1132,6 +1142,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.DraftMessage:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.MessageReactionsList:
_1.serialize(buffer, boxed)
case let _1 as Api.account.SentEmailCode:
_1.serialize(buffer, boxed)
case let _1 as Api.EncryptedFile:
@ -1340,6 +1352,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.help.Support:
_1.serialize(buffer, boxed)
case let _1 as Api.ReactionCount:
_1.serialize(buffer, boxed)
case let _1 as Api.MessagesFilter:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.Dialogs:
@ -1352,6 +1366,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.help.InviteText:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageUserReaction:
_1.serialize(buffer, boxed)
case let _1 as Api.BotInlineMessage:
_1.serialize(buffer, boxed)
case let _1 as Api.InputPeerNotifySettings:

View File

@ -823,6 +823,68 @@ public struct messages {
}
}
}
public enum MessageReactionsList: TypeConstructorDescription {
case messageReactionsList(flags: Int32, count: Int32, reactions: [Api.MessageUserReaction], users: [Api.User], nextOffset: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .messageReactionsList(let flags, let count, let reactions, let users, let nextOffset):
if boxed {
buffer.appendInt32(-1553558980)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(reactions.count))
for item in reactions {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .messageReactionsList(let flags, let count, let reactions, let users, let nextOffset):
return ("messageReactionsList", [("flags", flags), ("count", count), ("reactions", reactions), ("users", users), ("nextOffset", nextOffset)])
}
}
public static func parse_messageReactionsList(_ reader: BufferReader) -> MessageReactionsList? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: [Api.MessageUserReaction]?
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageUserReaction.self)
}
var _4: [Api.User]?
if let _ = reader.readInt32() {
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
var _5: String?
if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.messages.MessageReactionsList.messageReactionsList(flags: _1!, count: _2!, reactions: _3!, users: _4!, nextOffset: _5)
}
else {
return nil
}
}
}
public enum SearchCounter: TypeConstructorDescription {
case searchCounter(flags: Int32, filter: Api.MessagesFilter, count: Int32)
@ -4672,6 +4734,58 @@ public extension Api {
}
}
}
public enum VideoSize: TypeConstructorDescription {
case videoSize(type: String, location: Api.FileLocation, w: Int32, h: Int32, size: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .videoSize(let type, let location, let w, let h, let size):
if boxed {
buffer.appendInt32(1130084743)
}
serializeString(type, buffer: buffer, boxed: false)
location.serialize(buffer, true)
serializeInt32(w, buffer: buffer, boxed: false)
serializeInt32(h, buffer: buffer, boxed: false)
serializeInt32(size, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .videoSize(let type, let location, let w, let h, let size):
return ("videoSize", [("type", type), ("location", location), ("w", w), ("h", h), ("size", size)])
}
}
public static func parse_videoSize(_ reader: BufferReader) -> VideoSize? {
var _1: String?
_1 = parseString(reader)
var _2: Api.FileLocation?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.FileLocation
}
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
var _5: Int32?
_5 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.VideoSize.videoSize(type: _1!, location: _2!, w: _3!, h: _4!, size: _5!)
}
else {
return nil
}
}
}
public enum LangPackDifference: TypeConstructorDescription {
case langPackDifference(langCode: String, fromVersion: Int32, version: Int32, strings: [Api.LangPackString])
@ -5895,6 +6009,7 @@ public extension Api {
case updateDialogFilterOrder(order: [Int32])
case updateDialogFilters
case updatePhoneCallSignalingData(phoneCallId: Int64, data: Buffer)
case updateMessageReactions(peer: Api.Peer, msgId: Int32, reactions: Api.MessageReactions)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -6575,6 +6690,14 @@ public extension Api {
serializeInt64(phoneCallId, buffer: buffer, boxed: false)
serializeBytes(data, buffer: buffer, boxed: false)
break
case .updateMessageReactions(let peer, let msgId, let reactions):
if boxed {
buffer.appendInt32(357013699)
}
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false)
reactions.serialize(buffer, true)
break
}
}
@ -6742,6 +6865,8 @@ public extension Api {
return ("updateDialogFilters", [])
case .updatePhoneCallSignalingData(let phoneCallId, let data):
return ("updatePhoneCallSignalingData", [("phoneCallId", phoneCallId), ("data", data)])
case .updateMessageReactions(let peer, let msgId, let reactions):
return ("updateMessageReactions", [("peer", peer), ("msgId", msgId), ("reactions", reactions)])
}
}
@ -8087,6 +8212,27 @@ public extension Api {
return nil
}
}
public static func parse_updateMessageReactions(_ reader: BufferReader) -> Update? {
var _1: Api.Peer?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _2: Int32?
_2 = reader.readInt32()
var _3: Api.MessageReactions?
if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.MessageReactions
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.Update.updateMessageReactions(peer: _1!, msgId: _2!, reactions: _3!)
}
else {
return nil
}
}
}
public enum PopularContact: TypeConstructorDescription {
@ -9172,6 +9318,50 @@ public extension Api {
}
}
}
public enum MessageReactions: TypeConstructorDescription {
case messageReactions(flags: Int32, results: [Api.ReactionCount])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .messageReactions(let flags, let results):
if boxed {
buffer.appendInt32(-1199954735)
}
serializeInt32(flags, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(results.count))
for item in results {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .messageReactions(let flags, let results):
return ("messageReactions", [("flags", flags), ("results", results)])
}
}
public static func parse_messageReactions(_ reader: BufferReader) -> MessageReactions? {
var _1: Int32?
_1 = reader.readInt32()
var _2: [Api.ReactionCount]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReactionCount.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.MessageReactions.messageReactions(flags: _1!, results: _2!)
}
else {
return nil
}
}
}
public enum FileLocation: TypeConstructorDescription {
case fileLocationToBeDeprecated(volumeId: Int64, localId: Int32)
@ -16754,7 +16944,7 @@ public extension Api {
public enum Message: TypeConstructorDescription {
case messageEmpty(id: Int32)
case messageService(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, replyToMsgId: Int32?, date: Int32, action: Api.MessageAction)
case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?)
case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -16776,9 +16966,9 @@ public extension Api {
serializeInt32(date, buffer: buffer, boxed: false)
action.serialize(buffer, true)
break
case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let restrictionReason):
case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason):
if boxed {
buffer.appendInt32(1160515173)
buffer.appendInt32(-1752573244)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
@ -16800,6 +16990,7 @@ public extension Api {
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(editDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 16) != 0 {serializeString(postAuthor!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 17) != 0 {serializeInt64(groupedId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 20) != 0 {reactions!.serialize(buffer, true)}
if Int(flags) & Int(1 << 22) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(restrictionReason!.count))
for item in restrictionReason! {
@ -16815,8 +17006,8 @@ public extension Api {
return ("messageEmpty", [("id", id)])
case .messageService(let flags, let id, let fromId, let toId, let replyToMsgId, let date, let action):
return ("messageService", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("replyToMsgId", replyToMsgId), ("date", date), ("action", action)])
case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let restrictionReason):
return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)])
case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason):
return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("reactions", reactions), ("restrictionReason", restrictionReason)])
}
}
@ -16907,9 +17098,13 @@ public extension Api {
if Int(_1!) & Int(1 << 16) != 0 {_15 = parseString(reader) }
var _16: Int64?
if Int(_1!) & Int(1 << 17) != 0 {_16 = reader.readInt64() }
var _17: [Api.RestrictionReason]?
var _17: Api.MessageReactions?
if Int(_1!) & Int(1 << 20) != 0 {if let signature = reader.readInt32() {
_17 = Api.parse(reader, signature: signature) as? Api.MessageReactions
} }
var _18: [Api.RestrictionReason]?
if Int(_1!) & Int(1 << 22) != 0 {if let _ = reader.readInt32() {
_17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
_18 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
@ -16927,9 +17122,10 @@ public extension Api {
let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil
let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil
let _c16 = (Int(_1!) & Int(1 << 17) == 0) || _16 != nil
let _c17 = (Int(_1!) & Int(1 << 22) == 0) || _17 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 {
return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, editDate: _14, postAuthor: _15, groupedId: _16, restrictionReason: _17)
let _c17 = (Int(_1!) & Int(1 << 20) == 0) || _17 != nil
let _c18 = (Int(_1!) & Int(1 << 22) == 0) || _18 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 {
return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, editDate: _14, postAuthor: _15, groupedId: _16, reactions: _17, restrictionReason: _18)
}
else {
return nil
@ -18702,6 +18898,48 @@ public extension Api {
return Api.BaseTheme.baseThemeArctic
}
}
public enum ReactionCount: TypeConstructorDescription {
case reactionCount(flags: Int32, reaction: String, count: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .reactionCount(let flags, let reaction, let count):
if boxed {
buffer.appendInt32(1873957073)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(reaction, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .reactionCount(let flags, let reaction, let count):
return ("reactionCount", [("flags", flags), ("reaction", reaction), ("count", count)])
}
}
public static func parse_reactionCount(_ reader: BufferReader) -> ReactionCount? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.ReactionCount.reactionCount(flags: _1!, reaction: _2!, count: _3!)
}
else {
return nil
}
}
}
public enum MessagesFilter: TypeConstructorDescription {
case inputMessagesFilterEmpty
@ -19014,6 +19252,44 @@ public extension Api {
}
}
}
public enum MessageUserReaction: TypeConstructorDescription {
case messageUserReaction(userId: Int32, reaction: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .messageUserReaction(let userId, let reaction):
if boxed {
buffer.appendInt32(-764945220)
}
serializeInt32(userId, buffer: buffer, boxed: false)
serializeString(reaction, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .messageUserReaction(let userId, let reaction):
return ("messageUserReaction", [("userId", userId), ("reaction", reaction)])
}
}
public static func parse_messageUserReaction(_ reader: BufferReader) -> MessageUserReaction? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.MessageUserReaction.messageUserReaction(userId: _1!, reaction: _2!)
}
else {
return nil
}
}
}
public enum BotInlineMessage: TypeConstructorDescription {
case botInlineMessageText(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
@ -22447,7 +22723,7 @@ public extension Api {
}
public enum Document: TypeConstructorDescription {
case documentEmpty(id: Int64)
case document(flags: Int32, id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, mimeType: String, size: Int32, thumbs: [Api.PhotoSize]?, dcId: Int32, attributes: [Api.DocumentAttribute])
case document(flags: Int32, id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, mimeType: String, size: Int32, thumbs: [Api.PhotoSize]?, videoThumbs: [Api.VideoSize]?, dcId: Int32, attributes: [Api.DocumentAttribute])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -22457,9 +22733,9 @@ public extension Api {
}
serializeInt64(id, buffer: buffer, boxed: false)
break
case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let dcId, let attributes):
case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let videoThumbs, let dcId, let attributes):
if boxed {
buffer.appendInt32(-1683841855)
buffer.appendInt32(512177195)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
@ -22473,6 +22749,11 @@ public extension Api {
for item in thumbs! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(videoThumbs!.count))
for item in videoThumbs! {
item.serialize(buffer, true)
}}
serializeInt32(dcId, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(attributes.count))
@ -22487,8 +22768,8 @@ public extension Api {
switch self {
case .documentEmpty(let id):
return ("documentEmpty", [("id", id)])
case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let dcId, let attributes):
return ("document", [("flags", flags), ("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("date", date), ("mimeType", mimeType), ("size", size), ("thumbs", thumbs), ("dcId", dcId), ("attributes", attributes)])
case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let videoThumbs, let dcId, let attributes):
return ("document", [("flags", flags), ("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("date", date), ("mimeType", mimeType), ("size", size), ("thumbs", thumbs), ("videoThumbs", videoThumbs), ("dcId", dcId), ("attributes", attributes)])
}
}
@ -22522,11 +22803,15 @@ public extension Api {
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self)
} }
var _9: Int32?
_9 = reader.readInt32()
var _10: [Api.DocumentAttribute]?
var _9: [Api.VideoSize]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.VideoSize.self)
} }
var _10: Int32?
_10 = reader.readInt32()
var _11: [Api.DocumentAttribute]?
if let _ = reader.readInt32() {
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DocumentAttribute.self)
_11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DocumentAttribute.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
@ -22536,10 +22821,11 @@ public extension Api {
let _c6 = _6 != nil
let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil
let _c9 = _9 != nil
let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil
let _c10 = _10 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
return Api.Document.document(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, mimeType: _6!, size: _7!, thumbs: _8, dcId: _9!, attributes: _10!)
let _c11 = _11 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
return Api.Document.document(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, mimeType: _6!, size: _7!, thumbs: _8, videoThumbs: _9, dcId: _10!, attributes: _11!)
}
else {
return nil

View File

@ -3292,6 +3292,61 @@ public extension Api {
return result
})
}
public static func sendReaction(flags: Int32, peer: Api.InputPeer, msgId: Int32, reaction: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(627641572)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(reaction!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.sendReaction", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("reaction", reaction)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func getMessagesReactions(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-1950707482)
peer.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(id.count))
for item in id {
serializeInt32(item, buffer: buffer, boxed: false)
}
return (FunctionDescription(name: "messages.getMessagesReactions", parameters: [("peer", peer), ("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func getMessageReactionsList(flags: Int32, peer: Api.InputPeer, id: Int32, reaction: String?, offset: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.MessageReactionsList>) {
let buffer = Buffer()
buffer.appendInt32(-521245833)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(id, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(reaction!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(offset!, buffer: buffer, boxed: false)}
serializeInt32(limit, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getMessageReactionsList", parameters: [("flags", flags), ("peer", peer), ("id", id), ("reaction", reaction), ("offset", offset), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.MessageReactionsList? in
let reader = BufferReader(buffer)
var result: Api.messages.MessageReactionsList?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.messages.MessageReactionsList
}
return result
})
}
}
public struct channels {
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {

View File

@ -248,7 +248,7 @@ func rateCallAndSendLogs(account: Account, callId: CallId, starsCount: Int, comm
let id = arc4random64()
let name = "\(callId.id)_\(callId.accessHash).log"
let path = callLogsPath(account: account) + "/" + name
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
let message = EnqueueMessage.message(text: comment, attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
return rate
|> then(enqueueMessages(account: account, peerId: peerId, messages: [message])

View File

@ -158,6 +158,7 @@ private var declaredEncodables: Void = {
declareEncodable(ChatListFiltersFeaturedState.self, f: { ChatListFiltersFeaturedState(decoder: $0) })
declareEncodable(SynchronizeChatListFiltersOperation.self, f: { SynchronizeChatListFiltersOperation(decoder: $0) })
declareEncodable(PromoChatListItem.self, f: { PromoChatListItem(decoder: $0) })
declareEncodable(TelegramMediaFile.VideoThumbnail.self, f: { TelegramMediaFile.VideoThumbnail(decoder: $0) })
return
}()

View File

@ -53,7 +53,7 @@ func augmentMediaWithReference(_ mediaReference: AnyMediaReference) -> Media {
private func convertForwardedMediaForSecretChat(_ media: Media) -> Media {
if let file = media as? TelegramMediaFile {
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: file.partialReference, resource: file.resource, previewRepresentations: file.previewRepresentations, immediateThumbnailData: file.immediateThumbnailData, mimeType: file.mimeType, size: file.size, attributes: file.attributes)
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: file.partialReference, resource: file.resource, previewRepresentations: file.previewRepresentations, videoThumbnails: file.videoThumbnails, immediateThumbnailData: file.immediateThumbnailData, mimeType: file.mimeType, size: file.size, attributes: file.attributes)
} else if let image = media as? TelegramMediaImage {
return TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: image.representations, immediateThumbnailData: image.immediateThumbnailData, reference: image.reference, partialReference: image.partialReference, flags: [])
} else {

View File

@ -1413,7 +1413,7 @@ private func sendMessage(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Pos
if let fromMedia = currentMessage.media.first, let encryptedFile = encryptedFile, let file = file {
var toMedia: Media?
if let fromMedia = fromMedia as? TelegramMediaFile {
let updatedFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: encryptedFile.id), partialReference: nil, resource: SecretFileMediaResource(fileId: encryptedFile.id, accessHash: encryptedFile.accessHash, containerSize: encryptedFile.size, decryptedSize: file.size, datacenterId: Int(encryptedFile.datacenterId), key: file.key), previewRepresentations: fromMedia.previewRepresentations, immediateThumbnailData: fromMedia.immediateThumbnailData, mimeType: fromMedia.mimeType, size: fromMedia.size, attributes: fromMedia.attributes)
let updatedFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: encryptedFile.id), partialReference: nil, resource: SecretFileMediaResource(fileId: encryptedFile.id, accessHash: encryptedFile.accessHash, containerSize: encryptedFile.size, decryptedSize: file.size, datacenterId: Int(encryptedFile.datacenterId), key: file.key), previewRepresentations: fromMedia.previewRepresentations, videoThumbnails: fromMedia.videoThumbnails, immediateThumbnailData: fromMedia.immediateThumbnailData, mimeType: fromMedia.mimeType, size: fromMedia.size, attributes: fromMedia.attributes)
toMedia = updatedFile
updatedMedia = [updatedFile]
}

View File

@ -73,7 +73,7 @@ private final class MessageReactionCategoryContext {
self.state.loadingMore = true
self.statePromise.set(self.state)
/*var flags: Int32 = 0
var flags: Int32 = 0
var reaction: String?
switch self.category {
case .all:
@ -89,7 +89,7 @@ private final class MessageReactionCategoryContext {
return inputPeer
}
|> castError(LoadReactionsError.self)
|> mapToSignal { inputPeer -> Signal<Api.MessageReactionsList, LoadReactionsError> in
|> mapToSignal { inputPeer -> Signal<Api.messages.MessageReactionsList, LoadReactionsError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
@ -99,7 +99,7 @@ private final class MessageReactionCategoryContext {
}
}
//#if DEBUG
request = request |> delay(1.0, queue: .mainQueue())
//request = request |> delay(1.0, queue: .mainQueue())
//#endif
self.loadingDisposable.set((request
|> deliverOnMainQueue).start(next: { [weak self] result in
@ -141,7 +141,7 @@ private final class MessageReactionCategoryContext {
})
}, error: { _ in
}))*/
}))
}
}

View File

@ -33,9 +33,7 @@ private enum RequestUpdateMessageReactionError {
}
private func requestUpdateMessageReaction(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId) -> Signal<Never, RequestUpdateMessageReactionError> {
return .complete()
/*return postbox.transaction { transaction -> (Peer, String?)? in
return postbox.transaction { transaction -> (Peer, String?)? in
guard let peer = transaction.getPeer(messageId.peerId) else {
return nil
}
@ -91,7 +89,7 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st
|> castError(RequestUpdateMessageReactionError.self)
|> ignoreValues
}
}*/
}
}
private final class ManagedApplyPendingMessageReactionsActionsHelper {

View File

@ -111,7 +111,7 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
resource = EmptyMediaResource()
}
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, 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: [], immediateThumbnailData: nil, mimeType: 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

@ -717,7 +717,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
}
case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv):
if let file = file {
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
parsedMedia.append(fileMedia)
}
case let .decryptedMessageMediaDocument(thumb, thumbW, thumbH, mimeType, size, key, iv, attributes, caption):
@ -737,7 +737,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
}
case let .decryptedMessageMediaVideo(thumb, thumbW, thumbH, duration, mimeType, w, h, size, key, iv, caption):
@ -752,7 +752,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
}
case let .decryptedMessageMediaExternalDocument(id, accessHash, _, mimeType, size, thumb, dcId, attributes):
@ -785,7 +785,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
default:
break
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
case let .decryptedMessageMediaWebPage(url):
parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url)))
@ -915,7 +915,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
}
case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv):
if let file = file {
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
parsedMedia.append(fileMedia)
attributes.append(ConsumableContentMessageAttribute(consumed: false))
}
@ -936,7 +936,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
loop: for attr in parsedAttributes {
@ -967,7 +967,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
}
case let .decryptedMessageMediaExternalDocument(id, accessHash, date, mimeType, size, thumb, dcId, attributes):
@ -1000,7 +1000,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
default:
break
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
case let .decryptedMessageMediaWebPage(url):
parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url)))
@ -1149,7 +1149,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
}
case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv):
if let file = file {
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])
parsedMedia.append(fileMedia)
attributes.append(ConsumableContentMessageAttribute(consumed: false))
}
@ -1170,7 +1170,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
loop: for attr in parsedAttributes {
@ -1201,7 +1201,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
}
case let .decryptedMessageMediaExternalDocument(id, accessHash, date, mimeType, size, thumb, dcId, attributes):
@ -1234,7 +1234,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
default:
break
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
parsedMedia.append(fileMedia)
case let .decryptedMessageMediaWebPage(url):
parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url)))

View File

@ -4,7 +4,7 @@ import TelegramApi
import SyncCore
/*extension ReactionsMessageAttribute {
extension ReactionsMessageAttribute {
func withUpdatedResults(_ reactions: Api.MessageReactions) -> ReactionsMessageAttribute {
switch reactions {
case let .messageReactions(flags, results):
@ -34,7 +34,7 @@ import SyncCore
return ReactionsMessageAttribute(reactions: reactions)
}
}
}*/
}
public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsMessageAttribute? {
var current: ReactionsMessageAttribute?
@ -86,7 +86,7 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM
}
}
/*extension ReactionsMessageAttribute {
extension ReactionsMessageAttribute {
convenience init(apiReactions: Api.MessageReactions) {
switch apiReactions {
case let .messageReactions(_, results):
@ -98,4 +98,4 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM
})
}
}
}*/
}

View File

@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 114
return 117
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -195,7 +195,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin
|> mapToSignal { result -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
switch result {
case let .encryptedFile(id, accessHash, size, dcId, _):
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: size, datacenterId: Int(dcId), key: key), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: attributes)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: size, datacenterId: Int(dcId), key: key), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: attributes)
return .single(.result(.media(.standalone(media: media))))
case .encryptedFileEmpty:

View File

@ -136,7 +136,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
switch message {
case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, media, _, entities, _, _, _, _, _):
case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, media, _, entities, _, _, _, _, _, _):
let peerId: PeerId
switch toId {
case let .peerUser(userId):
@ -240,7 +240,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? {
switch message {
case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _):
case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _, _):
if let replyToMsgId = replyToMsgId {
let peerId: PeerId
switch toId {
@ -398,7 +398,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
extension StoreMessage {
convenience init?(apiMessage: Api.Message, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
switch apiMessage {
case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, date, message, media, replyMarkup, entities, views, editDate, postAuthor, groupingId, restrictionReason):
case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, date, message, media, replyMarkup, entities, views, editDate, postAuthor, groupingId, reactions, restrictionReason):
let peerId: PeerId
var authorId: PeerId?
switch toId {
@ -553,9 +553,9 @@ extension StoreMessage {
attributes.append(ContentRequiresValidationMessageAttribute())
}
/*if let reactions = reactions {
if let reactions = reactions {
attributes.append(ReactionsMessageAttribute(apiReactions: reactions))
}*/
}
if let restrictionReason = restrictionReason {
attributes.append(RestrictedContentMessageAttribute(rules: restrictionReason.map(RestrictionRule.init(apiReason:))))

View File

@ -148,11 +148,29 @@ func telegramMediaFileThumbnailRepresentationsFromApiSizes(datacenterId: Int32,
func telegramMediaFileFromApiDocument(_ document: Api.Document) -> TelegramMediaFile? {
switch document {
case let .document(_, id, accessHash, fileReference, _, mimeType, size, thumbs, dcId, attributes):
case let .document(_, id, accessHash, fileReference, _, mimeType, size, thumbs, videoThumbs, dcId, attributes):
let parsedAttributes = telegramMediaFileAttributesFromApiAttributes(attributes)
let (immediateThumbnail, previewRepresentations) = telegramMediaFileThumbnailRepresentationsFromApiSizes(datacenterId: dcId, documentId: id, accessHash: accessHash, fileReference: fileReference.makeData(), sizes: thumbs ?? [])
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: fileReference.makeData(), fileName: fileNameFromFileAttributes(parsedAttributes)), previewRepresentations: previewRepresentations, immediateThumbnailData: immediateThumbnail, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
var videoThumbnails: [TelegramMediaFile.VideoThumbnail] = []
if let videoThumbs = videoThumbs {
for thumb in videoThumbs {
switch thumb {
case let .videoSize(type, location, w, h, _):
let resource: TelegramMediaResource
switch location {
case let .fileLocationToBeDeprecated(volumeId, localId):
resource = CloudDocumentSizeMediaResource(datacenterId: dcId, documentId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference.makeData())
}
videoThumbnails.append(TelegramMediaFile.VideoThumbnail(
dimensions: PixelDimensions(width: w, height: h),
resource: resource))
}
}
}
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: fileReference.makeData(), fileName: fileNameFromFileAttributes(parsedAttributes)), previewRepresentations: previewRepresentations, videoThumbnails: videoThumbnails, immediateThumbnailData: immediateThumbnail, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
case .documentEmpty:
return nil
}

View File

@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService {
self.putNext(groups)
}
case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyToMsgId, entities):
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil)
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
if groups.count != 0 {
@ -75,7 +75,7 @@ class UpdateMessageService: NSObject, MTMessageService {
generatedToId = Api.Peer.peerUser(userId: self.peerId.id)
}
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil)
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
if groups.count != 0 {

View File

@ -28,7 +28,7 @@ private func collectPreCachedResources(for photo: Api.Photo) -> [(MediaResource,
private func collectPreCachedResources(for document: Api.Document) -> [(MediaResource, Data)]? {
switch document {
case let .document(_, id, accessHash, fileReference, _, _, _, thumbs, dcId, _):
case let .document(_, id, accessHash, fileReference, _, _, _, thumbs, _, dcId, _):
if let thumbs = thumbs {
for thumb in thumbs {
switch thumb {

View File

@ -101,7 +101,7 @@ extension TelegramWallpaper: Codable {
}
}
if let slug = slug {
self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: color != nil, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: blur, motion: motion, color: color, bottomColor: bottomColor, intensity: intensity, rotation: rotation))
self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: color != nil, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: blur, motion: motion, color: color, bottomColor: bottomColor, intensity: intensity, rotation: rotation))
} else {
throw PresentationThemeDecodingError.generic
}

View File

@ -240,4 +240,8 @@ public enum PresentationResourceParameterKey: Hashable {
case chatBubbleLamp(incoming: Bool)
case chatPsaInfo(color: UInt32)
case chatMessageLike(incoming: Bool, isSelected: Bool)
case chatMessageFreeLike(isSelected: Bool)
case chatMessageMediaLike(isSelected: Bool)
}

View File

@ -1001,4 +1001,35 @@ public struct PresentationResourcesChat {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/Question"), color: UIColor(rgb: color))
})
}
public static func chatMessageLike(_ theme: PresentationTheme, incoming: Bool, isSelected: Bool) -> UIImage? {
return theme.image(PresentationResourceParameterKey.chatMessageLike(incoming: incoming, isSelected: isSelected), { theme in
if isSelected {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Reactions/MessageHeartFilled"), color: UIColor(rgb: 0xfe1512))
} else {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Reactions/MessageHeartEmpty"), color: incoming ? theme.chat.message.incoming.secondaryTextColor : theme.chat.message.outgoing.secondaryTextColor)
}
})
}
public static func chatMessageFreeLike(_ theme: PresentationTheme, wallpaper: TelegramWallpaper, isSelected: Bool) -> UIImage? {
return theme.image(PresentationResourceParameterKey.chatMessageFreeLike(isSelected: isSelected), { theme in
if isSelected {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Reactions/MessageHeartFilled"), color: UIColor(rgb: 0xfe1512))
} else {
let serviceColor = serviceMessageColorComponents(theme: theme, wallpaper: wallpaper)
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Reactions/MessageHeartEmpty"), color: serviceColor.primaryText)
}
})
}
public static func chatMessageMediaLike(_ theme: PresentationTheme, isSelected: Bool) -> UIImage? {
return theme.image(PresentationResourceParameterKey.chatMessageMediaLike(isSelected: isSelected), { theme in
if isSelected {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Reactions/MessageHeartFilled"), color: UIColor(rgb: 0xfe1512))
} else {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Reactions/MessageHeartEmpty"), color: theme.chat.message.mediaDateAndStatusTextColor)
}
})
}
}

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_unlikelongtap_l.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_unlikelongtap_r.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_likelongtap.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_likemsg.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_likedmsg.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_unlikeswipe.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_likeswipe.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -162,7 +162,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont
imageDimensions = content?.dimensions?.cgSize
if let content = content, 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)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
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, _):
@ -258,7 +258,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont
let layerHolder = takeSampleBufferLayer()
layerHolder.layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.layer.addSublayer(layerHolder.layer)
let manager = SoftwareVideoLayerFrameManager(account: self.account, fileReference: videoFileReference, resource: videoFileReference.media.resource, layerHolder: layerHolder)
let manager = SoftwareVideoLayerFrameManager(account: self.account, fileReference: videoFileReference, layerHolder: layerHolder)
self.videoLayer = (thumbnailLayer, manager, layerHolder)
thumbnailLayer.ready = { [weak self, weak thumbnailLayer, weak manager] in
if let strongSelf = self, let thumbnailLayer = thumbnailLayer, let manager = manager {

View File

@ -578,27 +578,31 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
var reactionItems: [ReactionContextItem] = []
/*let reactions: [(String, String, String)] = [
("😔", "Sad", "sad"),
("😳", "Surprised", "surprised"),
("😂", "Fun", "lol"),
("👍", "Like", "thumbsup"),
("", "Love", "heart"),
("🥳", "Celebrate", "celebrate"),
("😭", "Cry", "cry"),
("😒", "Meh", "meh"),
("👌", "OK", "ok"),
("😐", "Poker", "poker"),
("💩", "Poop", "poop"),
("😊", "Smile", "smile")
]
for (value, text, name) in reactions {
if let path = getAppBundle().path(forResource: name, ofType: "tgs") {
reactionItems.append(ReactionContextItem(value: value, text: text, path: path))
var hasLike = false
let heart = "❤️"
for attribute in messages[0].attributes {
if let attribute = attribute as? ReactionsMessageAttribute {
for reaction in attribute.reactions {
if reaction.value == heart {
if reaction.isSelected {
hasLike = true
}
}
}
} else if let attribute = attribute as? PendingReactionsMessageAttribute {
if attribute.value == heart {
hasLike = true
}
}
}*/
if Namespaces.Message.allScheduled.contains(message.id.namespace) {
}
if hasLike {
reactionItems.append(ReactionContextItem(reaction: .unlike))
} else {
reactionItems.append(ReactionContextItem(reaction: .like))
}
if Namespaces.Message.allScheduled.contains(message.id.namespace) || message.id.peerId.namespace == Namespaces.Peer.SecretChat {
reactionItems = []
}
@ -614,24 +618,40 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self, let message = updatedMessages.first else {
return
}
let heart = "❤️"
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item {
if item.message.id == message.id {
itemNode.awaitingAppliedReaction = (value, { [weak itemNode] in
guard let controller = controller else {
return
}
if let itemNode = itemNode, let (targetNode, count) = itemNode.targetReactionNode(value: value) {
controller.dismissWithReaction(value: value, into: targetNode, hideNode: count == 1, completion: {
})
} else {
switch value {
case .like:
itemNode.awaitingAppliedReaction = (heart, { [weak itemNode] in
guard let controller = controller else {
return
}
if let itemNode = itemNode, let (targetEmptyNode, targetFilledNode) = itemNode.targetReactionNode(value: heart) {
controller.dismissWithReaction(value: heart, targetEmptyNode: targetEmptyNode, targetFilledNode: targetFilledNode, hideNode: true, completion: {
})
} else {
controller.dismiss()
}
})
case .unlike:
itemNode.awaitingAppliedReaction = (nil, {
guard let controller = controller else {
return
}
controller.dismiss()
}
})
})
}
}
}
}
let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: message.id, reaction: value).start()
switch value {
case .like:
let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: message.id, reaction: heart).start()
case .unlike:
let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: message.id, reaction: nil).start()
}
}
strongSelf.forEachController({ controller in
if let controller = controller as? TooltipScreen {
@ -1464,13 +1484,38 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}, canSetupReply: { [weak self] message in
if !message.flags.contains(.Incoming) {
if !message.flags.intersection([.Failed, .Sending, .Unsent]).isEmpty {
return false
return .none
}
}
if let strongSelf = self {
return canReplyInChat(strongSelf.presentationInterfaceState)
if canReplyInChat(strongSelf.presentationInterfaceState) {
return .reply
} else if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
var hasLike = false
let heart = "❤️"
for attribute in message.attributes {
if let attribute = attribute as? ReactionsMessageAttribute {
for reaction in attribute.reactions {
if reaction.value == heart {
if reaction.isSelected {
hasLike = true
}
}
}
} else if let attribute = attribute as? PendingReactionsMessageAttribute {
if attribute.value == heart {
hasLike = true
}
}
}
if hasLike {
return .unlike
} else {
return .like
}
}
}
return false
return .none
}, navigateToFirstDateMessage: { [weak self] timestamp in
guard let strongSelf = self else {
return
@ -1899,11 +1944,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
window.rootViewController?.present(controller, animated: true)
}
}
}, updateMessageReaction: { [weak self] messageId, reaction in
}, updateMessageLike: { [weak self] messageId, isLiked in
guard let strongSelf = self else {
return
}
let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: messageId, reaction: reaction).start()
let heart = "❤️"
if isLiked {
let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: messageId, reaction: heart).start()
} else {
let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: messageId, reaction: nil).start()
}
}, openMessageReactions: { [weak self] messageId in
guard let strongSelf = self else {
return
@ -6073,7 +6124,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if mimeType == "application/pdf" {
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 320, height: 320), resource: ICloudFileResource(urlData: item.urlData, thumbnail: true)))
}
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData, thumbnail: false), previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: mimeType, size: item.fileSize, attributes: [.FileName(fileName: item.fileName)])
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData, thumbnail: false), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: item.fileSize, attributes: [.FileName(fileName: item.fileName)])
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: replyMessageId, localGroupingKey: nil)
messages.append(message)
}
@ -6845,7 +6896,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
fileAttributes.append(.Sticker(displayText: "", packReference: nil, maskData: nil))
fileAttributes.append(.ImageSize(size: PixelDimensions(size)))
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "image/webp", size: data.count, attributes: fileAttributes)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: data.count, attributes: fileAttributes)
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil)
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
@ -7027,7 +7078,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: data.compressedData.count, attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)])
strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: data.compressedData.count, attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)])
strongSelf.recorderFeedback?.tap()
strongSelf.recorderFeedback = nil
@ -7112,7 +7163,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
self.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: recordedMediaPreview.resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int(recordedMediaPreview.fileSize), attributes: [.Audio(isVoice: true, duration: Int(recordedMediaPreview.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)])
self.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: recordedMediaPreview.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int(recordedMediaPreview.fileSize), attributes: [.Audio(isVoice: true, duration: Int(recordedMediaPreview.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)])
}
}

View File

@ -49,6 +49,13 @@ struct ChatInterfacePollActionState: Equatable {
var pollMessageIdsInProgress: [MessageId: [Data]] = [:]
}
public enum ChatControllerInteractionSwipeAction {
case none
case reply
case like
case unlike
}
public final class ChatControllerInteraction {
let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool
let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void
@ -87,7 +94,7 @@ public final class ChatControllerInteraction {
let openCheckoutOrReceipt: (MessageId) -> Void
let openSearch: () -> Void
let setupReply: (MessageId) -> Void
let canSetupReply: (Message) -> Bool
let canSetupReply: (Message) -> ChatControllerInteractionSwipeAction
let navigateToFirstDateMessage: (Int32) -> Void
let requestRedeliveryOfFailedMessages: (MessageId) -> Void
let addContact: (String) -> Void
@ -101,7 +108,7 @@ public final class ChatControllerInteraction {
let sendScheduledMessagesNow: ([MessageId]) -> Void
let editScheduledMessagesTime: ([MessageId]) -> Void
let performTextSelectionAction: (UInt32, NSAttributedString, TextSelectionAction) -> Void
let updateMessageReaction: (MessageId, String?) -> Void
let updateMessageLike: (MessageId, Bool) -> Void
let openMessageReactions: (MessageId) -> Void
let displaySwipeToReplyHint: () -> Void
let dismissReplyMarkupMessage: (Message) -> Void
@ -128,7 +135,7 @@ public final class ChatControllerInteraction {
var searchTextHighightState: (String, [MessageIndex])?
var seenOneTimeAnimatedMedia = Set<MessageId>()
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String?) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
self.openMessage = openMessage
self.openPeer = openPeer
self.openPeerMention = openPeerMention
@ -183,7 +190,7 @@ public final class ChatControllerInteraction {
self.sendScheduledMessagesNow = sendScheduledMessagesNow
self.editScheduledMessagesTime = editScheduledMessagesTime
self.performTextSelectionAction = performTextSelectionAction
self.updateMessageReaction = updateMessageReaction
self.updateMessageLike = updateMessageLike
self.openMessageReactions = openMessageReactions
self.displaySwipeToReplyHint = displaySwipeToReplyHint
self.dismissReplyMarkupMessage = dismissReplyMarkupMessage
@ -211,7 +218,7 @@ public final class ChatControllerInteraction {
return nil
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
}, canSetupReply: { _ in
return false
return .none
}, navigateToFirstDateMessage: { _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
@ -225,7 +232,7 @@ public final class ChatControllerInteraction {
}, sendScheduledMessagesNow: { _ in
}, editScheduledMessagesTime: { _ in
}, performTextSelectionAction: { _, _, _ in
}, updateMessageReaction: { _, _ in
}, updateMessageLike: { _, _ in
}, openMessageReactions: { _ in
}, displaySwipeToReplyHint: {
}, dismissReplyMarkupMessage: { _ in

View File

@ -70,6 +70,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
private var appliedForwardInfo: (Peer?, String?)?
private var currentSwipeAction: ChatControllerInteractionSwipeAction?
required init() {
self.contextSourceNode = ContextExtractedContentContainingNode()
self.containerNode = ContextControllerSourceNode()
@ -122,6 +124,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.addSubnode(self.containerNode)
self.contextSourceNode.contentNode.addSubnode(self.imageNode)
self.contextSourceNode.contentNode.addSubnode(self.dateAndStatusNode)
self.dateAndStatusNode.openReactions = { [weak self] in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
item.controllerInteraction.openMessageReactions(item.message.id)
}
}
deinit {
@ -176,7 +185,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if strongSelf.selectionNode != nil {
return false
}
return item.controllerInteraction.canSetupReply(item.message)
let action = item.controllerInteraction.canSetupReply(item.message)
strongSelf.currentSwipeAction = action
if case .none = action {
return false
} else {
return true
}
}
return false
}
@ -1103,7 +1118,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if translation.x < -45.0, self.swipeToReplyNode == nil, let item = self.item {
self.swipeToReplyFeedback?.impact()
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper))
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
self.swipeToReplyNode = swipeToReplyNode
self.addSubnode(swipeToReplyNode)
animateReplyNodeIn = true
@ -1129,7 +1144,18 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let translation = recognizer.translation(in: self.view)
if case .ended = recognizer.state, translation.x < -45.0 {
if let item = self.item {
item.controllerInteraction.setupReply(item.message.id)
if let currentSwipeAction = currentSwipeAction {
switch currentSwipeAction {
case .none:
break
case .reply:
item.controllerInteraction.setupReply(item.message.id)
case .like:
item.controllerInteraction.updateMessageLike(item.message.id, true)
case .unlike:
item.controllerInteraction.updateMessageLike(item.message.id, true)
}
}
}
}
var bounds = self.bounds

View File

@ -1055,7 +1055,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
return self.contentImageNode?.playMediaWithSound()
}
func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
if !self.statusNode.isHidden {
return self.statusNode.reactionNode(value: value)
}

View File

@ -183,7 +183,7 @@ class ChatMessageBubbleContentNode: ASDisplayNode {
func updateIsExtractedToContextPreview(_ value: Bool) {
}
func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
return nil
}
}

View File

@ -196,6 +196,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer?
private var reactionRecognizer: ReactionSwipeGestureRecognizer?
private var currentSwipeAction: ChatControllerInteractionSwipeAction?
override var visibility: ListViewItemNodeVisibility {
didSet {
if self.visibility != oldValue {
@ -485,13 +487,19 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
}
}
return item.controllerInteraction.canSetupReply(item.message)
let action = item.controllerInteraction.canSetupReply(item.message)
strongSelf.currentSwipeAction = action
if case .none = action {
return false
} else {
return true
}
}
return false
}
self.view.addGestureRecognizer(replyRecognizer)
} else {
let reactionRecognizer = ReactionSwipeGestureRecognizer(target: nil, action: nil)
/*let reactionRecognizer = ReactionSwipeGestureRecognizer(target: nil, action: nil)
self.reactionRecognizer = reactionRecognizer
reactionRecognizer.availableReactions = { [weak self] in
guard let strongSelf = self, let item = strongSelf.item, !item.presentationData.isPreview && !Namespaces.Message.allScheduled.contains(item.message.id.namespace) else {
@ -686,7 +694,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
}
}
self.view.addGestureRecognizer(reactionRecognizer)
self.view.addGestureRecognizer(reactionRecognizer)*/
}
}
@ -2162,7 +2170,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
strongSelf.awaitingAppliedReaction = nil
var targetNode: ASDisplayNode?
/*var targetNode: ASDisplayNode?
var hideTarget = false
if let awaitingAppliedReaction = awaitingAppliedReaction {
for contentNode in strongSelf.contentNodes {
@ -2173,7 +2181,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
}
}
strongSelf.reactionRecognizer?.complete(into: targetNode, hideTarget: hideTarget)
strongSelf.reactionRecognizer?.complete(into: targetNode, hideTarget: hideTarget)*/
f()
}
}
@ -3002,7 +3010,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
if translation.x < -45.0, self.swipeToReplyNode == nil, let item = self.item {
self.swipeToReplyFeedback?.impact()
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper))
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
self.swipeToReplyNode = swipeToReplyNode
self.insertSubnode(swipeToReplyNode, belowSubnode: self.messageAccessibilityArea)
animateReplyNodeIn = true
@ -3031,7 +3039,18 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
let translation = recognizer.translation(in: self.view)
if case .ended = recognizer.state, translation.x < -45.0 {
if let item = self.item {
item.controllerInteraction.setupReply(item.message.id)
if let currentSwipeAction = currentSwipeAction {
switch currentSwipeAction {
case .none:
break
case .reply:
item.controllerInteraction.setupReply(item.message.id)
case .like:
item.controllerInteraction.updateMessageLike(item.message.id, true)
case .unlike:
item.controllerInteraction.updateMessageLike(item.message.id, false)
}
}
}
}
var bounds = self.bounds
@ -3094,10 +3113,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
self.contextSourceNode.contentNode.addSubnode(accessoryItemNode)
}
override func targetReactionNode(value: String) -> (ASDisplayNode, Int)? {
override func targetReactionNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
for contentNode in self.contentNodes {
if let (reactionNode, count) = contentNode.reactionTargetNode(value: value) {
return (reactionNode, count)
if let (emptyNode, filledNode) = contentNode.reactionTargetNode(value: value) {
return (emptyNode, filledNode)
}
}
return nil

View File

@ -217,7 +217,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
}
}
override func reactionTargetNode(value: String) -> (ASImageNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
return nil
}
}

View File

@ -349,7 +349,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
}
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
if !self.dateAndStatusNode.isHidden {
return self.dateAndStatusNode.reactionNode(value: value)
}

View File

@ -43,89 +43,108 @@ enum ChatMessageDateAndStatusType: Equatable {
case FreeOutgoing(ChatMessageDateAndStatusOutgoingType)
}
private let reactionSize: CGFloat = 20.0
private let reactionFont = Font.regular(12.0)
private final class StatusReactionNodeParameters: NSObject {
let value: String
let previousValue: String?
init(value: String, previousValue: String?) {
self.value = value
self.previousValue = previousValue
}
}
private func drawReaction(context: CGContext, value: String, in rect: CGRect) {
var fileId: Int?
switch value {
case "😔":
fileId = 8
case "😳":
fileId = 19
case "😂":
fileId = 17
case "👍":
fileId = 6
case "":
fileId = 13
default:
break
}
if let fileId = fileId, let path = getAppBundle().path(forResource: "simplereaction_\(fileId)@2x", ofType: "png"), let image = UIImage(contentsOfFile: path) {
context.saveGState()
context.translateBy(x: rect.midX, y: rect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -rect.midX, y: -rect.midY)
context.draw(image.cgImage!, in: rect)
context.restoreGState()
} else {
let string = NSAttributedString(string: value, font: reactionFont, textColor: .black)
string.draw(at: CGPoint(x: rect.minX + 1.0, y: rect.minY + 3.0))
}
}
private final class StatusReactionNode: ASDisplayNode {
let value: String
var count: Int
var previousValue: String? {
didSet {
self.setNeedsDisplay()
}
}
let emptyImageNode: ASImageNode
let selectedImageNode: ASImageNode
init(value: String, count: Int, previousValue: String?) {
self.value = value
self.count = count
self.previousValue = previousValue
private var theme: PresentationTheme?
private var isSelected: Bool?
override init() {
self.emptyImageNode = ASImageNode()
self.emptyImageNode.displaysAsynchronously = false
self.selectedImageNode = ASImageNode()
self.selectedImageNode.displaysAsynchronously = false
super.init()
self.isOpaque = false
self.backgroundColor = nil
self.addSubnode(self.emptyImageNode)
self.addSubnode(self.selectedImageNode)
}
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
return StatusReactionNodeParameters(value: self.value, previousValue: self.previousValue)
}
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
let context = UIGraphicsGetCurrentContext()!
if !isRasterizing {
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.fill(bounds)
func update(type: ChatMessageDateAndStatusType, isSelected: Bool, count: Int, theme: PresentationTheme, wallpaper: TelegramWallpaper, animated: Bool) {
if self.theme !== theme {
self.theme = theme
let emptyImage: UIImage?
let selectedImage: UIImage?
switch type {
case .BubbleIncoming:
emptyImage = PresentationResourcesChat.chatMessageLike(theme, incoming: true, isSelected: false)
selectedImage = PresentationResourcesChat.chatMessageLike(theme, incoming: true, isSelected: true)
case .BubbleOutgoing:
emptyImage = PresentationResourcesChat.chatMessageLike(theme, incoming: false, isSelected: false)
selectedImage = PresentationResourcesChat.chatMessageLike(theme, incoming: false, isSelected: true)
case .ImageIncoming, .ImageOutgoing:
emptyImage = PresentationResourcesChat.chatMessageMediaLike(theme, isSelected: false)
selectedImage = PresentationResourcesChat.chatMessageMediaLike(theme, isSelected: true)
case .FreeIncoming, .FreeOutgoing:
emptyImage = PresentationResourcesChat.chatMessageFreeLike(theme, wallpaper: wallpaper, isSelected: false)
selectedImage = PresentationResourcesChat.chatMessageFreeLike(theme, wallpaper: wallpaper, isSelected: true)
}
if let emptyImage = emptyImage, let selectedImage = selectedImage {
self.emptyImageNode.image = emptyImage
self.emptyImageNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: emptyImage.size)
self.selectedImageNode.image = selectedImage
self.selectedImageNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: selectedImage.size)
}
}
guard let parameters = parameters as? StatusReactionNodeParameters else {
return
}
drawReaction(context: context, value: parameters.value, in: bounds)
if let previousValue = parameters.previousValue {
let previousRect = bounds.offsetBy(dx: -14.0, dy: 0)
context.setBlendMode(.destinationOut)
drawReaction(context: context, value: previousValue, in: previousRect)
if self.isSelected != isSelected {
let wasSelected = self.isSelected
self.isSelected = isSelected
self.emptyImageNode.isHidden = isSelected && count <= 1
self.selectedImageNode.isHidden = !isSelected
if let wasSelected = wasSelected, wasSelected, !isSelected {
if let image = self.selectedImageNode.image {
let leftImage = generateImage(image.size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
image.draw(in: CGRect(origin: CGPoint(), size: size))
UIGraphicsPopContext()
context.clear(CGRect(origin: CGPoint(x: size.width / 2.0, y: 0.0), size: CGSize(width: size.width / 2.0, height: size.height)))
})
let rightImage = generateImage(image.size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
image.draw(in: CGRect(origin: CGPoint(), size: size))
UIGraphicsPopContext()
context.clear(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width / 2.0, height: size.height)))
})
if let leftImage = leftImage, let rightImage = rightImage {
let leftView = UIImageView()
leftView.image = leftImage
leftView.frame = self.selectedImageNode.frame
let rightView = UIImageView()
rightView.image = rightImage
rightView.frame = self.selectedImageNode.frame
self.view.addSubview(leftView)
self.view.addSubview(rightView)
let duration: Double = 0.3
leftView.layer.animateRotation(from: 0.0, to: -CGFloat.pi * 0.7, duration: duration, removeOnCompletion: false)
rightView.layer.animateRotation(from: 0.0, to: CGFloat.pi * 0.7, duration: duration, removeOnCompletion: false)
leftView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -6.0, y: 8.0), duration: duration, removeOnCompletion: false, additive: true)
rightView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 6.0, y: 8.0), duration: duration, removeOnCompletion: false, additive: true)
leftView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { [weak leftView] _ in
leftView?.removeFromSuperview()
})
rightView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { [weak rightView] _ in
rightView?.removeFromSuperview()
})
}
}
}
}
}
}
@ -144,6 +163,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
private var type: ChatMessageDateAndStatusType?
private var theme: ChatPresentationThemeData?
private var layoutSize: CGSize?
var openReactions: (() -> Void)?
@ -173,6 +193,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
let makeReactionCountLayout = TextNode.asyncLayout(self.reactionCountNode)
let previousLayoutSize = self.layoutSize
return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions in
let dateColor: UIColor
var backgroundImage: UIImage?
@ -409,21 +431,31 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
backgroundInsets = UIEdgeInsets(top: 2.0, left: 7.0, bottom: 2.0, right: 7.0)
}
let reactionSize: CGFloat = 14.0
var reactionCountLayoutAndApply: (TextNodeLayout, () -> TextNode)?
let reactionSpacing: CGFloat = -4.0
let reactionTrailingSpacing: CGFloat = 4.0
var reactionInset: CGFloat = 0.0
if !reactions.isEmpty {
reactionInset = 5.0 + CGFloat(reactions.count) * reactionSize + CGFloat(reactions.count - 1) * reactionSpacing + reactionTrailingSpacing
reactionInset = -1.0 + CGFloat(reactions.count) * reactionSize + CGFloat(reactions.count - 1) * reactionSpacing + reactionTrailingSpacing
var count = 0
for reaction in reactions {
count += Int(reaction.count)
}
let layoutAndApply = makeReactionCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(count)", font: reactionCountFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0)))
reactionInset += layoutAndApply.0.size.width + 2.0
let countString: String
if count > 1000000 {
countString = "\(count / 1000000)M"
} else if count > 1000 {
countString = "\(count / 1000)K"
} else {
countString = "\(count)"
}
let layoutAndApply = makeReactionCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0)))
reactionInset += max(10.0, layoutAndApply.0.size.width) + 2.0
reactionCountLayoutAndApply = layoutAndApply
}
leftInset += reactionInset
@ -434,6 +466,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
if let strongSelf = self {
strongSelf.theme = presentationData.theme
strongSelf.type = type
strongSelf.layoutSize = layoutSize
if backgroundImage != nil {
if let currentBackgroundNode = currentBackgroundNode {
@ -563,12 +596,10 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
var reactionOffset: CGFloat = leftInset - reactionInset + backgroundInsets.left
for i in 0 ..< reactions.count {
let node: StatusReactionNode
if strongSelf.reactionNodes.count > i, strongSelf.reactionNodes[i].value == reactions[i].value {
if strongSelf.reactionNodes.count > i {
node = strongSelf.reactionNodes[i]
node.count = Int(reactions[i].count)
node.previousValue = i == 0 ? nil : reactions[i - 1].value
} else {
node = StatusReactionNode(value: reactions[i].value, count: Int(reactions[i].count), previousValue: i == 0 ? nil : reactions[i - 1].value)
node = StatusReactionNode()
if strongSelf.reactionNodes.count > i {
let previousNode = strongSelf.reactionNodes[i]
if animated {
@ -583,13 +614,15 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
strongSelf.reactionNodes.append(node)
}
}
node.update(type: type, isSelected: reactions[i].isSelected, count: Int(reactions[i].count), theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, animated: false)
if node.supernode == nil {
strongSelf.addSubnode(node)
if animated {
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
node.frame = CGRect(origin: CGPoint(x: reactionOffset, y: backgroundInsets.top + offset - 3.0), size: CGSize(width: reactionSize, height: reactionSize))
node.frame = CGRect(origin: CGPoint(x: reactionOffset, y: backgroundInsets.top + offset + 1.0), size: CGSize(width: reactionSize, height: reactionSize))
reactionOffset += reactionSize + reactionSpacing
}
if !reactions.isEmpty {
@ -598,6 +631,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
for _ in reactions.count ..< strongSelf.reactionNodes.count {
let node = strongSelf.reactionNodes.removeLast()
if animated {
if let previousLayoutSize = previousLayoutSize {
node.frame = node.frame.offsetBy(dx: layoutSize.width - previousLayoutSize.width, dy: 0.0)
}
node.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, removeOnCompletion: false)
node.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
node?.removeFromSupernode()
@ -622,6 +658,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
} else if let reactionCountNode = strongSelf.reactionCountNode {
strongSelf.reactionCountNode = nil
if animated {
if let previousLayoutSize = previousLayoutSize {
reactionCountNode.frame = reactionCountNode.frame.offsetBy(dx: layoutSize.width - previousLayoutSize.width, dy: 0.0)
}
reactionCountNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak reactionCountNode] _ in
reactionCountNode?.removeFromSupernode()
})
@ -630,7 +669,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
}
}
if false, !strongSelf.reactionNodes.isEmpty {
if !strongSelf.reactionNodes.isEmpty {
if strongSelf.reactionButtonNode == nil {
let reactionButtonNode = HighlightTrackingButtonNode()
strongSelf.reactionButtonNode = reactionButtonNode
@ -682,11 +721,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
}
}
func reactionNode(value: String) -> (ASDisplayNode, Int)? {
func reactionNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
for node in self.reactionNodes {
if node.value == value {
return (node, node.count)
}
return (node.emptyImageNode, node.selectedImageNode)
}
return nil
}

View File

@ -119,7 +119,7 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
self.interactiveFileNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
return self.interactiveFileNode.reactionTargetNode(value: value)
}
}

View File

@ -134,7 +134,7 @@ final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNode {
return self.contentNode.transitionNode(media: media)
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
return self.contentNode.reactionTargetNode(value: value)
}
}

View File

@ -47,6 +47,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
private var recognizer: TapLongTapOrDoubleTapGestureRecognizer?
private var currentSwipeAction: ChatControllerInteractionSwipeAction?
override var visibility: ListViewItemNodeVisibility {
didSet {
let wasVisible = oldValue != .none
@ -138,7 +140,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
if strongSelf.selectionNode != nil {
return false
}
return item.controllerInteraction.canSetupReply(item.message)
let action = item.controllerInteraction.canSetupReply(item.message)
strongSelf.currentSwipeAction = action
if case .none = action {
return false
} else {
return true
}
}
return false
}
@ -774,7 +782,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
if translation.x < -45.0, self.swipeToReplyNode == nil, let item = self.item {
self.swipeToReplyFeedback?.impact()
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper))
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
self.swipeToReplyNode = swipeToReplyNode
self.addSubnode(swipeToReplyNode)
animateReplyNodeIn = true
@ -800,7 +808,18 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
let translation = recognizer.translation(in: self.view)
if case .ended = recognizer.state, translation.x < -45.0 {
if let item = self.item {
item.controllerInteraction.setupReply(item.message.id)
if let currentSwipeAction = currentSwipeAction {
switch currentSwipeAction {
case .none:
break
case .reply:
item.controllerInteraction.setupReply(item.message.id)
case .like:
item.controllerInteraction.updateMessageLike(item.message.id, true)
case .unlike:
item.controllerInteraction.updateMessageLike(item.message.id, true)
}
}
}
}
var bounds = self.bounds

View File

@ -958,7 +958,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
self.playerUpdateTimer = nil
}
func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
if !self.dateAndStatusNode.isHidden {
return self.dateAndStatusNode.reactionNode(value: value)
}

View File

@ -137,7 +137,7 @@ final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContentNode {
return self.contentNode.transitionNode(media: media)
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
return self.contentNode.reactionTargetNode(value: value)
}
}

View File

@ -610,7 +610,7 @@ final class ChatMessageAccessibilityData {
}
if isSelected == nil {
var canReply = item.controllerInteraction.canSetupReply(item.message)
var canReply = item.controllerInteraction.canSetupReply(item.message) == .reply
for media in item.content.firstMessage.media {
if let _ = media as? TelegramMediaExpiredContent {
canReply = false
@ -829,7 +829,7 @@ public class ChatMessageItemView: ListViewItemNode {
}
}
func targetReactionNode(value: String) -> (ASDisplayNode, Int)? {
func targetReactionNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
return nil
}
}

View File

@ -472,7 +472,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
}
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
if !self.dateAndStatusNode.isHidden {
return self.dateAndStatusNode.reactionNode(value: value)
}

View File

@ -394,7 +394,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
return false
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
if !self.dateAndStatusNode.isHidden {
return self.dateAndStatusNode.reactionNode(value: value)
}

View File

@ -1735,7 +1735,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
}
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
if !self.statusNode.isHidden {
return self.statusNode.reactionNode(value: value)
}

View File

@ -248,7 +248,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode {
self.statusNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
if !self.statusNode.isHidden {
return self.statusNode.reactionNode(value: value)
}

View File

@ -44,6 +44,8 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
private var currentSwipeToReplyTranslation: CGFloat = 0.0
private var currentSwipeAction: ChatControllerInteractionSwipeAction?
required init() {
self.contextSourceNode = ContextExtractedContentContainingNode()
self.containerNode = ContextControllerSourceNode()
@ -96,6 +98,13 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
self.addSubnode(self.containerNode)
self.contextSourceNode.contentNode.addSubnode(self.imageNode)
self.contextSourceNode.contentNode.addSubnode(self.dateAndStatusNode)
self.dateAndStatusNode.openReactions = { [weak self] in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
item.controllerInteraction.openMessageReactions(item.message.id)
}
}
required init?(coder aDecoder: NSCoder) {
@ -150,7 +159,13 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
if strongSelf.selectionNode != nil {
return false
}
return item.controllerInteraction.canSetupReply(item.message)
let action = item.controllerInteraction.canSetupReply(item.message)
strongSelf.currentSwipeAction = action
if case .none = action {
return false
} else {
return true
}
}
return false
}
@ -789,7 +804,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
if translation.x < -45.0, self.swipeToReplyNode == nil, let item = self.item {
self.swipeToReplyFeedback?.impact()
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper))
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
self.swipeToReplyNode = swipeToReplyNode
self.addSubnode(swipeToReplyNode)
animateReplyNodeIn = true
@ -815,7 +830,18 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
let translation = recognizer.translation(in: self.view)
if case .ended = recognizer.state, translation.x < -45.0 {
if let item = self.item {
item.controllerInteraction.setupReply(item.message.id)
if let currentSwipeAction = currentSwipeAction {
switch currentSwipeAction {
case .none:
break
case .reply:
item.controllerInteraction.setupReply(item.message.id)
case .like:
item.controllerInteraction.updateMessageLike(item.message.id, true)
case .unlike:
item.controllerInteraction.updateMessageLike(item.message.id, true)
}
}
}
}
var bounds = self.bounds

View File

@ -5,9 +5,15 @@ import AsyncDisplayKit
import AppBundle
final class ChatMessageSwipeToReplyNode: ASDisplayNode {
enum Action {
case reply
case like
case unlike
}
private let backgroundNode: ASImageNode
init(fillColor: UIColor, strokeColor: UIColor, foregroundColor: UIColor) {
init(fillColor: UIColor, strokeColor: UIColor, foregroundColor: UIColor, action: ChatMessageSwipeToReplyNode.Action) {
self.backgroundNode = ASImageNode()
self.backgroundNode.isLayerBacked = true
self.backgroundNode.image = generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in
@ -25,15 +31,34 @@ final class ChatMessageSwipeToReplyNode: ASDisplayNode {
context.strokeEllipse(in: CGRect(origin: CGPoint(x: halfLineWidth, y: halfLineWidth), size: CGSize(width: size.width - lineWidth, height: size.width - lineWidth)))
}
if let image = UIImage(bundleImageName: "Chat/Message/ShareIcon") {
let imageRect = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size)
context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: -1.0, y: -1.0)
context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
context.clip(to: imageRect, mask: image.cgImage!)
context.setFillColor(foregroundColor.cgColor)
context.fill(imageRect)
switch action {
case .reply:
if let image = UIImage(bundleImageName: "Chat/Message/ShareIcon") {
let imageRect = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size)
context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: -1.0, y: -1.0)
context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
context.clip(to: imageRect, mask: image.cgImage!)
context.setFillColor(foregroundColor.cgColor)
context.fill(imageRect)
}
case .like, .unlike:
if let image = UIImage(bundleImageName: action == .like ? "Chat/Reactions/SwipeActionHeartFilled" : "Chat/Reactions/SwipeActionHeartBroken") {
let imageRect = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size)
context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
if case .like = action {
context.translateBy(x: 0.0, y: -1.0)
} else {
context.translateBy(x: 0.5, y: -1.0)
}
context.clip(to: imageRect, mask: image.cgImage!)
context.setFillColor(foregroundColor.cgColor)
context.fill(imageRect)
}
}
})
@ -43,3 +68,22 @@ final class ChatMessageSwipeToReplyNode: ASDisplayNode {
self.backgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 33.0, height: 33.0))
}
}
extension ChatMessageSwipeToReplyNode.Action {
init(_ action: ChatControllerInteractionSwipeAction?) {
if let action = action {
switch action {
case .none:
self = .reply
case .reply:
self = .reply
case .like:
self = .like
case .unlike:
self = .unlike
}
} else {
self = .reply
}
}
}

View File

@ -589,7 +589,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
}
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
if !self.statusNode.isHidden {
return self.statusNode.reactionNode(value: value)
}

View File

@ -543,7 +543,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
self.contentNode.updateTouchesAtPoint(point.flatMap { $0.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY) })
}
override func reactionTargetNode(value: String) -> (ASDisplayNode, Int)? {
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
return self.contentNode.reactionTargetNode(value: value)
}
}

View File

@ -400,7 +400,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, openSearch: {
}, setupReply: { _ in
}, canSetupReply: { _ in
return false
return .none
}, navigateToFirstDateMessage: { _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
@ -417,7 +417,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, sendScheduledMessagesNow: { _ in
}, editScheduledMessagesTime: { _ in
}, performTextSelectionAction: { _, _, _ in
}, updateMessageReaction: { _, _ in
}, updateMessageLike: { _, _ in
}, openMessageReactions: { _ in
}, displaySwipeToReplyHint: {
}, dismissReplyMarkupMessage: { _ in

View File

@ -93,13 +93,16 @@ public func fetchCachedResourceRepresentation(account: Account, resource: MediaR
}
} else if let size = resource.size {
return account.postbox.mediaBox.resourceData(resource, size: size, in: 0 ..< min(size, 256 * 1024))
|> mapToSignal { data -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|> mapToSignal { result -> Signal<CachedMediaResourceRepresentationResult, NoError> in
let (data, _) = result
return fetchCachedAlbumArtworkRepresentation(account: account, resource: resource, data: data, representation: representation)
|> `catch` { error -> Signal<CachedMediaResourceRepresentationResult, NoError> in
switch error {
case let .moreDataNeeded(targetSize):
return account.postbox.mediaBox.resourceData(resource, size: size, in: 0 ..< min(size, targetSize))
|> mapToSignal { data -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|> mapToSignal { result ->
Signal<CachedMediaResourceRepresentationResult, NoError> in
let (data, _) = result
return fetchCachedAlbumArtworkRepresentation(account: account, resource: resource, data: data, representation: representation)
|> `catch` { error -> Signal<CachedMediaResourceRepresentationResult, NoError> in
return .complete()

View File

@ -78,7 +78,7 @@ func paneGifSearchForQuery(account: Account, query: String, updateActivity: ((Bo
}
if type == "gif", let thumbnailResource = imageResource, let content = content, let dimensions = content.dimensions {
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: uniqueId ?? 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])])
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: uniqueId ?? 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: [])])
references.append(FileMediaReference.standalone(media: file))
}
case let .internalReference(_, _, _, _, _, _, file, _):

View File

@ -219,8 +219,8 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
imageResource = thumbnail.resource
}
imageDimensions = content?.dimensions?.cgSize
if type == "gif", let thumbnailResource = imageResource, let content = content, let dimensions = content.dimensions {
videoFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])])
if type == "gif", let thumbnailResource = thumbnail?.resource, let content = 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
}
@ -358,7 +358,8 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
layerHolder.layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
layerHolder.layer.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
strongSelf.layer.addSublayer(layerHolder.layer)
let manager = SoftwareVideoLayerFrameManager(account: item.account, fileReference: .standalone(media: videoFile), resource: videoFile.resource, layerHolder: layerHolder)
let manager = SoftwareVideoLayerFrameManager(account: item.account, fileReference: .standalone(media: videoFile), layerHolder: layerHolder)
strongSelf.videoLayer = (thumbnailLayer, manager, layerHolder)
thumbnailLayer.ready = { [weak thumbnailLayer, weak manager] in
if let strongSelf = self, let thumbnailLayer = thumbnailLayer, let manager = manager {
@ -419,7 +420,8 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
case let .Fetching(_, progress):
state = .progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(max(progress, 0.2)), cancelEnabled: false)
case .Remote:
state = .download(statusForegroundColor)
//state = .download(statusForegroundColor)
state = .none
case .Local:
state = .none
}

View File

@ -186,7 +186,7 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect,
}
}
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: Int(finalDuration), size: PixelDimensions(finalDimensions), flags: [.instantRoundVideo])])
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: Int(finalDuration), size: PixelDimensions(finalDimensions), flags: [.instantRoundVideo])])
var message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil)
let scheduleTime: Int32? = scheduleTimestamp > 0 ? scheduleTimestamp : nil

View File

@ -302,7 +302,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
layerHolder.layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
layerHolder.layer.frame = item.frame
self.scrollNode.layer.addSublayer(layerHolder.layer)
let manager = SoftwareVideoLayerFrameManager(account: self.account, fileReference: item.fileReference, resource: item.fileReference.media.resource, layerHolder: layerHolder)
let manager = SoftwareVideoLayerFrameManager(account: self.account, fileReference: item.fileReference, layerHolder: layerHolder)
self.visibleLayers[item.fileReference.media.fileId] = (manager, layerHolder)
self.visibleThumbnailLayers[item.fileReference.media.fileId]?.ready = { [weak self] in
if let strongSelf = self {

View File

@ -102,7 +102,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}, openSearch: {
}, setupReply: { _ in
}, canSetupReply: { _ in
return false
return .none
}, navigateToFirstDateMessage: { _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
@ -116,7 +116,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}, sendScheduledMessagesNow: { _ in
}, editScheduledMessagesTime: { _ in
}, performTextSelectionAction: { _, _, _ in
}, updateMessageReaction: { _, _ in
}, updateMessageLike: { _, _ in
}, openMessageReactions: { _ in
}, displaySwipeToReplyHint: {
}, dismissReplyMarkupMessage: { _ in

View File

@ -195,7 +195,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.containerNode.layer.insertSublayer(sampleBufferLayer.layer, above: self.imageNode.layer)
}
self.videoLayerFrameManager = SoftwareVideoLayerFrameManager(account: self.context.account, fileReference: FileMediaReference.message(message: MessageReference(item.message), media: file), resource: file.resource, layerHolder: sampleBufferLayer)
self.videoLayerFrameManager = SoftwareVideoLayerFrameManager(account: self.context.account, fileReference: FileMediaReference.message(message: MessageReference(item.message), media: file), layerHolder: sampleBufferLayer)
self.videoLayerFrameManager?.start()
}
} else {
@ -563,8 +563,6 @@ private enum ItemsLayout {
return (i, j - 1)
}
}
break
}
return (i, self.frames.count - 1)
}

View File

@ -1580,7 +1580,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}, openSearch: {
}, setupReply: { _ in
}, canSetupReply: { _ in
return false
return .none
}, navigateToFirstDateMessage: { _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
@ -1594,7 +1594,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}, sendScheduledMessagesNow: { _ in
}, editScheduledMessagesTime: { _ in
}, performTextSelectionAction: { _, _, _ in
}, updateMessageReaction: { _, _ in
}, updateMessageLike: { _, _ in
}, openMessageReactions: { _ in
}, displaySwipeToReplyHint: {
}, dismissReplyMarkupMessage: { _ in

View File

@ -407,7 +407,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
self?.activateSearch()
}, setupReply: { _ in
}, canSetupReply: { _ in
return false
return .none
}, navigateToFirstDateMessage: { _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
@ -421,7 +421,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
}, sendScheduledMessagesNow: { _ in
}, editScheduledMessagesTime: { _ in
}, performTextSelectionAction: { _, _, _ in
}, updateMessageReaction: { _, _ in
}, updateMessageLike: { _, _ in
}, openMessageReactions: { _ in
}, displaySwipeToReplyHint: {
}, dismissReplyMarkupMessage: { _ in

View File

@ -1115,7 +1115,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return nil
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
}, canSetupReply: { _ in
return false
return .none
}, navigateToFirstDateMessage: { _ in
}, requestRedeliveryOfFailedMessages: { _ in
}, addContact: { _ in
@ -1129,7 +1129,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}, sendScheduledMessagesNow: { _ in
}, editScheduledMessagesTime: { _ in
}, performTextSelectionAction: { _, _, _ in
}, updateMessageReaction: { _, _ in
}, updateMessageLike: { _, _ in
}, openMessageReactions: { _ in
}, displaySwipeToReplyHint: {
}, dismissReplyMarkupMessage: { _ in

View File

@ -31,7 +31,16 @@ final class SoftwareVideoLayerFrameManager {
private var layerRotationAngleAndAspect: (CGFloat, CGFloat)?
init(account: Account, fileReference: FileMediaReference, resource: MediaResource, layerHolder: SampleBufferLayer) {
init(account: Account, fileReference: FileMediaReference, layerHolder: SampleBufferLayer) {
var resource = fileReference.media.resource
for attribute in fileReference.media.attributes {
if case .Video = attribute {
if let thumbnail = fileReference.media.videoThumbnails.first {
resource = thumbnail.resource
}
}
}
nextWorker += 1
self.account = account
self.resource = resource

View File

@ -728,7 +728,7 @@ final class WatchAudioHandler: WatchRequestHandler {
replyMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMid)
}
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: data.count, attributes: [.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])), replyToMessageId: replyMessageId, localGroupingKey: nil)]).start()
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: data.count, attributes: [.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])), replyToMessageId: replyMessageId, localGroupingKey: nil)]).start()
}
})
} else {

View File

@ -39,7 +39,7 @@ struct WebSearchGalleryEntry: Equatable {
switch self.result {
case let .externalReference(_, _, type, _, _, _, content, thumbnail, _):
if let content = content, type == "gif", let thumbnailResource = 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)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
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, _):