mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Likes
This commit is contained in:
parent
1aa3a8b017
commit
150a176cbc
@ -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?()
|
||||
})
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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))
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Foundation
|
||||
/*import Foundation
|
||||
import AsyncDisplayKit
|
||||
import AnimatedStickerNode
|
||||
import Display
|
||||
@ -321,3 +321,4 @@ final class ReactionAttachedNode: ASDisplayNode {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,3 +83,4 @@ public final class ReactionSelectionParentNode: ASDisplayNode {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,3 +190,4 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer {
|
||||
self.state = .cancelled
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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] = [
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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>) {
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
}()
|
||||
|
@ -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 {
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
}))*/
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)))
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
@ -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! {
|
||||
|
@ -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:
|
||||
|
@ -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:))))
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenL.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenL.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_unlikelongtap_l.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenR.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenR.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_unlikelongtap_r.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_likelongtap.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/ic_likelongtap.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/ic_likelongtap.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_likemsg.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/ic_likemsg.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/ic_likemsg.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_likedmsg.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/ic_likedmsg.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/ic_likedmsg.pdf
vendored
Normal file
Binary file not shown.
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_unlikeswipe.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_likeswipe.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
@ -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 {
|
||||
|
@ -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)])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -217,7 +217,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func reactionTargetNode(value: String) -> (ASImageNode, Int)? {
|
||||
override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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, _):
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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, _):
|
||||
|
Loading…
x
Reference in New Issue
Block a user