diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 4b5ebfd66e..542ebd76ca 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -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?() }) diff --git a/submodules/LegacyDataImport/Sources/LegacyChatImport.swift b/submodules/LegacyDataImport/Sources/LegacyChatImport.swift index aa9a853019..b644a17248 100644 --- a/submodules/LegacyDataImport/Sources/LegacyChatImport.swift +++ b/submodules/LegacyDataImport/Sources/LegacyChatImport.swift @@ -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 { diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift index 24be4f12b1..8eee272e26 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift @@ -264,7 +264,7 @@ public func legacyEnqueueGifMessage(account: Account, data: Data) -> Signal 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)) diff --git a/submodules/LegacyUI/Sources/LegacyComponentsStickers.swift b/submodules/LegacyUI/Sources/LegacyComponentsStickers.swift index 15adb76b5f..429d7ae8a8 100644 --- a/submodules/LegacyUI/Sources/LegacyComponentsStickers.swift +++ b/submodules/LegacyUI/Sources/LegacyComponentsStickers.swift @@ -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)) diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift index a9e6e2ecaa..1297f241ba 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift @@ -91,7 +91,7 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa } if streamable { - let data: Signal + 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() diff --git a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift index f5e29c6572..ef33d6d613 100644 --- a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift +++ b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift @@ -8,10 +8,10 @@ import FFMpegBinding private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer?, bufferSize: Int32) -> Int32 { let context = Unmanaged.fromOpaque(userData!).takeUnretainedValue() - let data: Signal + 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 = 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 { diff --git a/submodules/Postbox/Sources/MediaBox.swift b/submodules/Postbox/Sources/MediaBox.swift index d2a33e2a8e..6efe9a4bb3 100644 --- a/submodules/Postbox/Sources/MediaBox.swift +++ b/submodules/Postbox/Sources/MediaBox.swift @@ -563,7 +563,7 @@ public final class MediaBox { } } - public func resourceData(_ resource: MediaResource, size: Int, in range: Range, mode: ResourceDataRangeMode = .complete) -> Signal { + public func resourceData(_ resource: MediaResource, size: Int, in range: Range, 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)) } } } diff --git a/submodules/Postbox/Sources/MediaBoxFile.swift b/submodules/Postbox/Sources/MediaBoxFile.swift index 1808a46492..7aa58b5042 100644 --- a/submodules/Postbox/Sources/MediaBoxFile.swift +++ b/submodules/Postbox/Sources/MediaBoxFile.swift @@ -884,7 +884,7 @@ final class MediaBoxFileContext { func data(range: Range, 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) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionAttachedNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionAttachedNode.swift index 4f99ad70bc..105a011ad0 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionAttachedNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionAttachedNode.swift @@ -1,4 +1,4 @@ -import Foundation +/*import Foundation import AsyncDisplayKit import AnimatedStickerNode import Display @@ -321,3 +321,4 @@ final class ReactionAttachedNode: ASDisplayNode { return nil } } +*/ diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index d7a15edd46..fcbe5bcdd9 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -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) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionGestureItem.swift b/submodules/ReactionSelectionNode/Sources/ReactionGestureItem.swift index 65ae1c94ef..bfc2c858c9 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionGestureItem.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionGestureItem.swift @@ -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 } diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift index 3243132a1e..ac5fe31631 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift @@ -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 } } + diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSelectionParentNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionSelectionParentNode.swift index e02bf48f5f..acecea37f4 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSelectionParentNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSelectionParentNode.swift @@ -83,3 +83,4 @@ public final class ReactionSelectionParentNode: ASDisplayNode { return nil } } + diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift b/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift index 025a52ce2a..1226d74b03 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift @@ -190,3 +190,4 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { self.state = .cancelled } } + diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index d181e22e61..18984dbed5 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -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)) diff --git a/submodules/SettingsUI/Sources/DebugController.swift b/submodules/SettingsUI/Sources/DebugController.swift index 377270fefc..6665ef5c7c 100644 --- a/submodules/SettingsUI/Sources/DebugController.swift +++ b/submodules/SettingsUI/Sources/DebugController.swift @@ -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() diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 0128aab537..309deaa769 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -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)) diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift index f85e96e5ce..2b7af5c72c 100644 --- a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift +++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift @@ -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() diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 83b912c220..d693773115 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -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) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift b/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift index 5b74af426a..ceb0836926 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift @@ -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] = [ diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index ea95fe1622..30ef3e814f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -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) diff --git a/submodules/SyncCore/Sources/TelegramMediaFile.swift b/submodules/SyncCore/Sources/TelegramMediaFile.swift index 2e27573ab8..e1d73f8cbb 100644 --- a/submodules/SyncCore/Sources/TelegramMediaFile.swift +++ b/submodules/SyncCore/Sources/TelegramMediaFile.swift @@ -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) } } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index bc0471dc1c..d579615703 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -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: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index b1eef5d412..6536337dcc 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -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 diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 84235c1d17..295decb640 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -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) { + 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) { + 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) { + 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) { diff --git a/submodules/TelegramCallsUI/Sources/CallRatingController.swift b/submodules/TelegramCallsUI/Sources/CallRatingController.swift index 65cb3a712b..263b5f7884 100644 --- a/submodules/TelegramCallsUI/Sources/CallRatingController.swift +++ b/submodules/TelegramCallsUI/Sources/CallRatingController.swift @@ -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]) diff --git a/submodules/TelegramCore/Sources/AccountManager.swift b/submodules/TelegramCore/Sources/AccountManager.swift index 17a9d71ed9..a60057dea9 100644 --- a/submodules/TelegramCore/Sources/AccountManager.swift +++ b/submodules/TelegramCore/Sources/AccountManager.swift @@ -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 }() diff --git a/submodules/TelegramCore/Sources/EnqueueMessage.swift b/submodules/TelegramCore/Sources/EnqueueMessage.swift index 0e972618e6..0ffa4789d9 100644 --- a/submodules/TelegramCore/Sources/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/EnqueueMessage.swift @@ -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 { diff --git a/submodules/TelegramCore/Sources/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/ManagedSecretChatOutgoingOperations.swift index f9c2d07935..43b879ccce 100644 --- a/submodules/TelegramCore/Sources/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/ManagedSecretChatOutgoingOperations.swift @@ -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] } diff --git a/submodules/TelegramCore/Sources/MessageReactionList.swift b/submodules/TelegramCore/Sources/MessageReactionList.swift index 3b7d205bf7..72b5cb3acc 100644 --- a/submodules/TelegramCore/Sources/MessageReactionList.swift +++ b/submodules/TelegramCore/Sources/MessageReactionList.swift @@ -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 in + |> mapToSignal { inputPeer -> Signal 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 - }))*/ + })) } } diff --git a/submodules/TelegramCore/Sources/MessageReactions.swift b/submodules/TelegramCore/Sources/MessageReactions.swift index 2ce880a75b..319be71ffa 100644 --- a/submodules/TelegramCore/Sources/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/MessageReactions.swift @@ -33,9 +33,7 @@ private enum RequestUpdateMessageReactionError { } private func requestUpdateMessageReaction(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId) -> Signal { - 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 { diff --git a/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift b/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift index 9f7d61856a..72bf73d224 100644 --- a/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift +++ b/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift @@ -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) diff --git a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift index 6a5b779752..6d6c753f8f 100644 --- a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift @@ -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))) diff --git a/submodules/TelegramCore/Sources/ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/ReactionsMessageAttribute.swift index 5bdfdf62c3..f82b9d7d75 100644 --- a/submodules/TelegramCore/Sources/ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ReactionsMessageAttribute.swift @@ -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 }) } } -}*/ +} diff --git a/submodules/TelegramCore/Sources/Serialization.swift b/submodules/TelegramCore/Sources/Serialization.swift index 55bda07a9d..eee8616a22 100644 --- a/submodules/TelegramCore/Sources/Serialization.swift +++ b/submodules/TelegramCore/Sources/Serialization.swift @@ -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! { diff --git a/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift b/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift index 03837a49a5..80e856b16d 100644 --- a/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift +++ b/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift @@ -195,7 +195,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin |> mapToSignal { result -> Signal 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: diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift index fff2ce294f..9d546091a9 100644 --- a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift @@ -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:)))) diff --git a/submodules/TelegramCore/Sources/TelegramMediaFile.swift b/submodules/TelegramCore/Sources/TelegramMediaFile.swift index bb3a084b92..5bd6461ad2 100644 --- a/submodules/TelegramCore/Sources/TelegramMediaFile.swift +++ b/submodules/TelegramCore/Sources/TelegramMediaFile.swift @@ -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 } diff --git a/submodules/TelegramCore/Sources/UpdateMessageService.swift b/submodules/TelegramCore/Sources/UpdateMessageService.swift index 34cd793682..fe537679f7 100644 --- a/submodules/TelegramCore/Sources/UpdateMessageService.swift +++ b/submodules/TelegramCore/Sources/UpdateMessageService.swift @@ -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 { diff --git a/submodules/TelegramCore/Sources/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/UpdatesApiUtils.swift index 33a510ac65..73b285a8ec 100644 --- a/submodules/TelegramCore/Sources/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/UpdatesApiUtils.swift @@ -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 { diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift index b258a03f73..a1f72c12b0 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift @@ -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 } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index ec68cf0997..d6e87f5af4 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -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) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift index 073d2c498f..2cf976c8f3 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift @@ -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) + } + }) + } } diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/Contents.json new file mode 100644 index 0000000000..6e965652df --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenL.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenL.imageset/Contents.json new file mode 100644 index 0000000000..569e6b32f7 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenL.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_unlikelongtap_l.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenL.imageset/ic_unlikelongtap_l.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenL.imageset/ic_unlikelongtap_l.pdf new file mode 100644 index 0000000000..9d15eb4a9b Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenL.imageset/ic_unlikelongtap_l.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenR.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenR.imageset/Contents.json new file mode 100644 index 0000000000..a67b531575 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenR.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_unlikelongtap_r.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenR.imageset/ic_unlikelongtap_r.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenR.imageset/ic_unlikelongtap_r.pdf new file mode 100644 index 0000000000..f36bdabc8b Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartBrokenR.imageset/ic_unlikelongtap_r.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/Contents.json new file mode 100644 index 0000000000..98e64a416b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_likelongtap.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/ic_likelongtap.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/ic_likelongtap.pdf new file mode 100644 index 0000000000..0da5794c59 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/ContextHeartFilled.imageset/ic_likelongtap.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/Contents.json new file mode 100644 index 0000000000..06277d6d27 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_likemsg.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/ic_likemsg.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/ic_likemsg.pdf new file mode 100644 index 0000000000..7cda0dcdf3 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartEmpty.imageset/ic_likemsg.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/Contents.json new file mode 100644 index 0000000000..7f8c6f6542 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_likedmsg.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/ic_likedmsg.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/ic_likedmsg.pdf new file mode 100644 index 0000000000..09754a444b Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/MessageHeartFilled.imageset/ic_likedmsg.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartBroken.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartBroken.imageset/Contents.json new file mode 100644 index 0000000000..0c00d676e4 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartBroken.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_unlikeswipe.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartBroken.imageset/ic_unlikeswipe.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartBroken.imageset/ic_unlikeswipe.pdf new file mode 100644 index 0000000000..ccf0e3d34c Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartBroken.imageset/ic_unlikeswipe.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartFilled.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartFilled.imageset/Contents.json new file mode 100644 index 0000000000..419414d99d --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartFilled.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_likeswipe.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartFilled.imageset/ic_likeswipe.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartFilled.imageset/ic_likeswipe.pdf new file mode 100644 index 0000000000..9497d98e20 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Reactions/SwipeActionHeartFilled.imageset/ic_likeswipe.pdf differ diff --git a/submodules/TelegramUI/Sources/ChatContextResultPeekContentNode.swift b/submodules/TelegramUI/Sources/ChatContextResultPeekContentNode.swift index f7ee6abaa1..146ab4620d 100644 --- a/submodules/TelegramUI/Sources/ChatContextResultPeekContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatContextResultPeekContentNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 1397ba2796..db0f269229 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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)]) } } diff --git a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift index 40e1a3946c..46e1b9c4b9 100644 --- a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift @@ -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() - 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 diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 2f783aba24..7886645106 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift index cee7ea0914..75ca4111d1 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift index 0269e3d6c9..ebb771f944 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift @@ -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 } } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index fbd9cd2df0..079011cba9 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift index b2c5038812..6b6fd15e7b 100644 --- a/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift @@ -217,7 +217,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { } } - override func reactionTargetNode(value: String) -> (ASImageNode, Int)? { + override func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? { return nil } } diff --git a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift index c351ad160c..a740794c22 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift index 114cebebbd..a973bd9e7d 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift index 701b5ce552..df47dae88f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift @@ -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) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift index 75fa0bed5c..de894e9555 100644 --- a/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift @@ -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) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index 968f3d1d31..0e59bd940c 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index 664a47f472..09f27fc685 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageInvoiceBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageInvoiceBubbleContentNode.swift index 1b7f36074c..022693b7c3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInvoiceBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInvoiceBubbleContentNode.swift @@ -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) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index b3726605c5..e2f907f602 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -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 } } diff --git a/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift index 178685165d..192883b28f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift index fec48009c9..577556c523 100644 --- a/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift index 2079ecad12..bb6149c63f 100644 --- a/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift index 2a410ec9ed..c7ec00047c 100644 --- a/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index e53c1a95ca..25ff12d5ec 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageSwipeToReplyNode.swift b/submodules/TelegramUI/Sources/ChatMessageSwipeToReplyNode.swift index e1fec4bd6a..4d9041526b 100644 --- a/submodules/TelegramUI/Sources/ChatMessageSwipeToReplyNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageSwipeToReplyNode.swift @@ -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 + } + } +} diff --git a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift index 5df3808ac2..210b5d42a6 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift index 981c37021d..4ad043b1a8 100644 --- a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -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) } } diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index e02fb53016..77de0a793e 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift b/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift index f8d2d788be..71a0c3e920 100644 --- a/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift +++ b/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift @@ -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 in + |> mapToSignal { result -> Signal in + let (data, _) = result return fetchCachedAlbumArtworkRepresentation(account: account, resource: resource, data: data, representation: representation) |> `catch` { error -> Signal in switch error { case let .moreDataNeeded(targetSize): return account.postbox.mediaBox.resourceData(resource, size: size, in: 0 ..< min(size, targetSize)) - |> mapToSignal { data -> Signal in + |> mapToSignal { result -> + Signal in + let (data, _) = result return fetchCachedAlbumArtworkRepresentation(account: account, resource: resource, data: data, representation: representation) |> `catch` { error -> Signal in return .complete() diff --git a/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift b/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift index 606f2431d9..cd5135ebb5 100644 --- a/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift +++ b/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift @@ -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, _): diff --git a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift index 0828dbe412..0f579383a5 100644 --- a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift b/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift index 6c7f420b3c..f9a2d36bb5 100644 --- a/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift +++ b/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/MultiplexedVideoNode.swift b/submodules/TelegramUI/Sources/MultiplexedVideoNode.swift index 573da8b10f..ab7981295f 100644 --- a/submodules/TelegramUI/Sources/MultiplexedVideoNode.swift +++ b/submodules/TelegramUI/Sources/MultiplexedVideoNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift index 5cec17efe8..a3446ce1f2 100644 --- a/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift index 431a987757..e07261291c 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index e4c267cc2d..5aa68dc9f1 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift b/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift index 459cb753f2..9f99916953 100644 --- a/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift +++ b/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 798051e427..ffb38834ad 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift b/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift index a4eda40927..c384df91d9 100644 --- a/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift +++ b/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift @@ -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 diff --git a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift index 0609691bde..45dfb9ddf7 100644 --- a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift +++ b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift @@ -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 { diff --git a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift index 44d6053249..2c68680fda 100644 --- a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift @@ -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, _):