Inline video playback fixes

This commit is contained in:
Ilya Laktyushin
2019-02-18 03:25:57 +04:00
parent 8ddc0b670a
commit a33d511431
9 changed files with 96 additions and 57 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<Void, NoError>?
var updatedPlayerStatusSignal: Signal<MediaPlayerStatus?, NoError>?
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)

View File

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

View File

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

View File

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

View File

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