From a33d5114310aff4bad98fb18be696aadc72eea95 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 18 Feb 2019 03:25:57 +0400 Subject: [PATCH] Inline video playback fixes --- .../ChatMessageAttachedContentNode.swift | 8 +-- .../ChatMessageInstantVideoItemNode.swift | 4 ++ ...atMessageInteractiveInstantVideoNode.swift | 12 ++++ .../ChatMessageInteractiveMediaBadge.swift | 56 +++++++++++-------- .../ChatMessageInteractiveMediaNode.swift | 44 +++++++-------- TelegramUI/ChatMessageItemView.swift | 2 +- .../ChatMessageMediaBubbleContentNode.swift | 4 +- TelegramUI/GridMessageItem.swift | 10 ++-- TelegramUI/UniversalVideoGalleryItem.swift | 13 ++++- 9 files changed, 96 insertions(+), 57 deletions(-) diff --git a/TelegramUI/ChatMessageAttachedContentNode.swift b/TelegramUI/ChatMessageAttachedContentNode.swift index b46eac957a..3446c180fe 100644 --- a/TelegramUI/ChatMessageAttachedContentNode.swift +++ b/TelegramUI/ChatMessageAttachedContentNode.swift @@ -334,7 +334,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { var updateInlineImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? var textCutout = TextNodeCutout() var initialWidth: CGFloat = CGFloat.greatestFiniteMagnitude - var refineContentImageLayout: ((CGSize, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> ChatMessageInteractiveMediaNode)))? + var refineContentImageLayout: ((CGSize, Bool, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> ChatMessageInteractiveMediaNode)))? var refineContentFileLayout: ((CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageInteractiveFileNode)))? var contentInstantVideoSizeAndApply: (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> ChatMessageInteractiveInstantVideoNode)? @@ -493,10 +493,10 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { var skipStandardStatus = false if let count = webpageGalleryMediaCount { - additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.bubble.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.bubble.mediaDateAndStatusTextColor, shape: .corners(2.0), text: NSAttributedString(string: "1 \(presentationData.strings.Common_of) \(count)")) + additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.bubble.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.bubble.mediaDateAndStatusTextColor, text: NSAttributedString(string: "1 \(presentationData.strings.Common_of) \(count)")) skipStandardStatus = imageMode } else if let mediaBadge = mediaBadge { - additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.bubble.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.bubble.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: mediaBadge)) + additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.bubble.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.bubble.mediaDateAndStatusTextColor, text: NSAttributedString(string: mediaBadge)) } if !skipStandardStatus { @@ -596,7 +596,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { var finalizeContentImageLayout: ((CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> ChatMessageInteractiveMediaNode))? if let refineContentImageLayout = refineContentImageLayout { - let (refinedWidth, finalizeImageLayout) = refineContentImageLayout(textConstrainedSize, automaticPlayback, ImageCorners(radius: 4.0)) + let (refinedWidth, finalizeImageLayout) = refineContentImageLayout(textConstrainedSize, automaticPlayback, true, ImageCorners(radius: 4.0)) finalizeContentImageLayout = finalizeImageLayout boundingSize.width = max(boundingSize.width, refinedWidth) diff --git a/TelegramUI/ChatMessageInstantVideoItemNode.swift b/TelegramUI/ChatMessageInstantVideoItemNode.swift index 0746cb6c9d..40922d669b 100644 --- a/TelegramUI/ChatMessageInstantVideoItemNode.swift +++ b/TelegramUI/ChatMessageInstantVideoItemNode.swift @@ -694,4 +694,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } + + override func playMediaWithSound() -> (() -> Void)? { + return self.interactiveVideoNode.playMediaWithSound() + } } diff --git a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift index f198b69a07..55a9821dee 100644 --- a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift +++ b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift @@ -681,5 +681,17 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { }) } } + + func playMediaWithSound() -> (() -> Void)? { + if case .visible(true) = self.visibility, let item = self.item { + return { + if !self.infoBackgroundNode.alpha.isZero { + let _ = item.controllerInteraction.openMessage(item.message, .default) + } + } + } else { + return nil + } + } } diff --git a/TelegramUI/ChatMessageInteractiveMediaBadge.swift b/TelegramUI/ChatMessageInteractiveMediaBadge.swift index 67ce89c91a..df8fcd3bb5 100644 --- a/TelegramUI/ChatMessageInteractiveMediaBadge.swift +++ b/TelegramUI/ChatMessageInteractiveMediaBadge.swift @@ -2,11 +2,6 @@ import Foundation import Display import AsyncDisplayKit -enum ChatMessageInteractiveMediaBadgeShape: Equatable { - case round - case corners(CGFloat) -} - enum ChatMessageInteractiveMediaDownloadState: Equatable { case remote case fetching(progress: Float?) @@ -15,13 +10,13 @@ enum ChatMessageInteractiveMediaDownloadState: Equatable { } enum ChatMessageInteractiveMediaBadgeContent: Equatable { - case text(inset: CGFloat, backgroundColor: UIColor, foregroundColor: UIColor, shape: ChatMessageInteractiveMediaBadgeShape, text: NSAttributedString) + case text(inset: CGFloat, backgroundColor: UIColor, foregroundColor: UIColor, text: NSAttributedString) case mediaDownload(backgroundColor: UIColor, foregroundColor: UIColor, duration: String, size: String?, muted: Bool, active: Bool) static func ==(lhs: ChatMessageInteractiveMediaBadgeContent, rhs: ChatMessageInteractiveMediaBadgeContent) -> Bool { switch lhs { - case let .text(lhsInset, lhsBackgroundColor, lhsForegroundColor, lhsShape, lhsText): - if case let .text(rhsInset, rhsBackgroundColor, rhsForegroundColor, rhsShape, rhsText) = rhs, lhsInset.isEqual(to: rhsInset), lhsBackgroundColor.isEqual(rhsBackgroundColor), lhsForegroundColor.isEqual(rhsForegroundColor), lhsShape == rhsShape, lhsText.isEqual(to: rhsText) { + case let .text(lhsInset, lhsBackgroundColor, lhsForegroundColor, lhsText): + if case let .text(rhsInset, rhsBackgroundColor, rhsForegroundColor, rhsText) = rhs, lhsInset.isEqual(to: rhsInset), lhsBackgroundColor.isEqual(rhsBackgroundColor), lhsForegroundColor.isEqual(rhsForegroundColor), lhsText.isEqual(to: rhsText) { return true } else { return false @@ -50,6 +45,7 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { private let backgroundNode: ASImageNode private let durationNode: ASTextNode private var sizeNode: ASTextNode? + private var measureNode: ASTextNode private var iconNode: ASImageNode? private var mediaDownloadStatusNode: RadialStatusNode? @@ -57,6 +53,7 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { self.backgroundNode = ASImageNode() self.backgroundNode.clipsToBounds = true self.durationNode = ASTextNode() + self.measureNode = ASTextNode() super.init() @@ -76,6 +73,14 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { } } + private let digitsSet = CharacterSet(charactersIn: "0123456789") + private func widthForString(_ string: String) -> CGFloat { + let convertedString = string.components(separatedBy: digitsSet).joined(separator: "8") + self.measureNode.attributedText = NSMutableAttributedString(string: convertedString, attributes: [.font: font]) + let size = self.measureNode.measure(CGSize(width: 240.0, height: 160.0)) + return size.width + } + func update(theme: PresentationTheme, content: ChatMessageInteractiveMediaBadgeContent?, mediaDownloadState: ChatMessageInteractiveMediaDownloadState?, alignment: NSTextAlignment = .left, animated: Bool) { var transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate @@ -87,12 +92,14 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { if let content = self.content { var previousActive: Bool? - if let previousContent = previousContent, case let .mediaDownload(_, _, _, _, _, active) = previousContent { + var previousMuted: Bool? + if let previousContent = previousContent, case let .mediaDownload(_, _, _, _, muted, active) = previousContent { previousActive = active + previousMuted = muted } switch content { - case let .text(inset, backgroundColor, foregroundColor, shape, text): + case let .text(inset, backgroundColor, foregroundColor, text): transition = .immediate if self.backgroundNodeColor != backgroundColor { @@ -108,7 +115,7 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { self.durationNode.attributedText = convertedText let durationSize = self.durationNode.measure(CGSize(width: 160.0, height: 160.0)) self.durationNode.frame = CGRect(x: 7.0 + inset, y: 2.0, width: durationSize.width, height: durationSize.height) - contentSize = CGSize(width: durationSize.width + 14.0 + inset, height: 18.0) + contentSize = CGSize(width: widthForString(text.string) + 14.0 + inset, height: 18.0) if let iconNode = self.iconNode { transition.updateTransformScale(node: iconNode, scale: 0.001) @@ -123,13 +130,17 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { if previousActive == nil { previousActive = active } + if previousMuted == nil { + previousMuted = muted + } - transition = previousActive != active ? transition : .immediate + let textTransition = previousActive != active ? transition : .immediate + transition = (previousMuted != muted || previousActive != active) ? transition : .immediate let durationString = NSMutableAttributedString(string: duration, attributes: [.font: font, .foregroundColor: foregroundColor]) self.durationNode.attributedText = durationString - var sizeSize: CGSize = CGSize() + var sizeWidth: CGFloat = 0.0 if let size = size { let sizeNode: ASTextNode if let current = self.sizeNode { @@ -141,24 +152,24 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { } let sizeString = NSMutableAttributedString(string: size, attributes: [.font: font, .foregroundColor: foregroundColor]) + sizeWidth = widthForString(size) sizeNode.attributedText = sizeString - sizeSize = sizeNode.measure(CGSize(width: 160.0, height: 160.0)) + let sizeSize = sizeNode.measure(CGSize(width: 160.0, height: 160.0)) - transition.updateFrame(node: sizeNode, frame: CGRect(x: active ? 42.0 : 7.0, y: active ? 20.0 : 2.0, width: sizeSize.width, height: sizeSize.height)) + textTransition.updateFrame(node: sizeNode, frame: CGRect(x: active ? 42.0 : 7.0, y: active ? 19.0 : 2.0, width: sizeSize.width, height: sizeSize.height)) transition.updateAlpha(node: sizeNode, alpha: 1.0) } else if let sizeNode = self.sizeNode { let sizeSize = sizeNode.frame.size - transition.updateFrame(node: sizeNode, frame: CGRect(x: active ? 42.0 : 7.0, y: active ? 20.0 : 2.0, width: sizeSize.width, height: sizeSize.height)) + textTransition.updateFrame(node: sizeNode, frame: CGRect(x: active ? 42.0 : 7.0, y: active ? 19.0 : 2.0, width: sizeSize.width, height: sizeSize.height)) transition.updateAlpha(node: sizeNode, alpha: 0.0) } - var durationSize = self.durationNode.measure(CGSize(width: 160.0, height: 160.0)) - durationSize.width = max(25.0, durationSize.width) + let durationSize = self.durationNode.measure(CGSize(width: 160.0, height: 160.0)) if let statusNode = self.mediaDownloadStatusNode { transition.updateAlpha(node: statusNode, alpha: active ? 1.0 : 0.0) } - transition.updateFrame(node: self.durationNode, frame: CGRect(x: active ? 42.0 : 7.0, y: active ? 7.0 : 2.0, width: durationSize.width, height: durationSize.height)) + textTransition.updateFrame(node: self.durationNode, frame: CGRect(x: active ? 42.0 : 7.0, y: active ? 6.0 : 2.0, width: durationSize.width, height: durationSize.height)) let iconNode: ASImageNode if let current = self.iconNode { @@ -175,7 +186,8 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/InlineVideoMute"), color: foregroundColor) } - transition.updatePosition(node: iconNode, position: CGPoint(x: (active ? 42.0 : 7.0) + floor(durationSize.width) + 4.0 + 7.0, y: (active ? 9.0 : 4.0) + 5.0)) + let durationWidth = widthForString(duration) + transition.updatePosition(node: iconNode, position: CGPoint(x: (active ? 42.0 : 7.0) + durationWidth + 4.0 + 7.0, y: (active ? 8.0 : 4.0) + 5.0)) if muted { transition.updateAlpha(node: iconNode, alpha: 1.0) @@ -185,7 +197,7 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { transition.updateTransformScale(node: iconNode, scale: 0.001) } - var contentWidth: CGFloat = max(sizeSize.width, durationSize.width + (muted ? 17.0 : 0.0)) + 14.0 + var contentWidth: CGFloat = max(sizeWidth, durationWidth + (muted ? 17.0 : 0.0)) + 14.0 if active { contentWidth += 36.0 } @@ -217,7 +229,7 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { if alignment == .right { originX -= contentSize.width } - var originY: CGFloat = 6.0 + var originY: CGFloat = 5.0 switch mediaDownloadState { case .remote: if let image = PresentationResourcesChat.chatBubbleFileCloudFetchMediaIcon(theme) { diff --git a/TelegramUI/ChatMessageInteractiveMediaNode.swift b/TelegramUI/ChatMessageInteractiveMediaNode.swift index b80bdefa53..17bc9baa13 100644 --- a/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -46,6 +46,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { private var media: Media? private var themeAndStrings: (PresentationTheme, PresentationStrings)? private var sizeCalculation: InteractiveMediaNodeSizeCalculation? + private var wideLayout: Bool? private var automaticDownload: InteractiveMediaNodeAutodownloadMode? var automaticPlayback: Bool? @@ -188,7 +189,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } } - func asyncLayout() -> (_ context: AccountContext, _ theme: PresentationTheme, _ strings: PresentationStrings, _ message: Message, _ media: Media, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: AutomaticMediaDownloadPeerType, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode) -> (CGSize, CGFloat, (CGSize, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> Void))) { + func asyncLayout() -> (_ context: AccountContext, _ theme: PresentationTheme, _ strings: PresentationStrings, _ message: Message, _ media: Media, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: AutomaticMediaDownloadPeerType, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode) -> (CGSize, CGFloat, (CGSize, Bool, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> Void))) { let currentMessage = self.message let currentMedia = self.media let imageLayout = self.imageNode.asyncLayout() @@ -290,7 +291,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { let _ = PresentationResourcesChat.chatBubbleSecretMediaIcon(theme) } - return (nativeSize, maxWidth, { constrainedSize, automaticPlayback, corners in + return (nativeSize, maxWidth, { constrainedSize, automaticPlayback, wideLayout, corners in var resultWidth: CGFloat isInlinePlayableVideo = isInlinePlayableVideo && automaticPlayback @@ -542,6 +543,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { strongSelf.context = context strongSelf.message = message strongSelf.media = media + strongSelf.wideLayout = wideLayout strongSelf.themeAndStrings = (theme, strings) strongSelf.sizeCalculation = sizeCalculation strongSelf.automaticPlayback = automaticPlayback @@ -568,14 +570,15 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { var updatedVideoNodeReadySignal: Signal? var updatedPlayerStatusSignal: Signal? - if let replaceVideoNode = replaceVideoNode { + if let currentReplaceVideoNode = replaceVideoNode { + replaceVideoNode = nil if let videoNode = strongSelf.videoNode { videoNode.canAttachContent = false videoNode.removeFromSupernode() strongSelf.videoNode = nil } - if replaceVideoNode, let updatedVideoFile = updateVideoFile { + if currentReplaceVideoNode, let updatedVideoFile = updateVideoFile { let decoration = ChatBubbleVideoDecoration(corners: arguments.corners, nativeSize: nativeSize, contentMode: contentMode, backgroundColor: arguments.emptyColor ?? .black) strongSelf.videoNodeDecoration = decoration let mediaManager = context.sharedContext.mediaManager @@ -752,7 +755,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } private func updateFetchStatus() { - guard let (theme, strings) = self.themeAndStrings, let sizeCalculation = self.sizeCalculation, let message = self.message, var automaticPlayback = self.automaticPlayback else { + guard let (theme, strings) = self.themeAndStrings, let sizeCalculation = self.sizeCalculation, let message = self.message, var automaticPlayback = self.automaticPlayback, let wideLayout = self.wideLayout else { return } @@ -813,12 +816,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } } - let radialStatusSize: CGFloat - if case .unconstrained = sizeCalculation { - radialStatusSize = 32.0 - } else { - radialStatusSize = 50.0 - } + let radialStatusSize: CGFloat = wideLayout ? 50.0 : 32.0 if progressRequired { if self.statusNode == nil { @@ -858,7 +856,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } string.append(NSAttributedString(string: title)) } - badgeContent = .text(inset: 0.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: string) + badgeContent = .text(inset: 0.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: string) } var animated = true @@ -891,7 +889,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { if let actualFetchStatus = self.actualFetchStatus, automaticPlayback { fetchStatus = actualFetchStatus } - + switch fetchStatus { case let .Fetching(_, progress): let adjustedProgress = max(progress, 0.027) @@ -906,7 +904,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } if let file = self.media as? TelegramMediaFile, (!file.isAnimated || message.flags.contains(.Unsent)) { - if case .constrained = sizeCalculation { + if wideLayout { if let size = file.size { if let duration = file.duration, !message.flags.contains(.Unsent) { let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition) @@ -951,7 +949,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } } else { let progressString = String(format: "%d%%", Int(progress * 100.0)) - badgeContent = .text(inset: message.flags.contains(.Unsent) ? 0.0 : 12.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: progressString)) + badgeContent = .text(inset: message.flags.contains(.Unsent) ? 0.0 : 12.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: progressString)) mediaDownloadState = automaticPlayback ? .none : .compactFetching(progress: progress) } @@ -1011,7 +1009,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { state = .download(bubbleTheme.mediaOverlayControlForegroundColor) if let file = self.media as? TelegramMediaFile, !file.isAnimated { let durationString = stringForDuration(playerDuration > 0 ? playerDuration : (file.duration ?? 0), position: playerPosition) - if case .constrained = sizeCalculation { + if wideLayout { if isMediaStreamable(message: message, media: file) { state = automaticPlayback ? .none : .play(bubbleTheme.mediaOverlayControlForegroundColor) badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: dataSizeString(file.size ?? 0), muted: muted, active: true) @@ -1023,11 +1021,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } else { if isMediaStreamable(message: message, media: file) { state = automaticPlayback ? .none : .play(bubbleTheme.mediaOverlayControlForegroundColor) - badgeContent = .text(inset: 12.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: durationString)) + badgeContent = .text(inset: 12.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: durationString)) mediaDownloadState = .compactRemote } else { state = automaticPlayback ? .none : state - badgeContent = .text(inset: 0.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: durationString)) + badgeContent = .text(inset: 0.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: durationString)) } } } else if let webpage = webpage, let automaticDownload = self.automaticDownload, case .full = automaticDownload, case let .Loaded(content) = webpage.content, content.type != "telegram_background" { @@ -1045,7 +1043,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { remainingTime = Int32(timeout) } - badgeContent = .text(inset: 0.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: strings.MessageTimer_ShortSeconds(Int32(remainingTime)))) + badgeContent = .text(inset: 0.0, backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: strings.MessageTimer_ShortSeconds(Int32(remainingTime)))) } if let statusNode = self.statusNode { @@ -1099,12 +1097,12 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } } - static func asyncLayout(_ node: ChatMessageInteractiveMediaNode?) -> (_ context: AccountContext, _ theme: PresentationTheme, _ strings: PresentationStrings, _ message: Message, _ media: Media, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: AutomaticMediaDownloadPeerType, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode) -> (CGSize, CGFloat, (CGSize, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> ChatMessageInteractiveMediaNode))) { + static func asyncLayout(_ node: ChatMessageInteractiveMediaNode?) -> (_ context: AccountContext, _ theme: PresentationTheme, _ strings: PresentationStrings, _ message: Message, _ media: Media, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: AutomaticMediaDownloadPeerType, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode) -> (CGSize, CGFloat, (CGSize, Bool, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> ChatMessageInteractiveMediaNode))) { let currentAsyncLayout = node?.asyncLayout() return { context, theme, strings, message, media, automaticDownload, peerType, sizeCalculation, layoutConstants, contentMode in var imageNode: ChatMessageInteractiveMediaNode - var imageLayout: (_ context: AccountContext, _ theme: PresentationTheme, _ strings: PresentationStrings, _ message: Message, _ media: Media, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: AutomaticMediaDownloadPeerType, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode) -> (CGSize, CGFloat, (CGSize, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> Void))) + var imageLayout: (_ context: AccountContext, _ theme: PresentationTheme, _ strings: PresentationStrings, _ message: Message, _ media: Media, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: AutomaticMediaDownloadPeerType, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode) -> (CGSize, CGFloat, (CGSize, Bool, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition, Bool) -> Void))) if let node = node, let currentAsyncLayout = currentAsyncLayout { imageNode = node @@ -1116,8 +1114,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { let (unboundSize, initialWidth, continueLayout) = imageLayout(context, theme, strings, message, media, automaticDownload, peerType, sizeCalculation, layoutConstants, contentMode) - return (unboundSize, initialWidth, { constrainedSize, automaticPlayback, corners in - let (finalWidth, finalLayout) = continueLayout(constrainedSize, automaticPlayback, corners) + return (unboundSize, initialWidth, { constrainedSize, automaticPlayback, wideLayout, corners in + let (finalWidth, finalLayout) = continueLayout(constrainedSize, automaticPlayback, wideLayout, corners) return (finalWidth, { boundingWidth in let (finalSize, apply) = finalLayout(boundingWidth) diff --git a/TelegramUI/ChatMessageItemView.swift b/TelegramUI/ChatMessageItemView.swift index c9c8589fca..f1d50f3b5e 100644 --- a/TelegramUI/ChatMessageItemView.swift +++ b/TelegramUI/ChatMessageItemView.swift @@ -78,7 +78,7 @@ struct ChatMessageItemLayoutConstants { self.bubble = ChatMessageItemBubbleLayoutConstants(edgeInset: 4.0, defaultSpacing: 2.0 + UIScreenPixel, mergedSpacing: 1.0, maximumWidthFill: ChatMessageItemWidthFill(compactInset: 36.0, compactWidthBoundary: 500.0, freeMaximumFillFactor: 0.85), minimumSize: CGSize(width: 40.0, height: 35.0), contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 0.0), borderInset: UIScreenPixel) self.text = ChatMessageItemTextLayoutConstants(bubbleInsets: UIEdgeInsets(top: 6.0 + UIScreenPixel, left: 12.0, bottom: 6.0 - UIScreenPixel, right: 12.0)) - self.image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 1.0 + UIScreenPixel, left: 1.0 + UIScreenPixel, bottom: 1.0 + UIScreenPixel, right: 1.0 + UIScreenPixel), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 17.0, mergedCornerRadius: 5.0, contentMergedCornerRadius: 5.0, maxDimensions: CGSize(width: 300.0, height: 300.0), minDimensions: CGSize(width: 160.0, height: 74.0)) + self.image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 1.0 + UIScreenPixel, left: 1.0 + UIScreenPixel, bottom: 1.0 + UIScreenPixel, right: 1.0 + UIScreenPixel), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 17.0, mergedCornerRadius: 5.0, contentMergedCornerRadius: 5.0, maxDimensions: CGSize(width: 300.0, height: 300.0), minDimensions: CGSize(width: 170.0, height: 74.0)) self.video = ChatMessageItemVideoLayoutConstants(maxHorizontalHeight: 250.0, maxVerticalHeight: 300.0) self.file = ChatMessageItemFileLayoutConstants(bubbleInsets: UIEdgeInsets(top: 15.0, left: 9.0, bottom: 15.0, right: 12.0)) self.instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 212.0, height: 212.0)) diff --git a/TelegramUI/ChatMessageMediaBubbleContentNode.swift b/TelegramUI/ChatMessageMediaBubbleContentNode.swift index cb060375da..296b788b5e 100644 --- a/TelegramUI/ChatMessageMediaBubbleContentNode.swift +++ b/TelegramUI/ChatMessageMediaBubbleContentNode.swift @@ -124,7 +124,9 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 7.0, hidesBackground: .emptyWallpaper, forceFullCorners: forceFullCorners, forceAlignment: .none) return (contentProperties, unboundSize, initialWidth + bubbleInsets.left + bubbleInsets.right, { constrainedSize, position in + var wideLayout = true if case let .mosaic(_, wide) = position { + wideLayout = wide automaticPlayback = automaticPlayback && wide } @@ -137,7 +139,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { let imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: updatedPosition, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius) - let (refinedWidth, finishLayout) = refineLayout(CGSize(width: constrainedSize.width - bubbleInsets.left - bubbleInsets.right, height: constrainedSize.height), automaticPlayback, imageCorners) + let (refinedWidth, finishLayout) = refineLayout(CGSize(width: constrainedSize.width - bubbleInsets.left - bubbleInsets.right, height: constrainedSize.height), automaticPlayback, wideLayout, imageCorners) return (refinedWidth + bubbleInsets.left + bubbleInsets.right, { boundingWidth in let (imageSize, imageApply) = finishLayout(boundingWidth - bubbleInsets.left - bubbleInsets.right) diff --git a/TelegramUI/GridMessageItem.swift b/TelegramUI/GridMessageItem.swift index 818fdc9e1d..5de0027c66 100644 --- a/TelegramUI/GridMessageItem.swift +++ b/TelegramUI/GridMessageItem.swift @@ -255,16 +255,16 @@ final class GridMessageItemNode: GridItemNode { switch status { case let .Fetching(_, progress): let progressString = String(format: "%d%%", Int(progress * 100.0)) - badgeContent = .text(inset: 12.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, shape: .round, text: NSAttributedString(string: progressString)) + badgeContent = .text(inset: 12.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: progressString)) mediaDownloadState = .compactFetching(progress: progress) case .Local: - badgeContent = .text(inset: 0.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, shape: .round, text: NSAttributedString(string: durationString)) + badgeContent = .text(inset: 0.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: durationString)) case .Remote: - badgeContent = .text(inset: 12.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, shape: .round, text: NSAttributedString(string: durationString)) + badgeContent = .text(inset: 12.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: durationString)) mediaDownloadState = .compactRemote } } else { - badgeContent = .text(inset: 0.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, shape: .round, text: NSAttributedString(string: durationString)) + badgeContent = .text(inset: 0.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: durationString)) } strongSelf.mediaBadgeNode.update(theme: item.theme, content: badgeContent, mediaDownloadState: mediaDownloadState, alignment: .right, animated: false) @@ -423,6 +423,8 @@ final class GridMessageItemNode: GridItemNode { } else { self.progressPressed() } + } else { + let _ = controllerInteraction.openMessage(message, .default) } case .longTap: controllerInteraction.openMessageContextMenu(message, false, self, self.bounds) diff --git a/TelegramUI/UniversalVideoGalleryItem.swift b/TelegramUI/UniversalVideoGalleryItem.swift index 53d03d3a37..e699b352da 100644 --- a/TelegramUI/UniversalVideoGalleryItem.swift +++ b/TelegramUI/UniversalVideoGalleryItem.swift @@ -158,6 +158,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { private var _isVisible = false private var initiallyActivated = false private var initiallyAppeared = false + private var ignoreNextVisibility = false + private var hideStatusNodeUntilCentrality = false private var validLayout: (ContainerViewLayout, CGFloat)? private var didPause = false private var isPaused = true @@ -445,7 +447,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { strongSelf.fetchStatus = fetchStatus if !item.hideControls { - strongSelf.statusButtonNode.isHidden = !initialBuffering && (strongSelf.didPause || !isPaused) && !fetching + strongSelf.statusButtonNode.isHidden = strongSelf.hideStatusNodeUntilCentrality || (!initialBuffering && (strongSelf.didPause || !isPaused) && !fetching) } if isAnimated || disablePlayerControls { @@ -540,6 +542,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if let videoNode = self.videoNode { if isCentral { + self.hideStatusNodeUntilCentrality = false if self.shouldAutoplayOnCentrality() { self.initiallyActivated = true videoNode.playOnceWithSound(playAndRecord: false, seekToStart: .none, actionAtEnd: .stop) @@ -557,7 +560,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if self._isVisible != isVisible { self._isVisible = isVisible - if let item = self.item, let videoNode = self.videoNode, self.initiallyAppeared || !item.fromPlayingVideo { + if let item = self.item, let videoNode = self.videoNode, !self.ignoreNextVisibility && (self.initiallyAppeared || !item.fromPlayingVideo) { videoNode.canAttachContent = isVisible if !item.returningFromOverlay { if isVisible { @@ -569,9 +572,14 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } self.updateDisplayPlaceholder(!videoNode.ownsContentNode) if self.shouldAutoplayOnCentrality() { + self.hideStatusNodeUntilCentrality = true self.statusButtonNode.isHidden = true } } + + if !isVisible { + self.ignoreNextVisibility = false + } } else if !isVisible { self.initiallyAppeared = true } @@ -590,6 +598,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } else { videoNode.playOnceWithSound(playAndRecord: false, actionAtEnd: .stop) } + self.ignoreNextVisibility = true } }