mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-01-29 23:40:06 +00:00
Inline video playback fixes
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user