mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Finalize timestamp sharing
This commit is contained in:
parent
d0b5f8b400
commit
c21ebb06b5
@ -49,11 +49,23 @@ public enum ShareControllerError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum ShareControllerSubject {
|
public enum ShareControllerSubject {
|
||||||
|
public final class PublicLinkPrefix {
|
||||||
|
public let visibleString: String
|
||||||
|
public let actualString: String
|
||||||
|
|
||||||
|
public init(visibleString: String, actualString: String) {
|
||||||
|
self.visibleString = visibleString
|
||||||
|
self.actualString = actualString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class MediaParameters {
|
public final class MediaParameters {
|
||||||
public let startAtTimestamp: Int32?
|
public let startAtTimestamp: Int32?
|
||||||
|
public let publicLinkPrefix: PublicLinkPrefix?
|
||||||
|
|
||||||
public init(startAtTimestamp: Int32?) {
|
public init(startAtTimestamp: Int32?, publicLinkPrefix: PublicLinkPrefix?) {
|
||||||
self.startAtTimestamp = startAtTimestamp
|
self.startAtTimestamp = startAtTimestamp
|
||||||
|
self.publicLinkPrefix = publicLinkPrefix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1367,6 +1367,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
return panelHeight
|
return panelHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func animateIn(transition: ContainedViewLayoutTransition) {
|
||||||
|
self.contentNode.alpha = 0.0
|
||||||
|
transition.updateAlpha(node: self.contentNode, alpha: self.visibilityAlpha)
|
||||||
|
}
|
||||||
|
|
||||||
override func animateIn(fromHeight: CGFloat, previousContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition) {
|
override func animateIn(fromHeight: CGFloat, previousContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition) {
|
||||||
if let scrubberView = self.scrubberView, scrubberView.superview == self.view {
|
if let scrubberView = self.scrubberView, scrubberView.superview == self.view {
|
||||||
if let previousContentNode = previousContentNode as? ChatItemGalleryFooterContentNode, previousContentNode.scrubberView != nil {
|
if let previousContentNode = previousContentNode as? ChatItemGalleryFooterContentNode, previousContentNode.scrubberView != nil {
|
||||||
@ -1392,6 +1397,10 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
self.scrollWrapperNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
self.scrollWrapperNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func animateOut(transition: ContainedViewLayoutTransition) {
|
||||||
|
transition.updateAlpha(node: self.contentNode, alpha: 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
override func animateOut(toHeight: CGFloat, nextContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
override func animateOut(toHeight: CGFloat, nextContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
||||||
if let scrubberView = self.scrubberView, scrubberView.superview == self.view {
|
if let scrubberView = self.scrubberView, scrubberView.superview == self.view {
|
||||||
if let nextContentNode = nextContentNode as? ChatItemGalleryFooterContentNode, nextContentNode.scrubberView != nil {
|
if let nextContentNode = nextContentNode as? ChatItemGalleryFooterContentNode, nextContentNode.scrubberView != nil {
|
||||||
@ -1718,6 +1727,30 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
shareController.dismissed = { [weak self] _ in
|
shareController.dismissed = { [weak self] _ in
|
||||||
self?.interacting?(false)
|
self?.interacting?(false)
|
||||||
}
|
}
|
||||||
|
shareController.onMediaTimestampLinkCopied = { [weak self] timestamp in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let text: String
|
||||||
|
if let timestamp {
|
||||||
|
//TODO:localize
|
||||||
|
let startTimeString: String
|
||||||
|
let hours = timestamp / (60 * 60)
|
||||||
|
let minutes = timestamp % (60 * 60) / 60
|
||||||
|
let seconds = timestamp % 60
|
||||||
|
if hours != 0 {
|
||||||
|
startTimeString = String(format: "%d:%02d:%02d", hours, minutes, seconds)
|
||||||
|
} else {
|
||||||
|
startTimeString = String(format: "%d:%02d", minutes, seconds)
|
||||||
|
}
|
||||||
|
text = "Link with start time at \(startTimeString) copied to clipboard."
|
||||||
|
} else {
|
||||||
|
text = presentationData.strings.Conversation_LinkCopied
|
||||||
|
}
|
||||||
|
|
||||||
|
self.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: text), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return true }), nil)
|
||||||
|
}
|
||||||
|
|
||||||
shareController.actionCompleted = { [weak self] in
|
shareController.actionCompleted = { [weak self] in
|
||||||
if let strongSelf = self, let actionCompletionText = actionCompletionText {
|
if let strongSelf = self, let actionCompletionText = actionCompletionText {
|
||||||
|
@ -42,6 +42,8 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
|||||||
|
|
||||||
private var currentChapter: MediaPlayerScrubbingChapter?
|
private var currentChapter: MediaPlayerScrubbingChapter?
|
||||||
|
|
||||||
|
private var isAnimatedOut: Bool = false
|
||||||
|
|
||||||
var hideWhenDurationIsUnknown = false {
|
var hideWhenDurationIsUnknown = false {
|
||||||
didSet {
|
didSet {
|
||||||
if self.hideWhenDurationIsUnknown {
|
if self.hideWhenDurationIsUnknown {
|
||||||
@ -150,6 +152,9 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateTimestampsVisibility(animated: Bool) {
|
func updateTimestampsVisibility(animated: Bool) {
|
||||||
|
if self.isAnimatedOut {
|
||||||
|
return
|
||||||
|
}
|
||||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
|
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
|
||||||
let alpha: CGFloat = self.isCollapsed == true || self.isLoading ? 0.0 : 1.0
|
let alpha: CGFloat = self.isCollapsed == true || self.isLoading ? 0.0 : 1.0
|
||||||
transition.updateAlpha(node: self.leftTimestampNode, alpha: alpha)
|
transition.updateAlpha(node: self.leftTimestampNode, alpha: alpha)
|
||||||
@ -375,4 +380,80 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
|||||||
}
|
}
|
||||||
return hitTestRect.contains(point)
|
return hitTestRect.contains(point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func animateIn(from scrubberTransition: GalleryItemScrubberTransition?, transition: ContainedViewLayoutTransition) {
|
||||||
|
if let scrubberTransition {
|
||||||
|
let fromRect = scrubberTransition.view.convert(scrubberTransition.view.bounds, to: self)
|
||||||
|
|
||||||
|
let targetCloneView = scrubberTransition.makeView()
|
||||||
|
self.addSubview(targetCloneView)
|
||||||
|
targetCloneView.frame = fromRect
|
||||||
|
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.TransitionState(sourceSize: fromRect.size, destinationSize: CGSize(width: self.scrubberNode.bounds.width, height: fromRect.height), progress: 0.0), .immediate)
|
||||||
|
targetCloneView.alpha = 1.0
|
||||||
|
|
||||||
|
transition.updateFrame(view: targetCloneView, frame: CGRect(origin: CGPoint(x: self.scrubberNode.frame.minX, y: self.scrubberNode.frame.maxY - fromRect.height - 3.0), size: CGSize(width: self.scrubberNode.bounds.width, height: fromRect.height)))
|
||||||
|
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.TransitionState(sourceSize: fromRect.size, destinationSize: CGSize(width: self.scrubberNode.bounds.width, height: fromRect.height), progress: 1.0), transition)
|
||||||
|
let scrubberTransitionView = scrubberTransition.view
|
||||||
|
scrubberTransitionView.isHidden = true
|
||||||
|
ContainedViewLayoutTransition.animated(duration: 0.08, curve: .easeInOut).updateAlpha(layer: targetCloneView.layer, alpha: 0.0, completion: { [weak scrubberTransitionView, weak targetCloneView] _ in
|
||||||
|
scrubberTransitionView?.isHidden = false
|
||||||
|
targetCloneView?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
|
||||||
|
let scrubberSourceRect = CGRect(origin: CGPoint(x: fromRect.minX, y: fromRect.maxY - 3.0), size: CGSize(width: fromRect.width, height: 3.0))
|
||||||
|
|
||||||
|
let leftTimestampOffset = CGPoint(x: self.leftTimestampNode.position.x - self.scrubberNode.frame.minX, y: self.leftTimestampNode.position.y - self.scrubberNode.frame.maxY)
|
||||||
|
let rightTimestampOffset = CGPoint(x: self.rightTimestampNode.position.x - self.scrubberNode.frame.maxX, y: self.rightTimestampNode.position.y - self.scrubberNode.frame.maxY)
|
||||||
|
|
||||||
|
transition.animatePosition(node: self.scrubberNode, from: scrubberSourceRect.center)
|
||||||
|
self.scrubberNode.animateWidth(from: scrubberSourceRect.width, transition: transition)
|
||||||
|
|
||||||
|
transition.animatePosition(node: self.leftTimestampNode, from: CGPoint(x: leftTimestampOffset.x + scrubberSourceRect.minX, y: leftTimestampOffset.y + scrubberSourceRect.maxY))
|
||||||
|
transition.animatePosition(node: self.rightTimestampNode, from: CGPoint(x: rightTimestampOffset.x + scrubberSourceRect.maxX, y: rightTimestampOffset.y + scrubberSourceRect.maxY))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scrubberNode.layer.animateAlpha(from: 0.0, to: self.leftTimestampNode.alpha, duration: 0.25)
|
||||||
|
self.leftTimestampNode.layer.animateAlpha(from: 0.0, to: self.leftTimestampNode.alpha, duration: 0.25)
|
||||||
|
self.rightTimestampNode.layer.animateAlpha(from: 0.0, to: self.leftTimestampNode.alpha, duration: 0.25)
|
||||||
|
self.infoNode.layer.animateAlpha(from: 0.0, to: self.leftTimestampNode.alpha, duration: 0.25)
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateOut(to scrubberTransition: GalleryItemScrubberTransition?, transition: ContainedViewLayoutTransition) {
|
||||||
|
self.isAnimatedOut = true
|
||||||
|
|
||||||
|
if let scrubberTransition {
|
||||||
|
let toRect = scrubberTransition.view.convert(scrubberTransition.view.bounds, to: self)
|
||||||
|
let scrubberDestinationRect = CGRect(origin: CGPoint(x: toRect.minX, y: toRect.maxY - 3.0), size: CGSize(width: toRect.width, height: 3.0))
|
||||||
|
|
||||||
|
let targetCloneView = scrubberTransition.makeView()
|
||||||
|
self.addSubview(targetCloneView)
|
||||||
|
targetCloneView.frame = CGRect(origin: CGPoint(x: self.scrubberNode.frame.minX, y: self.scrubberNode.frame.maxY - toRect.height), size: CGSize(width: self.scrubberNode.bounds.width, height: toRect.height))
|
||||||
|
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.TransitionState(sourceSize: CGSize(width: self.scrubberNode.bounds.width, height: toRect.height), destinationSize: toRect.size, progress: 0.0), .immediate)
|
||||||
|
targetCloneView.alpha = 0.0
|
||||||
|
|
||||||
|
transition.updateFrame(view: targetCloneView, frame: toRect)
|
||||||
|
scrubberTransition.updateView(targetCloneView, GalleryItemScrubberTransition.TransitionState(sourceSize: CGSize(width: self.scrubberNode.bounds.width, height: toRect.height), destinationSize: toRect.size, progress: 1.0), transition)
|
||||||
|
let scrubberTransitionView = scrubberTransition.view
|
||||||
|
scrubberTransitionView.isHidden = true
|
||||||
|
transition.updateAlpha(layer: targetCloneView.layer, alpha: 1.0, completion: { [weak scrubberTransitionView] _ in
|
||||||
|
scrubberTransitionView?.isHidden = false
|
||||||
|
})
|
||||||
|
|
||||||
|
let leftTimestampOffset = CGPoint(x: self.leftTimestampNode.position.x - self.scrubberNode.frame.minX, y: self.leftTimestampNode.position.y - self.scrubberNode.frame.maxY)
|
||||||
|
let rightTimestampOffset = CGPoint(x: self.rightTimestampNode.position.x - self.scrubberNode.frame.maxX, y: self.rightTimestampNode.position.y - self.scrubberNode.frame.maxY)
|
||||||
|
|
||||||
|
transition.animatePositionAdditive(layer: self.scrubberNode.layer, offset: CGPoint(), to: CGPoint(x: scrubberDestinationRect.midX - self.scrubberNode.position.x, y: scrubberDestinationRect.midY - self.scrubberNode.position.y), removeOnCompletion: false)
|
||||||
|
|
||||||
|
self.scrubberNode.animateWidth(to: scrubberDestinationRect.width, transition: transition)
|
||||||
|
|
||||||
|
transition.animatePositionAdditive(layer: self.leftTimestampNode.layer, offset: CGPoint(), to: CGPoint(x: -self.leftTimestampNode.position.x + (leftTimestampOffset.x + scrubberDestinationRect.minX), y: -self.leftTimestampNode.position.y + (leftTimestampOffset.y + scrubberDestinationRect.maxY)), removeOnCompletion: false)
|
||||||
|
|
||||||
|
transition.animatePositionAdditive(layer: self.rightTimestampNode.layer, offset: CGPoint(), to: CGPoint(x: -self.rightTimestampNode.position.x + (rightTimestampOffset.x + scrubberDestinationRect.maxX), y: -self.rightTimestampNode.position.y + (rightTimestampOffset.y + scrubberDestinationRect.maxY)), removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.updateAlpha(layer: self.scrubberNode.layer, alpha: 0.0)
|
||||||
|
transition.updateAlpha(layer: self.leftTimestampNode.layer, alpha: 0.0)
|
||||||
|
transition.updateAlpha(layer: self.rightTimestampNode.layer, alpha: 0.0)
|
||||||
|
transition.updateAlpha(layer: self.infoNode.layer, alpha: 0.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,13 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.pager.controlsVisibility = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !self.areControlsHidden && self.footerNode.alpha != 0.0
|
||||||
|
}
|
||||||
|
|
||||||
self.pager.updateOrientation = { [weak self] orientation in
|
self.pager.updateOrientation = { [weak self] orientation in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateOrientation?(orientation)
|
strongSelf.updateOrientation?(orientation)
|
||||||
@ -364,11 +371,15 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||||||
if !self.areControlsHidden {
|
if !self.areControlsHidden {
|
||||||
self.statusBar?.alpha = 1.0
|
self.statusBar?.alpha = 1.0
|
||||||
self.navigationBar?.alpha = 1.0
|
self.navigationBar?.alpha = 1.0
|
||||||
self.footerNode.alpha = 1.0
|
|
||||||
self.updateThumbnailContainerNodeAlpha(.immediate)
|
self.updateThumbnailContainerNodeAlpha(.immediate)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if !self.areControlsHidden {
|
||||||
|
self.footerNode.alpha = 1.0
|
||||||
|
self.footerNode.animateIn(transition: .animated(duration: 0.15, curve: .linear))
|
||||||
|
}
|
||||||
|
|
||||||
if animateContent {
|
if animateContent {
|
||||||
self.scrollView.layer.animateBounds(from: self.scrollView.layer.bounds.offsetBy(dx: 0.0, dy: -self.scrollView.layer.bounds.size.height), to: self.scrollView.layer.bounds, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
self.scrollView.layer.animateBounds(from: self.scrollView.layer.bounds.offsetBy(dx: 0.0, dy: -self.scrollView.layer.bounds.size.height), to: self.scrollView.layer.bounds, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
} else if useSimpleAnimation {
|
} else if useSimpleAnimation {
|
||||||
@ -402,13 +413,14 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||||||
UIView.animate(withDuration: 0.1, animations: {
|
UIView.animate(withDuration: 0.1, animations: {
|
||||||
self.statusBar?.alpha = 0.0
|
self.statusBar?.alpha = 0.0
|
||||||
self.navigationBar?.alpha = 0.0
|
self.navigationBar?.alpha = 0.0
|
||||||
self.footerNode.alpha = 0.0
|
|
||||||
self.currentThumbnailContainerNode?.alpha = 0.0
|
self.currentThumbnailContainerNode?.alpha = 0.0
|
||||||
}, completion: { _ in
|
}, completion: { _ in
|
||||||
interfaceAnimationCompleted = true
|
interfaceAnimationCompleted = true
|
||||||
intermediateCompletion()
|
intermediateCompletion()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.footerNode.animateOut(transition: .animated(duration: 0.1, curve: .easeInOut))
|
||||||
|
|
||||||
if animateContent {
|
if animateContent {
|
||||||
contentAnimationCompleted = false
|
contentAnimationCompleted = false
|
||||||
self.scrollView.layer.animateBounds(from: self.scrollView.layer.bounds, to: self.scrollView.layer.bounds.offsetBy(dx: 0.0, dy: -self.scrollView.layer.bounds.size.height), duration: 0.25, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { _ in
|
self.scrollView.layer.animateBounds(from: self.scrollView.layer.bounds, to: self.scrollView.layer.bounds.offsetBy(dx: 0.0, dy: -self.scrollView.layer.bounds.size.height), duration: 0.25, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { _ in
|
||||||
|
@ -37,9 +37,18 @@ open class GalleryFooterContentNode: ASDisplayNode {
|
|||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func animateIn(transition: ContainedViewLayoutTransition) {
|
||||||
|
self.alpha = 0.0
|
||||||
|
transition.updateAlpha(node: self, alpha: 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
open func animateIn(fromHeight: CGFloat, previousContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition) {
|
open func animateIn(fromHeight: CGFloat, previousContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func animateOut(transition: ContainedViewLayoutTransition) {
|
||||||
|
transition.updateAlpha(node: self, alpha: 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
open func animateOut(toHeight: CGFloat, nextContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
open func animateOut(toHeight: CGFloat, nextContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,32 @@ public final class GalleryFooterNode: ASDisplayNode {
|
|||||||
self.currentOverlayContentNode?.setVisibilityAlpha(alpha)
|
self.currentOverlayContentNode?.setVisibilityAlpha(alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func animateIn(transition: ContainedViewLayoutTransition) {
|
||||||
|
self.backgroundNode.alpha = 0.0
|
||||||
|
transition.updateAlpha(node: self.backgroundNode, alpha: 1.0)
|
||||||
|
|
||||||
|
if let currentFooterContentNode = self.currentFooterContentNode {
|
||||||
|
currentFooterContentNode.animateIn(transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let currentOverlayContentNode = self.currentOverlayContentNode {
|
||||||
|
currentOverlayContentNode.alpha = 0.0
|
||||||
|
transition.updateAlpha(node: currentOverlayContentNode, alpha: 1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateOut(transition: ContainedViewLayoutTransition) {
|
||||||
|
transition.updateAlpha(node: self.backgroundNode, alpha: 0.0)
|
||||||
|
|
||||||
|
if let currentFooterContentNode = self.currentFooterContentNode {
|
||||||
|
currentFooterContentNode.animateOut(transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let currentOverlayContentNode = self.currentOverlayContentNode {
|
||||||
|
transition.updateAlpha(node: currentOverlayContentNode, alpha: 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func updateLayout(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, footerContentNode: GalleryFooterContentNode?, overlayContentNode: GalleryOverlayContentNode?, thumbnailPanelHeight: CGFloat, isHidden: Bool, transition: ContainedViewLayoutTransition) {
|
public func updateLayout(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, footerContentNode: GalleryFooterContentNode?, overlayContentNode: GalleryOverlayContentNode?, thumbnailPanelHeight: CGFloat, isHidden: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
self.currentLayout = (layout, navigationBarHeight, thumbnailPanelHeight, isHidden)
|
self.currentLayout = (layout, navigationBarHeight, thumbnailPanelHeight, isHidden)
|
||||||
let cleanInsets = layout.insets(options: [])
|
let cleanInsets = layout.insets(options: [])
|
||||||
|
@ -27,6 +27,7 @@ open class GalleryItemNode: ASDisplayNode {
|
|||||||
|
|
||||||
public var toggleControlsVisibility: () -> Void = { }
|
public var toggleControlsVisibility: () -> Void = { }
|
||||||
public var updateControlsVisibility: (Bool) -> Void = { _ in }
|
public var updateControlsVisibility: (Bool) -> Void = { _ in }
|
||||||
|
public var controlsVisibility: () -> Bool = { return true }
|
||||||
public var updateOrientation: (UIInterfaceOrientation) -> Void = { _ in }
|
public var updateOrientation: (UIInterfaceOrientation) -> Void = { _ in }
|
||||||
public var dismiss: () -> Void = { }
|
public var dismiss: () -> Void = { }
|
||||||
public var beginCustomDismiss: (Bool) -> Void = { _ in }
|
public var beginCustomDismiss: (Bool) -> Void = { _ in }
|
||||||
|
@ -1,8 +1,42 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import UIKit
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import Display
|
||||||
|
|
||||||
|
public final class GalleryItemScrubberTransition {
|
||||||
|
public struct TransitionState: Equatable {
|
||||||
|
public var sourceSize: CGSize
|
||||||
|
public var destinationSize: CGSize
|
||||||
|
public var progress: CGFloat
|
||||||
|
|
||||||
|
public init(
|
||||||
|
sourceSize: CGSize,
|
||||||
|
destinationSize: CGSize,
|
||||||
|
progress: CGFloat
|
||||||
|
) {
|
||||||
|
self.sourceSize = sourceSize
|
||||||
|
self.destinationSize = destinationSize
|
||||||
|
self.progress = progress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public let view: UIView
|
||||||
|
public let makeView: () -> UIView
|
||||||
|
public let updateView: (UIView, TransitionState, ContainedViewLayoutTransition) -> Void
|
||||||
|
public let insertCloneTransitionView: ((UIView) -> Void)?
|
||||||
|
|
||||||
|
public init(view: UIView, makeView: @escaping () -> UIView, updateView: @escaping (UIView, TransitionState, ContainedViewLayoutTransition) -> Void, insertCloneTransitionView: ((UIView) -> Void)?) {
|
||||||
|
self.view = view
|
||||||
|
self.makeView = makeView
|
||||||
|
self.updateView = updateView
|
||||||
|
self.insertCloneTransitionView = insertCloneTransitionView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public protocol GalleryItemTransitionNode: AnyObject {
|
public protocol GalleryItemTransitionNode: AnyObject {
|
||||||
func isAvailableForGalleryTransition() -> Bool
|
func isAvailableForGalleryTransition() -> Bool
|
||||||
func isAvailableForInstantPageTransition() -> Bool
|
func isAvailableForInstantPageTransition() -> Bool
|
||||||
var decoration: UniversalVideoDecoration? { get }
|
var decoration: UniversalVideoDecoration? { get }
|
||||||
|
|
||||||
|
func scrubberTransition() -> GalleryItemScrubberTransition?
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,7 @@ public final class GalleryPagerNode: ASDisplayNode, ASScrollViewDelegate, ASGest
|
|||||||
public var centralItemIndexOffsetUpdated: (([GalleryItem]?, Int, CGFloat)?) -> Void = { _ in }
|
public var centralItemIndexOffsetUpdated: (([GalleryItem]?, Int, CGFloat)?) -> Void = { _ in }
|
||||||
public var toggleControlsVisibility: () -> Void = { }
|
public var toggleControlsVisibility: () -> Void = { }
|
||||||
public var updateControlsVisibility: (Bool) -> Void = { _ in }
|
public var updateControlsVisibility: (Bool) -> Void = { _ in }
|
||||||
|
public var controlsVisibility: () -> Bool = { return true }
|
||||||
public var updateOrientation: (UIInterfaceOrientation) -> Void = { _ in }
|
public var updateOrientation: (UIInterfaceOrientation) -> Void = { _ in }
|
||||||
public var dismiss: () -> Void = { }
|
public var dismiss: () -> Void = { }
|
||||||
public var beginCustomDismiss: (Bool) -> Void = { _ in }
|
public var beginCustomDismiss: (Bool) -> Void = { _ in }
|
||||||
@ -595,6 +596,7 @@ public final class GalleryPagerNode: ASDisplayNode, ASScrollViewDelegate, ASGest
|
|||||||
let node = self.items[index].node(synchronous: synchronous)
|
let node = self.items[index].node(synchronous: synchronous)
|
||||||
node.toggleControlsVisibility = self.toggleControlsVisibility
|
node.toggleControlsVisibility = self.toggleControlsVisibility
|
||||||
node.updateControlsVisibility = self.updateControlsVisibility
|
node.updateControlsVisibility = self.updateControlsVisibility
|
||||||
|
node.controlsVisibility = self.controlsVisibility
|
||||||
node.updateOrientation = self.updateOrientation
|
node.updateOrientation = self.updateOrientation
|
||||||
node.dismiss = self.dismiss
|
node.dismiss = self.dismiss
|
||||||
node.beginCustomDismiss = self.beginCustomDismiss
|
node.beginCustomDismiss = self.beginCustomDismiss
|
||||||
|
@ -1404,17 +1404,29 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
self.clipsToBounds = true
|
self.clipsToBounds = true
|
||||||
|
|
||||||
//TODO:wip-release
|
self.footerContentNode.shareMediaParameters = { [weak self] in
|
||||||
/*self.footerContentNode.shareMediaParameters = { [weak self] in
|
|
||||||
guard let self, let playerStatusValue = self.playerStatusValue else {
|
guard let self, let playerStatusValue = self.playerStatusValue else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if playerStatusValue.duration >= 60.0 * 10.0 {
|
if playerStatusValue.duration >= 60.0 * 10.0 {
|
||||||
return ShareControllerSubject.MediaParameters(startAtTimestamp: Int32(playerStatusValue.timestamp))
|
var publicLinkPrefix: ShareControllerSubject.PublicLinkPrefix?
|
||||||
|
if case let .message(message, _) = self.item?.contentInfo, message.id.namespace == Namespaces.Message.Cloud, let peer = message.peers[message.id.peerId] as? TelegramChannel, let username = peer.username {
|
||||||
|
let visibleString = "t.me/\(username)/\(message.id.id)"
|
||||||
|
publicLinkPrefix = ShareControllerSubject.PublicLinkPrefix(
|
||||||
|
visibleString: visibleString,
|
||||||
|
actualString: "https://\(visibleString)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShareControllerSubject.MediaParameters(
|
||||||
|
startAtTimestamp: Int32(playerStatusValue.timestamp),
|
||||||
|
publicLinkPrefix: publicLinkPrefix
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside)
|
self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside)
|
||||||
self.settingsBarButton.addTarget(self, action: #selector(self.settingsButtonPressed), forControlEvents: .touchUpInside)
|
self.settingsBarButton.addTarget(self, action: #selector(self.settingsButtonPressed), forControlEvents: .touchUpInside)
|
||||||
@ -2456,6 +2468,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let node = node.0 as? OverlayMediaItemNode, self.context.sharedContext.mediaManager.hasOverlayVideoNode(node) {
|
if let node = node.0 as? OverlayMediaItemNode, self.context.sharedContext.mediaManager.hasOverlayVideoNode(node) {
|
||||||
|
if let scrubberView = self.scrubberView {
|
||||||
|
scrubberView.animateIn(from: nil, transition: .animated(duration: 0.25, curve: .spring))
|
||||||
|
}
|
||||||
|
|
||||||
var transformedFrame = node.view.convert(node.view.bounds, to: videoNode.view)
|
var transformedFrame = node.view.convert(node.view.bounds, to: videoNode.view)
|
||||||
let transformedSuperFrame = node.view.convert(node.view.bounds, to: videoNode.view.superview)
|
let transformedSuperFrame = node.view.convert(node.view.bounds, to: videoNode.view.superview)
|
||||||
|
|
||||||
@ -2471,6 +2487,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
|
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
|
||||||
} else {
|
} else {
|
||||||
|
if let scrubberView = self.scrubberView {
|
||||||
|
let scrubberTransition = (node.0 as? GalleryItemTransitionNode)?.scrubberTransition()
|
||||||
|
scrubberView.animateIn(from: scrubberTransition, transition: .animated(duration: 0.25, curve: .spring))
|
||||||
|
}
|
||||||
|
|
||||||
var transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view)
|
var transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view)
|
||||||
var transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
|
var transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
|
||||||
var transformedSelfFrame = node.0.view.convert(node.0.view.bounds, to: self.view)
|
var transformedSelfFrame = node.0.view.convert(node.0.view.bounds, to: self.view)
|
||||||
@ -2570,6 +2591,14 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let scrubberView = self.scrubberView {
|
||||||
|
var scrubberTransition = (node.0 as? GalleryItemTransitionNode)?.scrubberTransition()
|
||||||
|
if !self.controlsVisibility() {
|
||||||
|
scrubberTransition = nil
|
||||||
|
}
|
||||||
|
scrubberView.animateOut(to: scrubberTransition, transition: .animated(duration: 0.25, curve: .spring))
|
||||||
|
}
|
||||||
|
|
||||||
let transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view)
|
let transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view)
|
||||||
var transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
|
var transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
|
||||||
let transformedSelfFrame = node.0.view.convert(node.0.view.bounds, to: self.view)
|
let transformedSelfFrame = node.0.view.convert(node.0.view.bounds, to: self.view)
|
||||||
|
@ -177,4 +177,8 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode, Galler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func scrubberTransition() -> GalleryItemScrubberTransition? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1091,4 +1091,12 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func animateWidth(from: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
transition.animateTransformScale(layer: self.layer, from: CGPoint(x: from / self.bounds.width, y: 1.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func animateWidth(to: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
transition.updateTransformScale(node: self, scale: CGPoint(x: to / self.bounds.width, y: 1.0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
|
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
|
||||||
"//submodules/UndoUI",
|
"//submodules/UndoUI",
|
||||||
"//submodules/Components/MultilineTextComponent",
|
"//submodules/Components/MultilineTextComponent",
|
||||||
|
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||||
"//submodules/Components/BundleIconComponent",
|
"//submodules/Components/BundleIconComponent",
|
||||||
"//submodules/TelegramUI/Components/LottieComponent",
|
"//submodules/TelegramUI/Components/LottieComponent",
|
||||||
"//submodules/TelegramUI/Components/MessageInputPanelComponent",
|
"//submodules/TelegramUI/Components/MessageInputPanelComponent",
|
||||||
|
@ -132,6 +132,8 @@ public final class ShareStartAtTimestampNode: HighlightTrackingButtonNode {
|
|||||||
return self.checkNode.selected
|
return self.checkNode.selected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var updated: (() -> Void)?
|
||||||
|
|
||||||
public init(titleText: String, titleTextColor: UIColor, checkNodeTheme: CheckNodeTheme) {
|
public init(titleText: String, titleTextColor: UIColor, checkNodeTheme: CheckNodeTheme) {
|
||||||
self.titleText = titleText
|
self.titleText = titleText
|
||||||
self.titleTextColor = titleTextColor
|
self.titleTextColor = titleTextColor
|
||||||
@ -154,6 +156,7 @@ public final class ShareStartAtTimestampNode: HighlightTrackingButtonNode {
|
|||||||
|
|
||||||
@objc private func pressed() {
|
@objc private func pressed() {
|
||||||
self.checkNode.setSelected(!self.checkNode.selected, animated: true)
|
self.checkNode.setSelected(!self.checkNode.selected, animated: true)
|
||||||
|
self.updated?()
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func layout() {
|
override public func layout() {
|
||||||
|
@ -436,6 +436,8 @@ public final class ShareController: ViewController {
|
|||||||
|
|
||||||
public var debugAction: (() -> Void)?
|
public var debugAction: (() -> Void)?
|
||||||
|
|
||||||
|
public var onMediaTimestampLinkCopied: ((Int32?) -> Void)?
|
||||||
|
|
||||||
public var parentNavigationController: NavigationController?
|
public var parentNavigationController: NavigationController?
|
||||||
|
|
||||||
public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false, collectibleItemInfo: TelegramCollectibleItemInfo? = nil) {
|
public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false, collectibleItemInfo: TelegramCollectibleItemInfo? = nil) {
|
||||||
@ -1210,6 +1212,12 @@ public final class ShareController: ViewController {
|
|||||||
return false
|
return false
|
||||||
}), in: .current)
|
}), in: .current)
|
||||||
}
|
}
|
||||||
|
self.controllerNode.onMediaTimestampLinkCopied = { [weak self] timestamp in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.onMediaTimestampLinkCopied?(timestamp)
|
||||||
|
}
|
||||||
self.controllerNode.debugAction = { [weak self] in
|
self.controllerNode.debugAction = { [weak self] in
|
||||||
self?.debugAction?()
|
self?.debugAction?()
|
||||||
}
|
}
|
||||||
|
@ -326,6 +326,8 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
private let fromPublicChannel: Bool
|
private let fromPublicChannel: Bool
|
||||||
private let segmentedValues: [ShareControllerSegmentedValue]?
|
private let segmentedValues: [ShareControllerSegmentedValue]?
|
||||||
private let collectibleItemInfo: TelegramCollectibleItemInfo?
|
private let collectibleItemInfo: TelegramCollectibleItemInfo?
|
||||||
|
private let mediaParameters: ShareControllerSubject.MediaParameters?
|
||||||
|
|
||||||
var selectedSegmentedIndex: Int = 0
|
var selectedSegmentedIndex: Int = 0
|
||||||
|
|
||||||
private let defaultAction: ShareControllerAction?
|
private let defaultAction: ShareControllerAction?
|
||||||
@ -365,6 +367,7 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
var enqueued: (([PeerId], [Int64]) -> Void)?
|
var enqueued: (([PeerId], [Int64]) -> Void)?
|
||||||
var present: ((ViewController) -> Void)?
|
var present: ((ViewController) -> Void)?
|
||||||
var disabledPeerSelected: ((EnginePeer) -> Void)?
|
var disabledPeerSelected: ((EnginePeer) -> Void)?
|
||||||
|
var onMediaTimestampLinkCopied: ((Int32?) -> Void)?
|
||||||
|
|
||||||
let ready = Promise<Bool>()
|
let ready = Promise<Bool>()
|
||||||
|
|
||||||
@ -397,6 +400,7 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
self.fromPublicChannel = fromPublicChannel
|
self.fromPublicChannel = fromPublicChannel
|
||||||
self.segmentedValues = segmentedValues
|
self.segmentedValues = segmentedValues
|
||||||
self.collectibleItemInfo = collectibleItemInfo
|
self.collectibleItemInfo = collectibleItemInfo
|
||||||
|
self.mediaParameters = mediaParameters
|
||||||
|
|
||||||
self.presetText = presetText
|
self.presetText = presetText
|
||||||
|
|
||||||
@ -471,7 +475,7 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
self.startAtTimestampNode = nil
|
self.startAtTimestampNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inputFieldNode = ShareInputFieldNode(theme: ShareInputFieldNodeTheme(presentationTheme: self.presentationData.theme), placeholder: self.presentationData.strings.ShareMenu_Comment)
|
self.inputFieldNode = ShareInputFieldNode(theme: ShareInputFieldNodeTheme(presentationTheme: self.presentationData.theme), strings: self.presentationData.strings, placeholder: self.presentationData.strings.ShareMenu_Comment)
|
||||||
self.inputFieldNode.text = presetText ?? ""
|
self.inputFieldNode.text = presetText ?? ""
|
||||||
self.inputFieldNode.preselectText()
|
self.inputFieldNode.preselectText()
|
||||||
self.inputFieldNode.alpha = 0.0
|
self.inputFieldNode.alpha = 0.0
|
||||||
@ -491,6 +495,15 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
|
|
||||||
self.isHidden = true
|
self.isHidden = true
|
||||||
|
|
||||||
|
self.startAtTimestampNode?.updated = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let (layout, navigationBarHeight, _) = self.containerLayout {
|
||||||
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.actionButtonNode.shouldBegin = { [weak self] in
|
self.actionButtonNode.shouldBegin = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
return !strongSelf.controllerInteraction!.selectedPeers.isEmpty
|
return !strongSelf.controllerInteraction!.selectedPeers.isEmpty
|
||||||
@ -593,7 +606,7 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !openedTopicList {
|
if !openedTopicList {
|
||||||
strongSelf.setActionNodesHidden(strongSelf.controllerInteraction!.selectedPeers.isEmpty && strongSelf.presetText == nil, inputField: true, actions: strongSelf.defaultAction == nil)
|
strongSelf.setActionNodesHidden(strongSelf.controllerInteraction!.selectedPeers.isEmpty && strongSelf.presetText == nil && strongSelf.mediaParameters?.publicLinkPrefix == nil, inputField: true, actions: strongSelf.defaultAction == nil)
|
||||||
|
|
||||||
strongSelf.updateButton()
|
strongSelf.updateButton()
|
||||||
|
|
||||||
@ -686,10 +699,44 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.inputFieldNode.onInputCopyText = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let publicLinkPrefix = self.mediaParameters?.publicLinkPrefix {
|
||||||
|
var timestampSuffix = ""
|
||||||
|
var effectiveStartTimestamp: Int32?
|
||||||
|
if let startAtTimestamp = self.mediaParameters?.startAtTimestamp, let startAtTimestampNode = self.startAtTimestampNode, startAtTimestampNode.value {
|
||||||
|
var startAtTimestampString = ""
|
||||||
|
let hours = startAtTimestamp / 3600
|
||||||
|
let minutes = startAtTimestamp / 60 % 60
|
||||||
|
let seconds = startAtTimestamp % 60
|
||||||
|
if hours == 0 && minutes == 0 {
|
||||||
|
startAtTimestampString = "\(startAtTimestamp)"
|
||||||
|
} else {
|
||||||
|
if hours != 0 {
|
||||||
|
startAtTimestampString += "\(hours)h"
|
||||||
|
}
|
||||||
|
if minutes != 0 {
|
||||||
|
startAtTimestampString += "\(minutes)m"
|
||||||
|
}
|
||||||
|
if seconds != 0 {
|
||||||
|
startAtTimestampString += "\(seconds)s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timestampSuffix = "?t=\(startAtTimestampString)"
|
||||||
|
effectiveStartTimestamp = startAtTimestamp
|
||||||
|
}
|
||||||
|
let inputCopyText = "\(publicLinkPrefix.actualString)\(timestampSuffix)"
|
||||||
|
UIPasteboard.general.string = inputCopyText
|
||||||
|
self.onMediaTimestampLinkCopied?(effectiveStartTimestamp)
|
||||||
|
}
|
||||||
|
self.cancel?()
|
||||||
|
}
|
||||||
|
|
||||||
self.updateButton()
|
self.updateButton()
|
||||||
|
|
||||||
if self.presetText != nil {
|
if self.presetText != nil || self.mediaParameters?.publicLinkPrefix != nil {
|
||||||
self.setActionNodesHidden(false, inputField: true, actions: true, animated: false)
|
self.setActionNodesHidden(false, inputField: true, actions: true, animated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -971,7 +1018,7 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
if contentNode is ShareSearchContainerNode {
|
if contentNode is ShareSearchContainerNode {
|
||||||
self.setActionNodesHidden(true, inputField: true, actions: true)
|
self.setActionNodesHidden(true, inputField: true, actions: true)
|
||||||
} else if !(contentNode is ShareLoadingContainer) {
|
} else if !(contentNode is ShareLoadingContainer) {
|
||||||
self.setActionNodesHidden(false, inputField: !self.controllerInteraction!.selectedPeers.isEmpty || self.presetText != nil, actions: true)
|
self.setActionNodesHidden(false, inputField: !self.controllerInteraction!.selectedPeers.isEmpty || self.presetText != nil || self.mediaParameters?.publicLinkPrefix != nil, actions: true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let contentNode = self.contentNode {
|
if let contentNode = self.contentNode {
|
||||||
@ -1021,7 +1068,7 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
var bottomGridInset: CGFloat = 0
|
var bottomGridInset: CGFloat = 0
|
||||||
|
|
||||||
var actionButtonHeight: CGFloat = 0
|
var actionButtonHeight: CGFloat = 0
|
||||||
if self.defaultAction != nil || !self.controllerInteraction!.selectedPeers.isEmpty || self.presetText != nil {
|
if self.defaultAction != nil || !self.controllerInteraction!.selectedPeers.isEmpty || self.presetText != nil || self.mediaParameters?.publicLinkPrefix != nil {
|
||||||
actionButtonHeight = buttonHeight
|
actionButtonHeight = buttonHeight
|
||||||
bottomGridInset += actionButtonHeight
|
bottomGridInset += actionButtonHeight
|
||||||
}
|
}
|
||||||
@ -1029,8 +1076,37 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
bottomGridInset += buttonHeight
|
bottomGridInset += buttonHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputHeight = self.inputFieldNode.updateLayout(width: contentContainerFrame.size.width, transition: transition)
|
var inputCopyText: String?
|
||||||
if !self.controllerInteraction!.selectedPeers.isEmpty || self.presetText != nil {
|
if !self.controllerInteraction!.selectedPeers.isEmpty || self.presetText != nil {
|
||||||
|
} else {
|
||||||
|
if let publicLinkPrefix = self.mediaParameters?.publicLinkPrefix {
|
||||||
|
var timestampSuffix = ""
|
||||||
|
if let startAtTimestamp = self.mediaParameters?.startAtTimestamp, let startAtTimestampNode = self.startAtTimestampNode, startAtTimestampNode.value {
|
||||||
|
var startAtTimestampString = ""
|
||||||
|
let hours = startAtTimestamp / 3600
|
||||||
|
let minutes = startAtTimestamp / 60 % 60
|
||||||
|
let seconds = startAtTimestamp % 60
|
||||||
|
if hours == 0 && minutes == 0 {
|
||||||
|
startAtTimestampString = "\(startAtTimestamp)"
|
||||||
|
} else {
|
||||||
|
if hours != 0 {
|
||||||
|
startAtTimestampString += "\(hours)h"
|
||||||
|
}
|
||||||
|
if minutes != 0 {
|
||||||
|
startAtTimestampString += "\(minutes)m"
|
||||||
|
}
|
||||||
|
if seconds != 0 {
|
||||||
|
startAtTimestampString += "\(seconds)s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timestampSuffix = "?t=\(startAtTimestampString)"
|
||||||
|
}
|
||||||
|
inputCopyText = "\(publicLinkPrefix.visibleString)\(timestampSuffix)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputHeight = self.inputFieldNode.updateLayout(width: contentContainerFrame.size.width, inputCopyText: inputCopyText, transition: transition)
|
||||||
|
if !self.controllerInteraction!.selectedPeers.isEmpty || self.presetText != nil || self.mediaParameters?.publicLinkPrefix != nil {
|
||||||
bottomGridInset += inputHeight
|
bottomGridInset += inputHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,9 @@ import AsyncDisplayKit
|
|||||||
import Display
|
import Display
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AppBundle
|
import AppBundle
|
||||||
|
import ComponentFlow
|
||||||
|
import MultilineTextComponent
|
||||||
|
import AnimatedTextComponent
|
||||||
|
|
||||||
private func generateClearIcon(color: UIColor) -> UIImage? {
|
private func generateClearIcon(color: UIColor) -> UIImage? {
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: color)
|
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: color)
|
||||||
@ -55,19 +58,154 @@ public extension ShareInputFieldNodeTheme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ShareInputCopyComponent: Component {
|
||||||
|
let theme: ShareInputFieldNodeTheme
|
||||||
|
let strings: PresentationStrings
|
||||||
|
let text: String
|
||||||
|
let action: () -> Void
|
||||||
|
|
||||||
|
init(
|
||||||
|
theme: ShareInputFieldNodeTheme,
|
||||||
|
strings: PresentationStrings,
|
||||||
|
text: String,
|
||||||
|
action: @escaping () -> Void
|
||||||
|
) {
|
||||||
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
|
self.text = text
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: ShareInputCopyComponent, rhs: ShareInputCopyComponent) -> Bool {
|
||||||
|
if lhs.theme !== rhs.theme {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.strings !== rhs.strings {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.text != rhs.text {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
final class View: UIView {
|
||||||
|
let text = ComponentView<Empty>()
|
||||||
|
let button = ComponentView<Empty>()
|
||||||
|
let textMask = UIImageView()
|
||||||
|
|
||||||
|
var component: ShareInputCopyComponent?
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: ShareInputCopyComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||||
|
let textChanged = self.component != nil && self.component?.text != component.text
|
||||||
|
self.component = component
|
||||||
|
|
||||||
|
var textItems: [AnimatedTextComponent.Item] = []
|
||||||
|
if let range = component.text.range(of: "?", options: .backwards) {
|
||||||
|
textItems.append(AnimatedTextComponent.Item(id: 0, isUnbreakable: true, content: .text(String(component.text[component.text.startIndex ..< range.lowerBound]))))
|
||||||
|
textItems.append(AnimatedTextComponent.Item(id: 1, isUnbreakable: true, content: .text(String(component.text[range.lowerBound...]))))
|
||||||
|
} else {
|
||||||
|
textItems.append(AnimatedTextComponent.Item(id: 0, isUnbreakable: true, content: .text(component.text)))
|
||||||
|
}
|
||||||
|
|
||||||
|
let sideInset: CGFloat = 12.0
|
||||||
|
let textSize = self.text.update(
|
||||||
|
transition: textChanged ? .spring(duration: 0.4) : .immediate,
|
||||||
|
component: AnyComponent(AnimatedTextComponent(
|
||||||
|
font: Font.regular(17.0),
|
||||||
|
color: component.theme.textColor,
|
||||||
|
items: textItems,
|
||||||
|
animateScale: false
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0)
|
||||||
|
)
|
||||||
|
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((availableSize.height - textSize.height) * 0.5)), size: textSize)
|
||||||
|
if let textView = self.text.view {
|
||||||
|
if textView.superview == nil {
|
||||||
|
self.addSubview(textView)
|
||||||
|
textView.mask = self.textMask
|
||||||
|
}
|
||||||
|
textView.frame = textFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
let buttonSize = self.button.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(Button(
|
||||||
|
content: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: component.strings.Conversation_LinkDialogCopy, font: Font.regular(17.0), textColor: component.theme.accentColor))
|
||||||
|
)),
|
||||||
|
action: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.component?.action()
|
||||||
|
}
|
||||||
|
).minSize(CGSize(width: 0.0, height: availableSize.height))),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - 40.0, height: 1000.0)
|
||||||
|
)
|
||||||
|
let buttonFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - buttonSize.width, y: floor((availableSize.height - buttonSize.height) * 0.5)), size: buttonSize)
|
||||||
|
if let buttonView = self.button.view {
|
||||||
|
if buttonView.superview == nil {
|
||||||
|
self.addSubview(buttonView)
|
||||||
|
}
|
||||||
|
buttonView.frame = buttonFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.textMask.image == nil {
|
||||||
|
let gradientWidth: CGFloat = 26.0
|
||||||
|
self.textMask.image = generateGradientImage(size: CGSize(width: gradientWidth, height: 8.0), colors: [
|
||||||
|
UIColor(white: 1.0, alpha: 1.0),
|
||||||
|
UIColor(white: 1.0, alpha: 1.0),
|
||||||
|
UIColor(white: 1.0, alpha: 0.0)
|
||||||
|
], locations: [
|
||||||
|
0.0,
|
||||||
|
1.0 / gradientWidth,
|
||||||
|
1.0
|
||||||
|
], direction: .horizontal)?.resizableImage(withCapInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: gradientWidth - 1.0), resizingMode: .stretch)
|
||||||
|
self.textMask.frame = CGRect(origin: CGPoint(), size: CGSize(width: max(0.0, buttonFrame.minX - 4.0 - textFrame.minX), height: textFrame.height))
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeView() -> View {
|
||||||
|
return View(frame: CGRect())
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate {
|
public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate {
|
||||||
private let theme: ShareInputFieldNodeTheme
|
private let theme: ShareInputFieldNodeTheme
|
||||||
|
private let strings: PresentationStrings
|
||||||
private let backgroundNode: ASImageNode
|
private let backgroundNode: ASImageNode
|
||||||
private let textInputNode: EditableTextNode
|
private let textInputNode: EditableTextNode
|
||||||
private let placeholderNode: ASTextNode
|
private let placeholderNode: ASTextNode
|
||||||
private let clearButton: HighlightableButtonNode
|
private let clearButton: HighlightableButtonNode
|
||||||
|
|
||||||
|
private var copyView: ComponentView<Empty>?
|
||||||
|
|
||||||
public var updateHeight: (() -> Void)?
|
public var updateHeight: (() -> Void)?
|
||||||
public var updateText: ((String) -> Void)?
|
public var updateText: ((String) -> Void)?
|
||||||
|
|
||||||
private let backgroundInsets = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 1.0, right: 16.0)
|
private let backgroundInsets = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 1.0, right: 16.0)
|
||||||
private let inputInsets = UIEdgeInsets(top: 10.0, left: 8.0, bottom: 10.0, right: 22.0)
|
private let inputInsets = UIEdgeInsets(top: 10.0, left: 8.0, bottom: 10.0, right: 22.0)
|
||||||
private let accessoryButtonsWidth: CGFloat = 10.0
|
private let accessoryButtonsWidth: CGFloat = 10.0
|
||||||
|
private var inputCopyText: String?
|
||||||
|
public var onInputCopyText: (() -> Void)?
|
||||||
|
|
||||||
private var selectTextOnce: Bool = false
|
private var selectTextOnce: Bool = false
|
||||||
|
|
||||||
@ -77,7 +215,7 @@ public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegat
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
self.textInputNode.attributedText = NSAttributedString(string: newValue, font: Font.regular(17.0), textColor: self.theme.textColor)
|
self.textInputNode.attributedText = NSAttributedString(string: newValue, font: Font.regular(17.0), textColor: self.theme.textColor)
|
||||||
self.placeholderNode.isHidden = !newValue.isEmpty
|
self.placeholderNode.isHidden = !newValue.isEmpty || self.inputCopyText != nil
|
||||||
self.clearButton.isHidden = newValue.isEmpty
|
self.clearButton.isHidden = newValue.isEmpty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,8 +226,9 @@ public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(theme: ShareInputFieldNodeTheme, placeholder: String) {
|
public init(theme: ShareInputFieldNodeTheme, strings: PresentationStrings, placeholder: String) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
|
|
||||||
self.backgroundNode = ASImageNode()
|
self.backgroundNode = ASImageNode()
|
||||||
self.backgroundNode.isLayerBacked = true
|
self.backgroundNode.isLayerBacked = true
|
||||||
@ -136,10 +275,11 @@ public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegat
|
|||||||
self.selectTextOnce = true
|
self.selectTextOnce = true
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
public func updateLayout(width: CGFloat, inputCopyText: String?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
let backgroundInsets = self.backgroundInsets
|
let backgroundInsets = self.backgroundInsets
|
||||||
let inputInsets = self.inputInsets
|
let inputInsets = self.inputInsets
|
||||||
let accessoryButtonsWidth = self.accessoryButtonsWidth
|
let accessoryButtonsWidth = self.accessoryButtonsWidth
|
||||||
|
self.inputCopyText = inputCopyText
|
||||||
|
|
||||||
let textFieldHeight = self.calculateTextFieldMetrics(width: width)
|
let textFieldHeight = self.calculateTextFieldMetrics(width: width)
|
||||||
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
|
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
|
||||||
@ -156,6 +296,43 @@ public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegat
|
|||||||
|
|
||||||
transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right - accessoryButtonsWidth, height: backgroundFrame.size.height)))
|
transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right - accessoryButtonsWidth, height: backgroundFrame.size.height)))
|
||||||
|
|
||||||
|
self.textInputNode.isUserInteractionEnabled = inputCopyText == nil
|
||||||
|
self.textInputNode.isHidden = inputCopyText != nil
|
||||||
|
self.placeholderNode.isHidden = !(self.textInputNode.textView.text ?? "").isEmpty || self.inputCopyText != nil
|
||||||
|
|
||||||
|
if let inputCopyText {
|
||||||
|
let copyView: ComponentView<Empty>
|
||||||
|
if let current = self.copyView {
|
||||||
|
copyView = current
|
||||||
|
} else {
|
||||||
|
copyView = ComponentView()
|
||||||
|
self.copyView = copyView
|
||||||
|
}
|
||||||
|
let copyViewSize = copyView.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(ShareInputCopyComponent(
|
||||||
|
theme: self.theme,
|
||||||
|
strings: self.strings,
|
||||||
|
text: inputCopyText,
|
||||||
|
action: {
|
||||||
|
self.onInputCopyText?()
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: backgroundFrame.size
|
||||||
|
)
|
||||||
|
let copyViewFrame = CGRect(origin: backgroundFrame.origin, size: copyViewSize)
|
||||||
|
if let copyComponentView = copyView.view {
|
||||||
|
if copyComponentView.superview == nil {
|
||||||
|
self.view.addSubview(copyComponentView)
|
||||||
|
}
|
||||||
|
copyComponentView.frame = copyViewFrame
|
||||||
|
}
|
||||||
|
} else if let copyView = self.copyView {
|
||||||
|
self.copyView = nil
|
||||||
|
copyView.view?.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
return panelHeight
|
return panelHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +347,7 @@ public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegat
|
|||||||
@objc public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
@objc public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
||||||
self.updateTextNodeText(animated: true)
|
self.updateTextNodeText(animated: true)
|
||||||
self.updateText?(editableTextNode.attributedText?.string ?? "")
|
self.updateText?(editableTextNode.attributedText?.string ?? "")
|
||||||
self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty
|
self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty || self.inputCopyText != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) {
|
public func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) {
|
||||||
@ -185,7 +362,7 @@ public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegat
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) {
|
public func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) {
|
||||||
self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty
|
self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty || self.inputCopyText != nil
|
||||||
self.clearButton.isHidden = true
|
self.clearButton.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,9 +371,13 @@ public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegat
|
|||||||
let inputInsets = self.inputInsets
|
let inputInsets = self.inputInsets
|
||||||
let accessoryButtonsWidth = self.accessoryButtonsWidth
|
let accessoryButtonsWidth = self.accessoryButtonsWidth
|
||||||
|
|
||||||
let unboundTextFieldHeight = max(33.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right - accessoryButtonsWidth, height: CGFloat.greatestFiniteMagnitude)).height))
|
if self.inputCopyText != nil {
|
||||||
|
return 41.0
|
||||||
|
} else {
|
||||||
|
let unboundTextFieldHeight = max(33.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right - accessoryButtonsWidth, height: CGFloat.greatestFiniteMagnitude)).height))
|
||||||
|
|
||||||
return min(61.0, max(41.0, unboundTextFieldHeight))
|
return min(61.0, max(41.0, unboundTextFieldHeight))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateTextNodeText(animated: Bool) {
|
private func updateTextNodeText(animated: Bool) {
|
||||||
|
@ -26,17 +26,20 @@ public final class AnimatedTextComponent: Component {
|
|||||||
public let color: UIColor
|
public let color: UIColor
|
||||||
public let items: [Item]
|
public let items: [Item]
|
||||||
public let noDelay: Bool
|
public let noDelay: Bool
|
||||||
|
public let animateScale: Bool
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
font: UIFont,
|
font: UIFont,
|
||||||
color: UIColor,
|
color: UIColor,
|
||||||
items: [Item],
|
items: [Item],
|
||||||
noDelay: Bool = false
|
noDelay: Bool = false,
|
||||||
|
animateScale: Bool = true
|
||||||
) {
|
) {
|
||||||
self.font = font
|
self.font = font
|
||||||
self.color = color
|
self.color = color
|
||||||
self.items = items
|
self.items = items
|
||||||
self.noDelay = noDelay
|
self.noDelay = noDelay
|
||||||
|
self.animateScale = animateScale
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: AnimatedTextComponent, rhs: AnimatedTextComponent) -> Bool {
|
public static func ==(lhs: AnimatedTextComponent, rhs: AnimatedTextComponent) -> Bool {
|
||||||
@ -52,6 +55,9 @@ public final class AnimatedTextComponent: Component {
|
|||||||
if lhs.noDelay != rhs.noDelay {
|
if lhs.noDelay != rhs.noDelay {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.animateScale != rhs.animateScale {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +178,9 @@ public final class AnimatedTextComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
characterComponentView.layer.animateScale(from: 0.001, to: 1.0, duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring)
|
if component.animateScale {
|
||||||
|
characterComponentView.layer.animateScale(from: 0.001, to: 1.0, duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
characterComponentView.layer.animatePosition(from: CGPoint(x: 0.0, y: characterSize.height * 0.5), to: CGPoint(), duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
characterComponentView.layer.animatePosition(from: CGPoint(x: 0.0, y: characterSize.height * 0.5), to: CGPoint(), duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||||
characterComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18, delay: delayNorm * delayWidth)
|
characterComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18, delay: delayNorm * delayWidth)
|
||||||
}
|
}
|
||||||
@ -202,7 +210,9 @@ public final class AnimatedTextComponent: Component {
|
|||||||
outFirstDelayWidth = characterComponentView.frame.minX
|
outFirstDelayWidth = characterComponentView.frame.minX
|
||||||
}
|
}
|
||||||
|
|
||||||
outScaleTransition.setScale(view: characterComponentView, scale: 0.01, delay: delayNorm * delayWidth)
|
if component.animateScale {
|
||||||
|
outScaleTransition.setScale(view: characterComponentView, scale: 0.01, delay: delayNorm * delayWidth)
|
||||||
|
}
|
||||||
outScaleTransition.setPosition(view: characterComponentView, position: CGPoint(x: characterComponentView.center.x, y: characterComponentView.center.y - characterComponentView.bounds.height * 0.4), delay: delayNorm * delayWidth)
|
outScaleTransition.setPosition(view: characterComponentView, position: CGPoint(x: characterComponentView.center.x, y: characterComponentView.center.y - characterComponentView.bounds.height * 0.4), delay: delayNorm * delayWidth)
|
||||||
outAlphaTransition.setAlpha(view: characterComponentView, alpha: 0.0, delay: delayNorm * delayWidth, completion: { [weak characterComponentView] _ in
|
outAlphaTransition.setAlpha(view: characterComponentView, alpha: 0.0, delay: delayNorm * delayWidth, completion: { [weak characterComponentView] _ in
|
||||||
characterComponentView?.removeFromSuperview()
|
characterComponentView?.removeFromSuperview()
|
||||||
|
@ -626,13 +626,6 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
transition.updateAlpha(node: statusNode, alpha: 1.0 - factor)
|
transition.updateAlpha(node: statusNode, alpha: 1.0 - factor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.imageNode.imageUpdated = { [weak self] image in
|
|
||||||
guard let self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.timestampMaskView?.image = image
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -794,6 +787,66 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct MaskImageCornerKey: Hashable {
|
||||||
|
var bottomLeft: CGFloat
|
||||||
|
var bottomRight: CGFloat
|
||||||
|
var leftInset: CGFloat
|
||||||
|
var rightInset: CGFloat
|
||||||
|
|
||||||
|
init(bottomLeft: CGFloat, bottomRight: CGFloat, leftInset: CGFloat, rightInset: CGFloat) {
|
||||||
|
self.bottomLeft = bottomLeft
|
||||||
|
self.bottomRight = bottomRight
|
||||||
|
self.leftInset = leftInset
|
||||||
|
self.rightInset = rightInset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static var timestampMaskImageCache: [MaskImageCornerKey: UIImage] = [:]
|
||||||
|
|
||||||
|
private func generateTimestampMaskImage(corners: ImageCorners) -> UIImage? {
|
||||||
|
var insets = corners.extendedEdges
|
||||||
|
insets.top = 0.0
|
||||||
|
insets.bottom = 0.0
|
||||||
|
|
||||||
|
let cacheKey = MaskImageCornerKey(bottomLeft: corners.bottomLeft.radius, bottomRight: corners.bottomRight.radius, leftInset: insets.left, rightInset: insets.right)
|
||||||
|
if let image = ChatMessageInteractiveMediaNode.timestampMaskImageCache[cacheKey] {
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
|
let imageSize = CGSize(width: corners.bottomLeft.radius + corners.bottomRight.radius + insets.left + insets.right + 1.0, height: 1.0 + max(corners.bottomLeft.radius, corners.bottomRight.radius))
|
||||||
|
|
||||||
|
guard let context = DrawingContext(size: imageSize, clear: true) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
context.withContext { c in
|
||||||
|
c.setFillColor(UIColor.white.cgColor)
|
||||||
|
c.move(to: CGPoint(x: insets.left, y: insets.top))
|
||||||
|
c.addLine(to: CGPoint(x: insets.left, y: imageSize.height - insets.bottom - corners.bottomLeft.radius))
|
||||||
|
c.addArc(tangent1End: CGPoint(x: insets.left, y: imageSize.height - insets.bottom), tangent2End: CGPoint(x: insets.left + corners.bottomLeft.radius, y: imageSize.height - insets.bottom), radius: corners.bottomLeft.radius)
|
||||||
|
c.addLine(to: CGPoint(x: imageSize.width - insets.right - corners.bottomRight.radius, y: imageSize.height - insets.bottom))
|
||||||
|
c.addArc(tangent1End: CGPoint(x: imageSize.width - insets.right, y: imageSize.height - insets.bottom), tangent2End: CGPoint(x: imageSize.width - insets.right, y: imageSize.height - insets.bottom - corners.bottomRight.radius), radius: corners.bottomRight.radius)
|
||||||
|
c.addLine(to: CGPoint(x: imageSize.width - insets.right, y: insets.top))
|
||||||
|
c.closePath()
|
||||||
|
c.fillPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
let image = context.generateImage()?.resizableImage(
|
||||||
|
withCapInsets: UIEdgeInsets(
|
||||||
|
top: 0,
|
||||||
|
left: corners.bottomLeft.radius + insets.left,
|
||||||
|
bottom: imageSize.height - 1.0,
|
||||||
|
right: corners.bottomRight.radius + insets.right
|
||||||
|
),
|
||||||
|
resizingMode: .stretch
|
||||||
|
)
|
||||||
|
|
||||||
|
if let image {
|
||||||
|
ChatMessageInteractiveMediaNode.timestampMaskImageCache[cacheKey] = image
|
||||||
|
}
|
||||||
|
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
public func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ dateTimeFormat: PresentationDateTimeFormat, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ media: Media, _ mediaIndex: Int?, _ dateAndStatus: ChatMessageDateAndStatus?, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: MediaAutoDownloadPeerType, _ peerId: EnginePeer.Id?, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode, _ presentationContext: ChatPresentationContext) -> (CGSize, CGFloat, (CGSize, Bool, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) {
|
public func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ dateTimeFormat: PresentationDateTimeFormat, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ media: Media, _ mediaIndex: Int?, _ dateAndStatus: ChatMessageDateAndStatus?, _ automaticDownload: InteractiveMediaNodeAutodownloadMode, _ peerType: MediaAutoDownloadPeerType, _ peerId: EnginePeer.Id?, _ sizeCalculation: InteractiveMediaNodeSizeCalculation, _ layoutConstants: ChatMessageItemLayoutConstants, _ contentMode: InteractiveMediaNodeContentMode, _ presentationContext: ChatPresentationContext) -> (CGSize, CGFloat, (CGSize, Bool, Bool, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) {
|
||||||
let currentMessage = self.message
|
let currentMessage = self.message
|
||||||
let currentMedia = self.media
|
let currentMedia = self.media
|
||||||
@ -1771,6 +1824,9 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
strongSelf.pinchContainerNode.update(size: imageFrame.size, transition: .immediate)
|
strongSelf.pinchContainerNode.update(size: imageFrame.size, transition: .immediate)
|
||||||
strongSelf.imageNode.frame = CGRect(origin: CGPoint(), size: imageFrame.size)
|
strongSelf.imageNode.frame = CGRect(origin: CGPoint(), size: imageFrame.size)
|
||||||
}
|
}
|
||||||
|
if strongSelf.currentImageArguments?.corners != arguments.corners, let timestampMaskView = strongSelf.timestampMaskView {
|
||||||
|
timestampMaskView.image = strongSelf.generateTimestampMaskImage(corners: arguments.corners)
|
||||||
|
}
|
||||||
strongSelf.currentImageArguments = arguments
|
strongSelf.currentImageArguments = arguments
|
||||||
imageApply()
|
imageApply()
|
||||||
|
|
||||||
@ -1950,8 +2006,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:wip-release
|
var videoTimestamp: Int32?
|
||||||
/*var videoTimestamp: Int32?
|
|
||||||
var storedVideoTimestamp: Int32?
|
var storedVideoTimestamp: Int32?
|
||||||
for attribute in message.attributes {
|
for attribute in message.attributes {
|
||||||
if let attribute = attribute as? ForwardVideoTimestampAttribute {
|
if let attribute = attribute as? ForwardVideoTimestampAttribute {
|
||||||
@ -1985,7 +2040,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
strongSelf.timestampMaskView = timestampMaskView
|
strongSelf.timestampMaskView = timestampMaskView
|
||||||
timestampContainerView.mask = timestampMaskView
|
timestampContainerView.mask = timestampMaskView
|
||||||
|
|
||||||
timestampMaskView.image = strongSelf.imageNode.image
|
timestampMaskView.image = strongSelf.generateTimestampMaskImage(corners: arguments.corners)
|
||||||
}
|
}
|
||||||
|
|
||||||
let videoTimestampBackgroundLayer: SimpleLayer
|
let videoTimestampBackgroundLayer: SimpleLayer
|
||||||
@ -2038,7 +2093,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
strongSelf.videoTimestampForegroundLayer = nil
|
strongSelf.videoTimestampForegroundLayer = nil
|
||||||
videoTimestampForegroundLayer.removeFromSuperlayer()
|
videoTimestampForegroundLayer.removeFromSuperlayer()
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
if let animatedStickerNode = strongSelf.animatedStickerNode {
|
if let animatedStickerNode = strongSelf.animatedStickerNode {
|
||||||
animatedStickerNode.frame = imageFrame
|
animatedStickerNode.frame = imageFrame
|
||||||
@ -3037,6 +3092,72 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func scrubberTransition() -> GalleryItemScrubberTransition? {
|
||||||
|
if let timestampContainerView = self.timestampContainerView, let timestampMaskView = self.timestampMaskView, let videoTimestampBackgroundLayer = self.videoTimestampBackgroundLayer, let videoTimestampForegroundLayer = self.videoTimestampForegroundLayer {
|
||||||
|
final class TimestampContainerTransitionView: UIView {
|
||||||
|
let containerView: UIView
|
||||||
|
let containerMaskView: UIImageView
|
||||||
|
let backgroundLayer: SimpleLayer
|
||||||
|
let foregroundLayer: SimpleLayer
|
||||||
|
let fraction: CGFloat
|
||||||
|
|
||||||
|
init(timestampContainerView: UIView?, timestampMaskView: UIImageView?, videoTimestampBackgroundLayer: SimpleLayer?, videoTimestampForegroundLayer: SimpleLayer?) {
|
||||||
|
self.containerView = UIView()
|
||||||
|
self.containerMaskView = UIImageView()
|
||||||
|
self.backgroundLayer = SimpleLayer()
|
||||||
|
self.foregroundLayer = SimpleLayer()
|
||||||
|
|
||||||
|
if let videoTimestampBackgroundLayer, let videoTimestampForegroundLayer {
|
||||||
|
self.fraction = videoTimestampForegroundLayer.bounds.width / videoTimestampBackgroundLayer.bounds.width
|
||||||
|
} else {
|
||||||
|
self.fraction = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
super.init(frame: CGRect())
|
||||||
|
|
||||||
|
self.addSubview(self.containerView)
|
||||||
|
|
||||||
|
self.containerView.mask = self.containerMaskView
|
||||||
|
self.containerMaskView.image = timestampMaskView?.image
|
||||||
|
|
||||||
|
self.containerView.layer.addSublayer(self.backgroundLayer)
|
||||||
|
self.containerView.layer.addSublayer(self.foregroundLayer)
|
||||||
|
|
||||||
|
self.backgroundLayer.backgroundColor = videoTimestampBackgroundLayer?.backgroundColor
|
||||||
|
self.foregroundLayer.backgroundColor = videoTimestampForegroundLayer?.backgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(state: GalleryItemScrubberTransition.TransitionState, transition: ContainedViewLayoutTransition) {
|
||||||
|
let containerFrame = CGRect(origin: CGPoint(), size: state.sourceSize.interpolate(to: state.destinationSize, amount: state.progress))
|
||||||
|
transition.updateFrame(view: self.containerView, frame: containerFrame)
|
||||||
|
transition.updateFrame(view: self.containerMaskView, frame: CGRect(origin: CGPoint(), size: containerFrame.size))
|
||||||
|
|
||||||
|
transition.updateFrame(layer: self.backgroundLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: containerFrame.height - 3.0), size: CGSize(width: containerFrame.width, height: 3.0)))
|
||||||
|
transition.updateFrame(layer: self.foregroundLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: containerFrame.height - 3.0), size: CGSize(width: containerFrame.width * self.fraction, height: 3.0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GalleryItemScrubberTransition(
|
||||||
|
view: timestampContainerView,
|
||||||
|
makeView: { [weak timestampContainerView, weak timestampMaskView, weak videoTimestampBackgroundLayer, weak videoTimestampForegroundLayer] in
|
||||||
|
return TimestampContainerTransitionView(timestampContainerView: timestampContainerView, timestampMaskView: timestampMaskView, videoTimestampBackgroundLayer: videoTimestampBackgroundLayer, videoTimestampForegroundLayer: videoTimestampForegroundLayer)
|
||||||
|
},
|
||||||
|
updateView: { view, state, transition in
|
||||||
|
if let view = view as? TimestampContainerTransitionView {
|
||||||
|
view.update(state: state, transition: transition)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
insertCloneTransitionView: nil
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func playMediaWithSound() -> (action: (Double?) -> Void, soundEnabled: Bool, isVideoMessage: Bool, isUnread: Bool, badgeNode: ASDisplayNode?)? {
|
public func playMediaWithSound() -> (action: (Double?) -> Void, soundEnabled: Bool, isVideoMessage: Bool, isUnread: Bool, badgeNode: ASDisplayNode?)? {
|
||||||
var isAnimated = false
|
var isAnimated = false
|
||||||
if let file = self.media as? TelegramMediaFile {
|
if let file = self.media as? TelegramMediaFile {
|
||||||
|
@ -176,7 +176,7 @@ public func presentPeerReportOptions(
|
|||||||
var message = ""
|
var message = ""
|
||||||
var items: [ActionSheetItem] = []
|
var items: [ActionSheetItem] = []
|
||||||
items.append(ReportPeerHeaderActionSheetItem(context: context, text: presentationData.strings.Report_AdditionalDetailsText))
|
items.append(ReportPeerHeaderActionSheetItem(context: context, text: presentationData.strings.Report_AdditionalDetailsText))
|
||||||
items.append(ReportPeerDetailsActionSheetItem(context: context, theme: presentationData.theme, placeholderText: presentationData.strings.Report_AdditionalDetailsPlaceholder, textUpdated: { text in
|
items.append(ReportPeerDetailsActionSheetItem(context: context, theme: presentationData.theme, strings: presentationData.strings, placeholderText: presentationData.strings.Report_AdditionalDetailsPlaceholder, textUpdated: { text in
|
||||||
message = text
|
message = text
|
||||||
}))
|
}))
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Report_Report, color: .accent, font: .bold, enabled: true, action: {
|
items.append(ActionSheetButtonItem(title: presentationData.strings.Report_Report, color: .accent, font: .bold, enabled: true, action: {
|
||||||
@ -309,7 +309,7 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
|
|||||||
var message = ""
|
var message = ""
|
||||||
var items: [ActionSheetItem] = []
|
var items: [ActionSheetItem] = []
|
||||||
items.append(ReportPeerHeaderActionSheetItem(context: context, text: presentationData.strings.Report_AdditionalDetailsText))
|
items.append(ReportPeerHeaderActionSheetItem(context: context, text: presentationData.strings.Report_AdditionalDetailsText))
|
||||||
items.append(ReportPeerDetailsActionSheetItem(context: context, theme: presentationData.theme, placeholderText: presentationData.strings.Report_AdditionalDetailsPlaceholder, textUpdated: { text in
|
items.append(ReportPeerDetailsActionSheetItem(context: context, theme: presentationData.theme, strings: presentationData.strings, placeholderText: presentationData.strings.Report_AdditionalDetailsPlaceholder, textUpdated: { text in
|
||||||
message = text
|
message = text
|
||||||
}))
|
}))
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Report_Report, color: .accent, font: .bold, enabled: true, action: {
|
items.append(ActionSheetButtonItem(title: presentationData.strings.Report_Report, color: .accent, font: .bold, enabled: true, action: {
|
||||||
|
@ -11,18 +11,20 @@ import AppBundle
|
|||||||
public final class ReportPeerDetailsActionSheetItem: ActionSheetItem {
|
public final class ReportPeerDetailsActionSheetItem: ActionSheetItem {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
let strings: PresentationStrings
|
||||||
let placeholderText: String
|
let placeholderText: String
|
||||||
let textUpdated: (String) -> Void
|
let textUpdated: (String) -> Void
|
||||||
|
|
||||||
public init(context: AccountContext, theme: PresentationTheme, placeholderText: String, textUpdated: @escaping (String) -> Void) {
|
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, placeholderText: String, textUpdated: @escaping (String) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
self.placeholderText = placeholderText
|
self.placeholderText = placeholderText
|
||||||
self.textUpdated = textUpdated
|
self.textUpdated = textUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
public func node(theme: ActionSheetControllerTheme) -> ActionSheetItemNode {
|
public func node(theme: ActionSheetControllerTheme) -> ActionSheetItemNode {
|
||||||
return ReportPeerDetailsActionSheetItemNode(theme: theme, presentationTheme: self.theme, context: self.context, placeholderText: self.placeholderText, textUpdated: self.textUpdated)
|
return ReportPeerDetailsActionSheetItemNode(theme: theme, presentationTheme: self.theme, strings: self.strings, context: self.context, placeholderText: self.placeholderText, textUpdated: self.textUpdated)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateNode(_ node: ActionSheetItemNode) {
|
public func updateNode(_ node: ActionSheetItemNode) {
|
||||||
@ -36,10 +38,10 @@ private final class ReportPeerDetailsActionSheetItemNode: ActionSheetItemNode {
|
|||||||
|
|
||||||
private let accessibilityArea: AccessibilityAreaNode
|
private let accessibilityArea: AccessibilityAreaNode
|
||||||
|
|
||||||
init(theme: ActionSheetControllerTheme, presentationTheme: PresentationTheme, context: AccountContext, placeholderText: String, textUpdated: @escaping (String) -> Void) {
|
init(theme: ActionSheetControllerTheme, presentationTheme: PresentationTheme, strings: PresentationStrings, context: AccountContext, placeholderText: String, textUpdated: @escaping (String) -> Void) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
|
||||||
self.inputFieldNode = ShareInputFieldNode(theme: ShareInputFieldNodeTheme(presentationTheme: presentationTheme), placeholder: placeholderText)
|
self.inputFieldNode = ShareInputFieldNode(theme: ShareInputFieldNodeTheme(presentationTheme: presentationTheme), strings: strings, placeholder: placeholderText)
|
||||||
|
|
||||||
self.accessibilityArea = AccessibilityAreaNode()
|
self.accessibilityArea = AccessibilityAreaNode()
|
||||||
|
|
||||||
@ -58,7 +60,7 @@ private final class ReportPeerDetailsActionSheetItemNode: ActionSheetItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override func updateLayout(constrainedSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
public override func updateLayout(constrainedSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
let inputHeight = self.inputFieldNode.updateLayout(width: constrainedSize.width, transition: .immediate)
|
let inputHeight = self.inputFieldNode.updateLayout(width: constrainedSize.width, inputCopyText: nil, transition: .immediate)
|
||||||
self.inputFieldNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: constrainedSize.width, height: inputHeight))
|
self.inputFieldNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: constrainedSize.width, height: inputHeight))
|
||||||
|
|
||||||
let size = CGSize(width: constrainedSize.width, height: inputHeight)
|
let size = CGSize(width: constrainedSize.width, height: inputHeight)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user