From a834ed1df9d72bc33116abbe33a08fc46c9d45ff Mon Sep 17 00:00:00 2001 From: Andrei Rychkov Date: Mon, 17 Jun 2019 23:54:36 +0300 Subject: [PATCH 1/5] Fix markdown text conversion --- TelegramUI/ChatTextInputAttributes.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TelegramUI/ChatTextInputAttributes.swift b/TelegramUI/ChatTextInputAttributes.swift index da8b39e4..090d3e62 100644 --- a/TelegramUI/ChatTextInputAttributes.swift +++ b/TelegramUI/ChatTextInputAttributes.swift @@ -646,7 +646,7 @@ func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttributedStri while let match = regex.firstMatch(in: string as String, range: NSMakeRange(0, string.length)) { let matchIndex = stringOffset + match.range.location - result.append(text.attributedSubstring(from: NSMakeRange(0, match.range.location))) + result.append(text.attributedSubstring(from: NSMakeRange(text.length - string.length, match.range.location))) var pre = match.range(at: 3) if pre.location != NSNotFound { From 04032a51116230be8ee69c3d31443500e6ec78c1 Mon Sep 17 00:00:00 2001 From: Andrei Rychkov Date: Fri, 21 Jun 2019 11:51:05 +0300 Subject: [PATCH 2/5] Implement continuous video seek --- .../ChatMessageInteractiveFileNode.swift | 4 +- .../ChatVideoGalleryItemScrubberView.swift | 6 +- TelegramUI/InstantPageAudioNode.swift | 13 +- TelegramUI/MediaPlayerScrubbingNode.swift | 124 +++++++++++------- TelegramUI/OverlayPlayerControlsNode.swift | 4 +- TelegramUI/UniversalVideoGalleryItem.swift | 18 ++- 6 files changed, 105 insertions(+), 64 deletions(-) diff --git a/TelegramUI/ChatMessageInteractiveFileNode.swift b/TelegramUI/ChatMessageInteractiveFileNode.swift index 2fa6ffb4..74ab5575 100644 --- a/TelegramUI/ChatMessageInteractiveFileNode.swift +++ b/TelegramUI/ChatMessageInteractiveFileNode.swift @@ -534,7 +534,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { if strongSelf.waveformScrubbingNode == nil { let waveformScrubbingNode = MediaPlayerScrubbingNode(content: .custom(backgroundNode: strongSelf.waveformNode, foregroundContentNode: strongSelf.waveformForegroundNode)) waveformScrubbingNode.hitTestSlop = UIEdgeInsetsMake(-10.0, 0.0, -10.0, 0.0) - waveformScrubbingNode.seek = { timestamp in + waveformScrubbingNode.seek = { timestamp, isContinuous in + guard !isContinuous else { return } + if let strongSelf = self, let context = strongSelf.context, let message = strongSelf.message, let type = peerMessageMediaPlayerType(message) { context.sharedContext.mediaManager.playlistControl(.seek(timestamp), type: type) } diff --git a/TelegramUI/ChatVideoGalleryItemScrubberView.swift b/TelegramUI/ChatVideoGalleryItemScrubberView.swift index 1eb4806f..83851552 100644 --- a/TelegramUI/ChatVideoGalleryItemScrubberView.swift +++ b/TelegramUI/ChatVideoGalleryItemScrubberView.swift @@ -40,7 +40,7 @@ final class ChatVideoGalleryItemScrubberView: UIView { } } - var seek: (Double) -> Void = { _ in } + var seek: (_ timestamp: Double, _ isContinuous: Bool) -> Void = { _, _ in } override init(frame: CGRect) { self.scrubberNode = MediaPlayerScrubbingNode(content: .standard(lineHeight: 5.0, lineCap: .round, scrubberHandle: .circle, backgroundColor: UIColor(white: 1.0, alpha: 0.42), foregroundColor: .white)) @@ -57,8 +57,8 @@ final class ChatVideoGalleryItemScrubberView: UIView { super.init(frame: frame) - self.scrubberNode.seek = { [weak self] timestamp in - self?.seek(timestamp) + self.scrubberNode.seek = { [weak self] timestamp, isContinuous in + self?.seek(timestamp, isContinuous) } self.scrubberNode.playerStatusUpdated = { [weak self] status in diff --git a/TelegramUI/InstantPageAudioNode.swift b/TelegramUI/InstantPageAudioNode.swift index 006efb2f..dbbf274c 100644 --- a/TelegramUI/InstantPageAudioNode.swift +++ b/TelegramUI/InstantPageAudioNode.swift @@ -127,12 +127,13 @@ final class InstantPageAudioNode: ASDisplayNode, InstantPageNode { } } - self.scrubbingNode.seek = { [weak self] timestamp in - if let strongSelf = self { - if let _ = strongSelf.playbackState { - strongSelf.context.sharedContext.mediaManager.playlistControl(.seek(timestamp), type: strongSelf.playlistType) - } - } + self.scrubbingNode.seek = { [weak self] timestamp, isContinuous in + guard !isContinuous, + let strongSelf = self, + let _ = strongSelf.playbackState + else { return } + + strongSelf.context.sharedContext.mediaManager.playlistControl(.seek(timestamp), type: strongSelf.playlistType) } /*if let applicationContext = account.applicationContext as? TelegramApplicationContext, let (playlistId, itemId) = instantPageAudioPlaylistAndItemIds(webpage: webpage, media: self.media) { diff --git a/TelegramUI/MediaPlayerScrubbingNode.swift b/TelegramUI/MediaPlayerScrubbingNode.swift index abd427a1..6eb929c3 100644 --- a/TelegramUI/MediaPlayerScrubbingNode.swift +++ b/TelegramUI/MediaPlayerScrubbingNode.swift @@ -187,14 +187,17 @@ final class MediaPlayerScrubbingNode: ASDisplayNode { var playbackStatusUpdated: ((MediaPlayerPlaybackStatus?) -> Void)? var playerStatusUpdated: ((MediaPlayerStatus?) -> Void)? - var seek: ((Double) -> Void)? - + var seek: ((_ timestamp: Double, _ isContinuous: Bool) -> Void)? + private let _scrubbingTimestamp = Promise(nil) var scrubbingTimestamp: Signal { return self._scrubbingTimestamp.get() } var ignoreSeekId: Int? + + private let seekTimeout: Double + var trackingTimestampTimer: SwiftSignalKit.Timer? var enableScrubbing: Bool = true { didSet { @@ -327,8 +330,9 @@ final class MediaPlayerScrubbingNode: ASDisplayNode { } } - init(content: MediaPlayerScrubbingNodeContent) { + init(content: MediaPlayerScrubbingNodeContent, seekTimeout: Double = 0.05) { self.contentNodes = MediaPlayerScrubbingNode.contentNodesFromContent(content, enableScrubbing: self.enableScrubbing) + self.seekTimeout = seekTimeout super.init() @@ -362,6 +366,22 @@ final class MediaPlayerScrubbingNode: ASDisplayNode { subnode.removeFromSupernode() } } + + let startSeeking = { [weak self] in + self?.trackingTimestampTimer?.invalidate() + if let currentTimestampValue = self?.scrubbingTimestampValue, + let strongSelf = self { + strongSelf.trackingTimestampTimer = SwiftSignalKit.Timer(timeout: strongSelf.seekTimeout, repeat: false, completion: { [weak self] in + self?.seek?(currentTimestampValue, true) + }, queue: Queue.mainQueue()) + strongSelf.trackingTimestampTimer?.start() + } + } + + let endSeeking = { [weak self] (scrubbingTimestampValue: Double) in + self?.trackingTimestampTimer?.invalidate() + self?.seek?(scrubbingTimestampValue, false) + } switch self.contentNodes { case let .standard(node): @@ -373,43 +393,45 @@ final class MediaPlayerScrubbingNode: ASDisplayNode { if let handleNodeContainer = node.handleNodeContainer { self.addSubnode(handleNodeContainer) handleNodeContainer.beginScrubbing = { [weak self] in - if let strongSelf = self { - if let statusValue = strongSelf.statusValue, Double(0.0).isLess(than: statusValue.duration) { - strongSelf.scrubbingBeginTimestamp = statusValue.timestamp - strongSelf.scrubbingTimestampValue = statusValue.timestamp - strongSelf._scrubbingTimestamp.set(.single(strongSelf.scrubbingTimestampValue)) - strongSelf.updateProgressAnimations() - } + guard let strongSelf = self else { return } + + if let statusValue = strongSelf.statusValue, Double(0.0).isLess(than: statusValue.duration) { + strongSelf.scrubbingBeginTimestamp = statusValue.timestamp + strongSelf.scrubbingTimestampValue = statusValue.timestamp + strongSelf._scrubbingTimestamp.set(.single(strongSelf.scrubbingTimestampValue)) + strongSelf.updateProgressAnimations() + startSeeking() } } handleNodeContainer.updateScrubbing = { [weak self] addedFraction in - if let strongSelf = self { - if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) { - strongSelf.scrubbingTimestampValue = max(0.0, min(statusValue.duration, scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction))) - strongSelf._scrubbingTimestamp.set(.single(strongSelf.scrubbingTimestampValue)) - strongSelf.updateProgressAnimations() - } + guard let strongSelf = self else { return } + + if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) { + strongSelf.scrubbingTimestampValue = max(0.0, min(statusValue.duration, scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction))) + strongSelf._scrubbingTimestamp.set(.single(strongSelf.scrubbingTimestampValue)) + strongSelf.updateProgressAnimations() + startSeeking() } } handleNodeContainer.endScrubbing = { [weak self] apply in - if let strongSelf = self { - strongSelf.scrubbingBeginTimestamp = nil - let scrubbingTimestampValue = strongSelf.scrubbingTimestampValue - strongSelf.scrubbingTimestampValue = nil - strongSelf._scrubbingTimestamp.set(.single(nil)) - if let scrubbingTimestampValue = scrubbingTimestampValue, apply { - if let statusValue = strongSelf.statusValue { - switch statusValue.status { - case .buffering: - break - default: - strongSelf.ignoreSeekId = statusValue.seekId - } + guard let strongSelf = self else { return } + + strongSelf.scrubbingBeginTimestamp = nil + let scrubbingTimestampValue = strongSelf.scrubbingTimestampValue + strongSelf.scrubbingTimestampValue = nil + strongSelf._scrubbingTimestamp.set(.single(nil)) + if let scrubbingTimestampValue = scrubbingTimestampValue, apply { + if let statusValue = strongSelf.statusValue { + switch statusValue.status { + case .buffering: + break + default: + strongSelf.ignoreSeekId = statusValue.seekId } - strongSelf.seek?(scrubbingTimestampValue) } - strongSelf.updateProgressAnimations() + endSeeking(scrubbingTimestampValue) } + strongSelf.updateProgressAnimations() } } @@ -429,32 +451,34 @@ final class MediaPlayerScrubbingNode: ASDisplayNode { if let handleNodeContainer = node.handleNodeContainer { self.addSubnode(handleNodeContainer) handleNodeContainer.beginScrubbing = { [weak self] in - if let strongSelf = self { - if let statusValue = strongSelf.statusValue, Double(0.0).isLess(than: statusValue.duration) { - strongSelf.scrubbingBeginTimestamp = statusValue.timestamp - strongSelf.scrubbingTimestampValue = statusValue.timestamp - strongSelf.updateProgressAnimations() - } + guard let strongSelf = self else { return } + + if let statusValue = strongSelf.statusValue, Double(0.0).isLess(than: statusValue.duration) { + strongSelf.scrubbingBeginTimestamp = statusValue.timestamp + strongSelf.scrubbingTimestampValue = statusValue.timestamp + strongSelf.updateProgressAnimations() + startSeeking() } } handleNodeContainer.updateScrubbing = { [weak self] addedFraction in - if let strongSelf = self { - if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) { - strongSelf.scrubbingTimestampValue = scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction) - strongSelf.updateProgressAnimations() - } + guard let strongSelf = self else { return } + + if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) { + strongSelf.scrubbingTimestampValue = scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction) + strongSelf.updateProgressAnimations() + startSeeking() } } handleNodeContainer.endScrubbing = { [weak self] apply in - if let strongSelf = self { - strongSelf.scrubbingBeginTimestamp = nil - let scrubbingTimestampValue = strongSelf.scrubbingTimestampValue - strongSelf.scrubbingTimestampValue = nil - if let scrubbingTimestampValue = scrubbingTimestampValue, apply { - strongSelf.seek?(scrubbingTimestampValue) - } - strongSelf.updateProgressAnimations() + guard let strongSelf = self else { return } + + strongSelf.scrubbingBeginTimestamp = nil + let scrubbingTimestampValue = strongSelf.scrubbingTimestampValue + strongSelf.scrubbingTimestampValue = nil + if let scrubbingTimestampValue = scrubbingTimestampValue, apply { + endSeeking(scrubbingTimestampValue) } + strongSelf.updateProgressAnimations() } } diff --git a/TelegramUI/OverlayPlayerControlsNode.swift b/TelegramUI/OverlayPlayerControlsNode.swift index 2d618e1f..dd0f5b78 100644 --- a/TelegramUI/OverlayPlayerControlsNode.swift +++ b/TelegramUI/OverlayPlayerControlsNode.swift @@ -284,8 +284,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode { } }) - self.scrubberNode.seek = { [weak self] value in - self?.control?(.seek(value)) + self.scrubberNode.seek = { [weak self] timestamp, isContinuous in + self?.control?(.seek(timestamp)) } self.collapseNode.addTarget(self, action: #selector(self.collapsePressed), forControlEvents: .touchUpInside) diff --git a/TelegramUI/UniversalVideoGalleryItem.swift b/TelegramUI/UniversalVideoGalleryItem.swift index 0222c86a..6e4eeaa3 100644 --- a/TelegramUI/UniversalVideoGalleryItem.swift +++ b/TelegramUI/UniversalVideoGalleryItem.swift @@ -163,6 +163,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { private var validLayout: (ContainerViewLayout, CGFloat)? private var didPause = false private var isPaused = true + private var isPausedDuringSeek = false private var dismissOnOrientationChange = false private var keepSoundOnDismiss = false @@ -197,8 +198,21 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { super.init() - self.scrubberView.seek = { [weak self] timecode in - self?.videoNode?.seek(timecode) + self.scrubberView.seek = { [weak self] timecode, isContinuous in + guard let strongSelf = self else { return } + + if isContinuous { + if !strongSelf.isPaused { + strongSelf.isPausedDuringSeek = true + strongSelf.videoNode?.pause() + } + } else if strongSelf.isPausedDuringSeek { + strongSelf.isPausedDuringSeek = false + if strongSelf.isPaused { + strongSelf.videoNode?.play() + } + } + strongSelf.videoNode?.seek(timecode) } self.statusButtonNode.addSubnode(self.statusNode) From f8f9b82d5fc2db756ba5731ee73f31a19f371263 Mon Sep 17 00:00:00 2001 From: Andrei Rychkov Date: Fri, 21 Jun 2019 18:14:01 +0300 Subject: [PATCH 3/5] Extend video scrubber hit test --- .../ChatVideoGalleryItemScrubberView.swift | 9 +++++ TelegramUI/MediaPlayerScrubbingNode.swift | 37 +++++++------------ 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/TelegramUI/ChatVideoGalleryItemScrubberView.swift b/TelegramUI/ChatVideoGalleryItemScrubberView.swift index 83851552..56f65d9b 100644 --- a/TelegramUI/ChatVideoGalleryItemScrubberView.swift +++ b/TelegramUI/ChatVideoGalleryItemScrubberView.swift @@ -163,4 +163,13 @@ final class ChatVideoGalleryItemScrubberView: UIView { self.scrubberNode.frame = CGRect(origin: CGPoint(x: scrubberInset, y: 6.0), size: CGSize(width: size.width - leftInset - rightInset - scrubberInset * 2.0, height: scrubberHeight)) } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + var hitTestRect = self.bounds + let minHeightDiff = 44.0 - hitTestRect.height + if (minHeightDiff > 0) { + hitTestRect = bounds.insetBy(dx: 0, dy: -minHeightDiff / 2.0) + } + return hitTestRect.contains(point) + } } diff --git a/TelegramUI/MediaPlayerScrubbingNode.swift b/TelegramUI/MediaPlayerScrubbingNode.swift index 6eb929c3..9a30dcf1 100644 --- a/TelegramUI/MediaPlayerScrubbingNode.swift +++ b/TelegramUI/MediaPlayerScrubbingNode.swift @@ -715,30 +715,19 @@ final class MediaPlayerScrubbingNode: ASDisplayNode { } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - var hitBounds = self.bounds - let hitTestSlop = self.hitTestSlop - hitBounds.origin.x += hitTestSlop.left - hitBounds.origin.y += hitTestSlop.top - hitBounds.size.width += -hitTestSlop.left - hitTestSlop.right - hitBounds.size.height += -hitTestSlop.top - hitTestSlop.bottom - - if hitBounds.contains(point) { - switch self.contentNodes { - case let .standard(node): - if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled { - return handleNodeContainer.view - } else { - return nil - } - case let .custom(node): - if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled { - return handleNodeContainer.view - } else { - return nil - } - } - } else { - return nil + switch self.contentNodes { + case let .standard(node): + if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled { + return handleNodeContainer.view + } else { + return nil + } + case let .custom(node): + if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled { + return handleNodeContainer.view + } else { + return nil + } } } } From fff6ff12622103f572cad00298283705aa4006ca Mon Sep 17 00:00:00 2001 From: Andrei Rychkov Date: Sat, 22 Jun 2019 23:20:27 +0300 Subject: [PATCH 4/5] Fix item options node animations --- TelegramUI/ItemListRevealOptionsNode.swift | 40 +++++++++++++++------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/TelegramUI/ItemListRevealOptionsNode.swift b/TelegramUI/ItemListRevealOptionsNode.swift index cc742b41..62181082 100644 --- a/TelegramUI/ItemListRevealOptionsNode.swift +++ b/TelegramUI/ItemListRevealOptionsNode.swift @@ -140,7 +140,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode { self.animationNode?.reset() } - func updateLayout(isFirst: Bool, isLeft: Bool, baseSize: CGSize, alignment: ItemListRevealOptionAlignment, isExpanded: Bool, extendedWidth: CGFloat, sideInset: CGFloat, transition: ContainedViewLayoutTransition, additive: Bool, revealFactor: CGFloat) { + func updateLayout(isFirst: Bool, isLeft: Bool, baseSize: CGSize, alignment: ItemListRevealOptionAlignment, isExpanded: Bool, extendedWidth: CGFloat, sideInset: CGFloat, transition: ContainedViewLayoutTransition, additive: Bool, revealFactor: CGFloat, animateIconMovement: Bool) { self.highlightNode.frame = CGRect(origin: CGPoint(), size: baseSize) var animateAdditive = false @@ -163,7 +163,9 @@ private final class ItemListRevealOptionNode: ASDisplayNode { } else { deltaX = -(previousFrame.width - backgroundFrame.width) } - transition.animatePositionAdditive(node: self.backgroundNode, offset: CGPoint(x: deltaX, y: 0.0)) + if !animateIconMovement { + transition.animatePositionAdditive(node: self.backgroundNode, offset: CGPoint(x: deltaX, y: 0.0)) + } } else { deltaX = 0.0 transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) @@ -185,20 +187,22 @@ private final class ItemListRevealOptionNode: ASDisplayNode { let titleIconSpacing: CGFloat = 11.0 let iconFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - imageSize.width + sideInset) / 2.0), y: contentRect.midY - imageSize.height / 2.0 + iconOffset), size: imageSize) if animateAdditive { + let iconOffsetX = animateIconMovement ? animationNode.frame.minX - iconFrame.minX : deltaX animationNode.frame = iconFrame - transition.animatePositionAdditive(node: animationNode, offset: CGPoint(x: deltaX, y: 0.0)) + transition.animatePositionAdditive(node: animationNode, offset: CGPoint(x: iconOffsetX, y: 0.0)) } else { transition.updateFrame(node: animationNode, frame: iconFrame) } let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - titleSize.width + sideInset) / 2.0), y: contentRect.midY + titleIconSpacing), size: titleSize) if animateAdditive { + let titleOffsetX = animateIconMovement ? self.titleNode.frame.minX - titleFrame.minX : deltaX self.titleNode.frame = titleFrame - transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: deltaX, y: 0.0)) + transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: titleOffsetX, y: 0.0)) } else { transition.updateFrame(node: self.titleNode, frame: titleFrame) } - + if (abs(revealFactor) >= 0.4) { animationNode.play() } else if abs(revealFactor) < CGFloat.ulpOfOne && !transition.isAnimated { @@ -209,24 +213,27 @@ private final class ItemListRevealOptionNode: ASDisplayNode { let titleIconSpacing: CGFloat = 11.0 let iconFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - imageSize.width + sideInset) / 2.0), y: contentRect.midY - imageSize.height / 2.0 + iconOffset), size: imageSize) if animateAdditive { + let iconOffsetX = animateIconMovement ? iconNode.frame.minX - iconFrame.minX : deltaX iconNode.frame = iconFrame - transition.animatePositionAdditive(node: iconNode, offset: CGPoint(x: deltaX, y: 0.0)) + transition.animatePositionAdditive(node: iconNode, offset: CGPoint(x: iconOffsetX, y: 0.0)) } else { transition.updateFrame(node: iconNode, frame: iconFrame) } let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - titleSize.width + sideInset) / 2.0), y: contentRect.midY + titleIconSpacing), size: titleSize) if animateAdditive { + let titleOffsetX = animateIconMovement ? self.titleNode.frame.minX - titleFrame.minX : deltaX self.titleNode.frame = titleFrame - transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: deltaX, y: 0.0)) + transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: titleOffsetX, y: 0.0)) } else { transition.updateFrame(node: self.titleNode, frame: titleFrame) } } else { let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - titleSize.width + sideInset) / 2.0), y: contentRect.minY + floor((baseSize.height - titleSize.height) / 2.0)), size: titleSize) if animateAdditive { + let titleOffsetX = animateIconMovement ? self.titleNode.frame.minX - titleFrame.minX : deltaX self.titleNode.frame = titleFrame - transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: deltaX, y: 0.0)) + transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: titleOffsetX, y: 0.0)) } else { transition.updateFrame(node: self.titleNode, frame: titleFrame) } @@ -355,7 +362,6 @@ final class ItemListRevealOptionsNode: ASDisplayNode { while i >= 0 && i < self.optionNodes.count { let node = self.optionNodes[i] let nodeWidth = i == (self.optionNodes.count - 1) ? lastNodeWidth : basicNodeWidth - let defaultAlignment: ItemListRevealOptionAlignment = self.isLeft ? .right : .left var nodeTransition = transition var isExpanded = false if (self.isLeft && i == 0) || (!self.isLeft && i == self.optionNodes.count - 1) { @@ -364,7 +370,7 @@ final class ItemListRevealOptionsNode: ASDisplayNode { } } if let _ = node.alignment, node.isExpanded != isExpanded { - nodeTransition = transition.isAnimated ? transition : .animated(duration: 0.2, curve: .spring) + nodeTransition = transition.isAnimated ? transition : .animated(duration: 0.2, curve: .easeInOut) if !transition.isAnimated { self.tapticAction() } @@ -393,9 +399,19 @@ final class ItemListRevealOptionsNode: ASDisplayNode { transition.updateFrame(node: node, frame: CGRect(origin: CGPoint(x: nodeLeftOffset, y: 0.0), size: CGSize(width: extendedWidth, height: size.height)), completion: { _ in completionCount -= 1 intermediateCompletion() - }) - node.updateLayout(isFirst: (self.isLeft && i == 0) || (!self.isLeft && i == self.optionNodes.count - 1), isLeft: self.isLeft, baseSize: CGSize(width: nodeWidth, height: size.height), alignment: defaultAlignment, isExpanded: isExpanded, extendedWidth: extendedWidth, sideInset: sideInset, transition: nodeTransition, additive: !transition.isAnimated, revealFactor: revealFactor) + var nodeAlignment: ItemListRevealOptionAlignment + if (self.optionNodes.count > 1) { + nodeAlignment = self.isLeft ? .right : .left + } else { + if (self.isLeft) { + nodeAlignment = isExpanded ? .right : .left + } else { + nodeAlignment = isExpanded ? .left : .right + } + } + let animateIconMovement = self.optionNodes.count == 1 + node.updateLayout(isFirst: (self.isLeft && i == 0) || (!self.isLeft && i == self.optionNodes.count - 1), isLeft: self.isLeft, baseSize: CGSize(width: nodeWidth, height: size.height), alignment: nodeAlignment, isExpanded: isExpanded, extendedWidth: extendedWidth, sideInset: sideInset, transition: nodeTransition, additive: !transition.isAnimated, revealFactor: revealFactor, animateIconMovement: animateIconMovement) if self.isLeft { i -= 1 From fc8f1e4c80320769d06c439b8d416100b8bd5b80 Mon Sep 17 00:00:00 2001 From: Andrei Rychkov Date: Sun, 23 Jun 2019 00:49:41 +0300 Subject: [PATCH 5/5] Make chat document gallery node scrollable --- TelegramUI/ChatDocumentGalleryItem.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TelegramUI/ChatDocumentGalleryItem.swift b/TelegramUI/ChatDocumentGalleryItem.swift index a146d564..bea8f7af 100644 --- a/TelegramUI/ChatDocumentGalleryItem.swift +++ b/TelegramUI/ChatDocumentGalleryItem.swift @@ -82,7 +82,7 @@ private final class ChatDocumentURLProtocol: URLProtocol { } } -class ChatDocumentGalleryItemNode: GalleryItemNode, WKNavigationDelegate { +class ChatDocumentGalleryItemNode: ZoomableContentGalleryItemNode, WKNavigationDelegate { fileprivate let _title = Promise() private let statusNodeContainer: HighlightableButtonNode @@ -127,9 +127,9 @@ class ChatDocumentGalleryItemNode: GalleryItemNode, WKNavigationDelegate { self.statusNode.isHidden = true super.init() - - self.view.addSubview(self.webView) - + + self.view.insertSubview(self.webView, belowSubview: self.scrollNode.view) + self.statusNodeContainer.addSubnode(self.statusNode) self.addSubnode(self.statusNodeContainer)