Shared media improvements

This commit is contained in:
Ali 2021-10-22 01:41:39 +04:00
parent 205211d5ba
commit b5d1b377f2
9 changed files with 442 additions and 91 deletions

View File

@ -10,12 +10,12 @@ import TelegramPresentationData
import ComponentFlow
import PhotoResources
private final class MediaPreviewNode: ASDisplayNode {
private final class MediaPreviewView: UIView {
private let context: AccountContext
private let message: EngineMessage
private let media: EngineMedia
private let imageNode: TransformImageNode
private let imageView: TransformImageView
private var requestedImage: Bool = false
private var disposable: Disposable?
@ -25,11 +25,15 @@ private final class MediaPreviewNode: ASDisplayNode {
self.message = message
self.media = media
self.imageNode = TransformImageNode()
self.imageView = TransformImageView()
super.init()
super.init(frame: CGRect())
self.addSubnode(self.imageNode)
self.addSubview(self.imageView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
@ -44,7 +48,7 @@ private final class MediaPreviewNode: ASDisplayNode {
if !self.requestedImage {
self.requestedImage = true
let signal = mediaGridMessagePhoto(account: self.context.account, photoReference: .message(message: MessageReference(self.message._asMessage()), media: image), fullRepresentationSize: CGSize(width: 36.0, height: 36.0), synchronousLoad: synchronousLoads)
self.imageNode.setSignal(signal, attemptSynchronously: synchronousLoads)
self.imageView.setSignal(signal, attemptSynchronously: synchronousLoads)
}
}
} else if case let .file(file) = self.media {
@ -53,13 +57,13 @@ private final class MediaPreviewNode: ASDisplayNode {
if !self.requestedImage {
self.requestedImage = true
let signal = mediaGridMessageVideo(postbox: self.context.account.postbox, videoReference: .message(message: MessageReference(self.message._asMessage()), media: file), synchronousLoad: synchronousLoads, autoFetchFullSizeThumbnail: true, useMiniThumbnailIfAvailable: true)
self.imageNode.setSignal(signal, attemptSynchronously: synchronousLoads)
self.imageView.setSignal(signal, attemptSynchronously: synchronousLoads)
}
}
}
let makeLayout = self.imageNode.asyncLayout()
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
let makeLayout = self.imageView.asyncLayout()
self.imageView.frame = CGRect(origin: CGPoint(), size: size)
let apply = makeLayout(TransformImageArguments(corners: ImageCorners(radius: size.width / 2.0), imageSize: dimensions.aspectFilled(size), boundingSize: size, intrinsicInsets: UIEdgeInsets()))
apply()
}
@ -152,6 +156,13 @@ private final class ImageCache: Equatable {
var color: UInt32
}
private struct Text: Hashable {
var fontSize: CGFloat
var isSemibold: Bool
var color: UInt32
var string: String
}
private var items: [AnyHashable: UIImage] = [:]
func filledCircle(diameter: CGFloat, color: UIColor) -> UIImage {
@ -169,6 +180,31 @@ private final class ImageCache: Equatable {
self.items[key] = image
return image
}
func text(fontSize: CGFloat, isSemibold: Bool, color: UIColor, string: String) -> UIImage {
let key = AnyHashable(Text(fontSize: fontSize, isSemibold: isSemibold, color: color.argb, string: string))
if let image = self.items[key] {
return image
}
let font: UIFont
if isSemibold {
font = Font.semibold(fontSize)
} else {
font = Font.regular(fontSize)
}
let attributedString = NSAttributedString(string: string, font: font, textColor: color)
let rect = attributedString.boundingRect(with: CGSize(width: 1000.0, height: 1000.0), options: .usesLineFragmentOrigin, context: nil)
let image = generateImage(CGSize(width: ceil(rect.width), height: ceil(rect.height)), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
attributedString.draw(in: rect)
UIGraphicsPopContext()
})!
self.items[key] = image
return image
}
}
private final class DayComponent: Component {
@ -223,28 +259,28 @@ private final class DayComponent: Component {
}
final class View: UIView {
private let buttonNode: HighlightableButtonNode
private let button: HighlightableButton
private let highlightNode: ASImageNode
private let titleNode: ImmediateTextNode
private var mediaPreviewNode: MediaPreviewNode?
private let highlightView: UIImageView
private let titleView: UIImageView
private var mediaPreviewView: MediaPreviewView?
private var action: (() -> Void)?
private var currentMedia: DayMedia?
init() {
self.buttonNode = HighlightableButtonNode()
self.highlightNode = ASImageNode()
self.titleNode = ImmediateTextNode()
self.button = HighlightableButton()
self.highlightView = UIImageView()
self.titleView = UIImageView()
super.init(frame: CGRect())
self.buttonNode.addSubnode(self.highlightNode)
self.buttonNode.addSubnode(self.titleNode)
self.button.addSubview(self.highlightView)
self.button.addSubview(self.titleView)
self.addSubnode(self.buttonNode)
self.addSubview(self.button)
self.buttonNode.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
self.button.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
@ -258,58 +294,64 @@ private final class DayComponent: Component {
func update(component: DayComponent, availableSize: CGSize, environment: Environment<ImageCache>, transition: Transition) -> CGSize {
self.action = component.action
let shadowInset: CGFloat = 0.0
let diameter = min(availableSize.width, availableSize.height)
let contentFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - diameter) / 2.0), y: floor((availableSize.height - diameter) / 2.0)), size: CGSize(width: diameter, height: diameter))
let imageCache = environment[ImageCache.self]
if component.media != nil {
self.highlightNode.image = imageCache.value.filledCircle(diameter: diameter, color: UIColor(white: 0.0, alpha: 0.2))
self.highlightView.image = imageCache.value.filledCircle(diameter: diameter, color: UIColor(white: 0.0, alpha: 0.2))
} else if component.isCurrent {
self.highlightNode.image = imageCache.value.filledCircle(diameter: diameter, color: component.theme.list.itemAccentColor)
self.highlightView.image = imageCache.value.filledCircle(diameter: diameter, color: component.theme.list.itemAccentColor)
} else {
self.highlightNode.image = nil
self.highlightView.image = nil
}
if self.currentMedia != component.media {
self.currentMedia = component.media
if let mediaPreviewNode = self.mediaPreviewNode {
self.mediaPreviewNode = nil
mediaPreviewNode.removeFromSupernode()
if let mediaPreviewView = self.mediaPreviewView {
self.mediaPreviewView = nil
mediaPreviewView.removeFromSuperview()
}
if let media = component.media {
let mediaPreviewNode = MediaPreviewNode(context: component.context, message: media.message, media: media.media)
self.mediaPreviewNode = mediaPreviewNode
self.buttonNode.insertSubnode(mediaPreviewNode, belowSubnode: self.highlightNode)
let mediaPreviewView = MediaPreviewView(context: component.context, message: media.message, media: media.media)
self.mediaPreviewView = mediaPreviewView
self.button.insertSubview(mediaPreviewView, belowSubview: self.highlightView)
}
}
let titleColor: UIColor
let titleFont: UIFont
let titleFontSize: CGFloat
let titleFontIsSemibold: Bool
if component.isCurrent || component.media != nil {
titleColor = component.theme.list.itemCheckColors.foregroundColor
titleFont = Font.semibold(17.0)
titleFontSize = 17.0
titleFontIsSemibold = true
} else if component.isEnabled {
titleColor = component.theme.list.itemPrimaryTextColor
titleFont = Font.regular(17.0)
titleFontSize = 17.0
titleFontIsSemibold = false
} else {
titleColor = component.theme.list.itemDisabledTextColor
titleFont = Font.regular(17.0)
titleFontSize = 17.0
titleFontIsSemibold = false
}
self.titleNode.attributedText = NSAttributedString(string: component.title, font: titleFont, textColor: titleColor)
let titleSize = self.titleNode.updateLayout(availableSize)
transition.setFrame(view: self.highlightNode.view, frame: CGRect(origin: CGPoint(x: -shadowInset, y: -shadowInset), size: CGSize(width: availableSize.width + shadowInset * 2.0, height: availableSize.height + shadowInset * 2.0)))
let titleImage = imageCache.value.text(fontSize: titleFontSize, isSemibold: titleFontIsSemibold, color: titleColor, string: component.title)
self.titleView.image = titleImage
let titleSize = titleImage.size
self.titleNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: floor((availableSize.height - titleSize.height) / 2.0)), size: titleSize)
transition.setFrame(view: self.highlightView, frame: contentFrame)
self.buttonNode.frame = CGRect(origin: CGPoint(), size: availableSize)
self.buttonNode.isEnabled = component.isEnabled && component.media != nil
self.titleView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: floor((availableSize.height - titleSize.height) / 2.0)), size: titleSize)
if let mediaPreviewNode = self.mediaPreviewNode {
mediaPreviewNode.frame = CGRect(origin: CGPoint(), size: availableSize)
mediaPreviewNode.updateLayout(size: availableSize, synchronousLoads: false)
self.button.frame = CGRect(origin: CGPoint(), size: availableSize)
self.button.isEnabled = component.isEnabled && component.media != nil
if let mediaPreviewView = self.mediaPreviewView {
mediaPreviewView.frame = contentFrame
mediaPreviewView.updateLayout(size: contentFrame.size, synchronousLoads: false)
}
return availableSize
@ -382,6 +424,7 @@ private final class MonthComponent: CombinedComponent {
let weekdaySize: CGFloat = 46.0
let weekdaySpacing: CGFloat = 6.0
let usableWeekdayWidth = floor((context.availableSize.width - sideInset * 2.0 - weekdaySpacing * 6.0) / 7.0)
let weekdayWidth = floor((context.availableSize.width - sideInset * 2.0) / 7.0)
let title = title.update(
@ -440,7 +483,7 @@ private final class MonthComponent: CombinedComponent {
environment: {
context.environment[ImageCache.self]
},
availableSize: CGSize(width: weekdaySize, height: weekdaySize),
availableSize: CGSize(width: usableWeekdayWidth, height: weekdaySize),
transition: .immediate
)
}
@ -843,7 +886,7 @@ public final class CalendarMessageScreen: ViewController {
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(dismissPressed)), animated: false)
//TODO:localize
self.navigationItem.setTitle("Jump to Date", animated: false)
self.navigationItem.setTitle("Calendar", animated: false)
}
required public init(coder aDecoder: NSCoder) {

View File

@ -206,3 +206,183 @@ open class TransformImageNode: ASDisplayNode {
}
}
}
open class TransformImageView: UIView {
public var imageUpdated: ((UIImage?) -> Void)?
public var contentAnimations: TransformImageNodeContentAnimations = []
private var disposable = MetaDisposable()
private var currentTransform: ((TransformImageArguments) -> DrawingContext?)?
private var currentArguments: TransformImageArguments?
private var argumentsPromise = ValuePromise<TransformImageArguments>(ignoreRepeated: true)
private var overlayColor: UIColor?
private var overlayView: UIView?
override public init(frame: CGRect) {
super.init(frame: frame)
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.accessibilityIgnoresInvertColors = true
}
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.disposable.dispose()
}
override open var frame: CGRect {
didSet {
if let overlayView = self.overlayView {
overlayView.frame = self.bounds
}
}
}
public func reset() {
self.disposable.set(nil)
self.currentArguments = nil
self.currentTransform = nil
self.layer.contents = nil
}
public func setSignal(_ signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>, attemptSynchronously: Bool = false, dispatchOnDisplayLink: Bool = true) {
let argumentsPromise = self.argumentsPromise
let data = combineLatest(signal, argumentsPromise.get())
let resultData: Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments), NoError>
if attemptSynchronously {
resultData = data
} else {
resultData = data
|> deliverOn(Queue.concurrentDefaultQueue())
}
let result = resultData
|> mapToThrottled { transform, arguments -> Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments, UIImage?)?, NoError> in
return deferred {
if let context = transform(arguments) {
return .single((transform, arguments, context.generateImage()))
} else {
return .single(nil)
}
}
}
self.disposable.set((result |> deliverOnMainQueue).start(next: { [weak self] next in
let apply: () -> Void = {
if let strongSelf = self {
if strongSelf.layer.contents == nil {
if strongSelf.contentAnimations.contains(.firstUpdate) && !attemptSynchronously {
strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
}
} else if strongSelf.contentAnimations.contains(.subsequentUpdates) {
let tempLayer = CALayer()
tempLayer.frame = strongSelf.bounds
tempLayer.contentsGravity = strongSelf.layer.contentsGravity
tempLayer.contents = strongSelf.layer.contents
strongSelf.layer.addSublayer(tempLayer)
tempLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak tempLayer] _ in
tempLayer?.removeFromSuperlayer()
})
}
var imageUpdate: UIImage?
if let (transform, arguments, image) = next {
strongSelf.currentTransform = transform
strongSelf.currentArguments = arguments
strongSelf.layer.contents = image?.cgImage
imageUpdate = image
}
if let _ = strongSelf.overlayColor {
strongSelf.applyOverlayColor(animated: false)
}
if let imageUpdated = strongSelf.imageUpdated {
imageUpdated(imageUpdate)
}
}
}
if dispatchOnDisplayLink && !attemptSynchronously {
displayLinkDispatcher.dispatch {
apply()
}
} else {
apply()
}
}))
}
public func asyncLayout() -> (TransformImageArguments) -> (() -> Void) {
let currentTransform = self.currentTransform
let currentArguments = self.currentArguments
return { [weak self] arguments in
let updatedImage: UIImage?
if currentArguments != arguments {
updatedImage = currentTransform?(arguments)?.generateImage()
} else {
updatedImage = nil
}
return {
guard let strongSelf = self else {
return
}
if let image = updatedImage {
strongSelf.layer.contents = image.cgImage
strongSelf.currentArguments = arguments
if let _ = strongSelf.overlayColor {
strongSelf.applyOverlayColor(animated: false)
}
}
strongSelf.argumentsPromise.set(arguments)
}
}
}
public func setOverlayColor(_ color: UIColor?, animated: Bool) {
var updated = false
if let overlayColor = self.overlayColor, let color = color {
updated = !overlayColor.isEqual(color)
} else if (self.overlayColor != nil) != (color != nil) {
updated = true
}
if updated {
self.overlayColor = color
if let _ = self.overlayColor {
self.applyOverlayColor(animated: animated)
} else if let overlayView = self.overlayView {
self.overlayView = nil
if animated {
overlayView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak overlayView] _ in
overlayView?.removeFromSuperview()
})
} else {
overlayView.removeFromSuperview()
}
}
}
}
private func applyOverlayColor(animated: Bool) {
if let overlayColor = self.overlayColor {
if let contents = self.layer.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID {
if let overlayView = self.overlayView {
(overlayView as! UIImageView).image = UIImage(cgImage: contents as! CGImage).withRenderingMode(.alwaysTemplate)
overlayView.tintColor = overlayColor
} else {
let overlayView = UIImageView()
overlayView.image = UIImage(cgImage: contents as! CGImage).withRenderingMode(.alwaysTemplate)
overlayView.tintColor = overlayColor
overlayView.frame = self.bounds
self.addSubview(overlayView)
self.overlayView = overlayView
}
}
}
}
}

View File

@ -29,7 +29,7 @@ public protocol SparseItemGridDisplayItem: AnyObject {
public protocol SparseItemGridBinding: AnyObject {
func createLayer() -> SparseItemGridLayer?
func createView() -> SparseItemGridView?
func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem])
func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: Bool)
func unbindLayer(layer: SparseItemGridLayer)
func scrollerTextForTag(tag: Int32) -> String?
func loadHole(anchor: SparseItemGrid.HoleAnchor, at location: SparseItemGrid.HoleLocation) -> Signal<Never, NoError>
@ -464,7 +464,7 @@ public final class SparseItemGrid: ASDisplayNode {
self.layout = Layout(containerLayout: containerLayout, zoomLevel: self.zoomLevel)
self.items = items
self.updateVisibleItems(resetScrolling: true, restoreScrollPosition: restoreScrollPosition)
self.updateVisibleItems(resetScrolling: true, synchronous: false, restoreScrollPosition: restoreScrollPosition)
}
}
@ -474,7 +474,7 @@ public final class SparseItemGrid: ASDisplayNode {
@objc func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !self.ignoreScrolling {
self.updateVisibleItems(resetScrolling: false, restoreScrollPosition: nil)
self.updateVisibleItems(resetScrolling: false, synchronous: true, restoreScrollPosition: nil)
if let layout = self.layout, let items = self.items {
let offset = scrollView.contentOffset.y
@ -660,7 +660,7 @@ public final class SparseItemGrid: ASDisplayNode {
}
}
private func updateVisibleItems(resetScrolling: Bool, restoreScrollPosition: (y: CGFloat, index: Int)?) {
private func updateVisibleItems(resetScrolling: Bool, synchronous: Bool, restoreScrollPosition: (y: CGFloat, index: Int)?) {
guard let layout = self.layout, let items = self.items else {
return
}
@ -753,7 +753,7 @@ public final class SparseItemGrid: ASDisplayNode {
}
if !bindItems.isEmpty {
items.itemBinding.bindLayers(items: bindItems, layers: bindLayers)
items.itemBinding.bindLayers(items: bindItems, layers: bindLayers, synchronous: synchronous)
}
for item in updateLayers {

View File

@ -159,17 +159,28 @@ public final class TooltipComponent: Component {
}
public final class View: UIView {
private let backgroundNode: NavigationBackgroundNode
private let backgroundView: UIView
private let backgroundViewMask: UIImageView
private var icon: ComponentHostView<Empty>?
private let content: ComponentHostView<Empty>
init() {
self.backgroundNode = NavigationBackgroundNode(color: UIColor(white: 0.2, alpha: 0.7))
self.backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
self.backgroundViewMask = UIImageView()
self.backgroundViewMask.image = generateImage(CGSize(width: 42.0, height: 42.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.black.cgColor)
let _ = try? drawSvgPath(context, path: "M0,18.0252 C0,14.1279 0,12.1792 0.5358,10.609 C1.5362,7.6772 3.8388,5.3746 6.7706,4.3742 C8.3409,3.8384 10.2895,3.8384 14.1868,3.8384 L16.7927,3.8384 C18.2591,3.8384 18.9923,3.8384 19.7211,3.8207 C25.1911,3.6877 30.6172,2.8072 35.8485,1.2035 C36.5454,0.9899 37.241,0.758 38.6321,0.2943 C39.1202,0.1316 39.3643,0.0503 39.5299,0.0245 C40.8682,-0.184 42.0224,0.9702 41.8139,2.3085 C41.7881,2.4741 41.7067,2.7181 41.544,3.2062 C41.0803,4.5974 40.8485,5.293 40.6348,5.99 C39.0312,11.2213 38.1507,16.6473 38.0177,22.1173 C38,22.846 38,23.5793 38,25.0457 L38,27.6516 C38,31.5489 38,33.4975 37.4642,35.0677 C36.4638,37.9995 34.1612,40.3022 31.2294,41.3026 C29.6591,41.8384 27.7105,41.8384 23.8132,41.8384 L16,41.8384 C10.3995,41.8384 7.5992,41.8384 5.4601,40.7484 C3.5785,39.7897 2.0487,38.2599 1.0899,36.3783 C0,34.2392 0,31.4389 0,25.8384 L0,18.0252 Z ")
})?.stretchableImage(withLeftCapWidth: 16, topCapHeight: 34)
self.content = ComponentHostView<Empty>()
super.init(frame: CGRect())
self.addSubnode(self.backgroundNode)
self.addSubview(self.backgroundView)
self.backgroundView.mask = self.backgroundViewMask
self.addSubview(self.content)
}
@ -228,8 +239,10 @@ public final class TooltipComponent: Component {
contentRect.origin.y = component.arrowLocation.minY - contentRect.height
}
transition.setFrame(view: self.backgroundNode.view, frame: contentRect)
self.backgroundNode.update(size: contentRect.size, cornerRadius: 8.0, transition: .immediate)
let maskedBackgroundFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: contentRect.minY - 4.0), size: CGSize(width: contentRect.width + 4.0, height: contentRect.height + 4.0))
self.backgroundView.frame = maskedBackgroundFrame
self.backgroundViewMask.frame = CGRect(origin: CGPoint(), size: maskedBackgroundFrame.size)
if let iconSize = iconSize, let icon = self.icon {
transition.setFrame(view: icon, frame: CGRect(origin: CGPoint(x: contentRect.minX + insets.left, y: contentRect.minY + insets.top + floor((contentRect.height - insets.top - insets.bottom - iconSize.height) / 2.0)), size: iconSize))
@ -378,7 +391,7 @@ private final class ShadowRoundedRectangle: Component {
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(component.color.cgColor)
context.setShadow(offset: CGSize(width: 0.0, height: -2.0), blur: 5.0, color: UIColor(white: 0.0, alpha: 0.3).cgColor)
context.setShadow(offset: CGSize(width: 0.0, height: -1.0), blur: 4.0, color: UIColor(white: 0.0, alpha: 0.2).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset, y: shadowInset), size: CGSize(width: size.width - shadowInset * 2.0, height: size.height - shadowInset * 2.0)))
})?.stretchableImage(withLeftCapWidth: Int(diameter + shadowInset * 2.0) / 2, topCapHeight: Int(diameter + shadowInset * 2.0) / 2)
@ -752,6 +765,11 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
) {
self.containerSize = containerSize
if self.dateIndicator.alpha.isZero {
let transition: ContainedViewLayoutTransition = .immediate
transition.updateSublayerTransformOffset(layer: self.dateIndicator.layer, offset: CGPoint())
}
if isScrolling {
self.updateActivityTimer(isScrolling: true)
}
@ -781,15 +799,15 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
let topIndicatorInset: CGFloat = indicatorVerticalInset + containerInsets.top
let bottomIndicatorInset: CGFloat = indicatorVerticalInset + containerInsets.bottom
let scrollIndicatorHeight = max(35.0, ceil(scrollIndicatorHeightFraction * containerSize.height))
let scrollIndicatorHeight = max(44.0, ceil(scrollIndicatorHeightFraction * containerSize.height))
let indicatorPositionFraction = min(1.0, max(0.0, contentOffset / (contentHeight - containerSize.height)))
let indicatorTopPosition = topIndicatorInset
let indicatorBottomPosition = containerSize.height - bottomIndicatorInset - scrollIndicatorHeight
let dateIndicatorTopPosition = topIndicatorInset
let dateIndicatorBottomPosition = containerSize.height - bottomIndicatorInset - indicatorSize.height
let dateIndicatorTopPosition = topIndicatorInset + 4.0
let dateIndicatorBottomPosition = containerSize.height - bottomIndicatorInset - 4.0 - indicatorSize.height
self.indicatorPosition = indicatorTopPosition * (1.0 - indicatorPositionFraction) + indicatorBottomPosition * indicatorPositionFraction
self.scrollIndicatorHeight = scrollIndicatorHeight
@ -805,8 +823,9 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
transition.updateFrame(view: self.dateIndicator, frame: CGRect(origin: CGPoint(x: containerSize.width - 12.0 - indicatorSize.width, y: dateIndicatorPosition), size: indicatorSize))
if isScrolling {
self.dateIndicator.alpha = 1.0
self.lineIndicator.alpha = 1.0
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)
transition.updateAlpha(layer: self.dateIndicator.layer, alpha: 1.0)
transition.updateAlpha(layer: self.lineIndicator.layer, alpha: 1.0)
}
self.updateLineTooltip(containerSize: containerSize)
@ -823,7 +842,7 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
return
}
let lineIndicatorSize = CGSize(width: self.isDragging ? 6.0 : 3.0, height: scrollIndicatorHeight)
let lineIndicatorSize = CGSize(width: (self.isDragging || self.lineTooltip != nil) ? 6.0 : 3.0, height: scrollIndicatorHeight)
let mappedTransition: Transition
switch transition {
case .immediate:
@ -889,6 +908,9 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
lineTooltip.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
let transition: ContainedViewLayoutTransition = .immediate
transition.updateSublayerTransformOffset(layer: self.dateIndicator.layer, offset: CGPoint(x: -3.0, y: 0.0))
displayTooltip.completed()
}
@ -912,7 +934,7 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
font: Font.regular(13.0),
color: .white
)),
arrowLocation: self.lineIndicator.frame.insetBy(dx: -4.0, dy: -4.0)
arrowLocation: self.lineIndicator.frame.insetBy(dx: -3.0, dy: -8.0)
)),
environment: {},
containerSize: containerSize

View File

@ -922,9 +922,34 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
let historyView = (strongSelf.opaqueTransactionState as? ChatHistoryTransactionOpaqueState)?.historyView
let displayRange = strongSelf.displayedItemRange
if let filteredEntries = historyView?.filteredEntries, let visibleRange = displayRange.visibleRange {
let firstEntry = filteredEntries[filteredEntries.count - 1 - visibleRange.firstIndex]
strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Navigation(index: .message(firstEntry.index), anchorIndex: .message(firstEntry.index), count: historyMessageCount, highlight: false), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0)
var anchorIndex: MessageIndex?
loop: for index in visibleRange.firstIndex ..< filteredEntries.count {
switch filteredEntries[filteredEntries.count - 1 - index] {
case let .MessageEntry(message, _, _, _, _, _):
if message.adAttribute == nil {
anchorIndex = message.index
break loop
}
case let .MessageGroupEntry(_, messages, _):
for (message, _, _, _) in messages {
if message.adAttribute == nil {
anchorIndex = message.index
break loop
}
}
default:
break
}
}
if anchorIndex == nil, let historyView = historyView {
for entry in historyView.originalView.entries {
anchorIndex = entry.message.index
break
}
}
if let anchorIndex = anchorIndex {
strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Navigation(index: .message(anchorIndex), anchorIndex: .message(anchorIndex), count: historyMessageCount, highlight: false), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0)
}
} else {
if let subject = subject, case let .message(messageId, highlight, _) = subject {
strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(location: .id(messageId), count: 60, highlight: highlight), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0)

View File

@ -909,7 +909,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
return ItemView()
}
func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem]) {
func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: Bool) {
for i in 0 ..< items.count {
guard let item = items[i] as? VisualMediaItem else {
continue
@ -964,16 +964,19 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
if let image = result.image {
layer.contents = image.cgImage
layer.hasContents = true
if !synchronous {
layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
if let loadSignal = result.loadSignal {
let shimmerColor = self.getShimmerColors().background
layer.disposable = (loadSignal
|> deliverOnMainQueue).start(next: { [weak layer] image in
guard let layer = layer else {
return
}
let copyLayer = ItemLayer()
copyLayer.backgroundColor = UIColor(rgb: shimmerColor).cgColor
copyLayer.contents = layer.contents
copyLayer.contentsRect = layer.contentsRect
copyLayer.frame = layer.bounds
layer.addSublayer(copyLayer)
copyLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak copyLayer] _ in
@ -1560,7 +1563,17 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
)
if let (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams {
var gridSnapshot: UIView?
if reloadAtTop {
gridSnapshot = self.itemGrid.view.snapshotView(afterScreenUpdates: false)
}
self.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: false, transition: .immediate)
if let gridSnapshot = gridSnapshot {
self.view.addSubview(gridSnapshot)
gridSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak gridSnapshot] _ in
gridSnapshot?.removeFromSuperview()
})
}
}
if !self.didSetReady {

View File

@ -1666,6 +1666,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let subtitleNodeContainer: ASDisplayNode
let subtitleNodeRawContainer: ASDisplayNode
let subtitleNode: MultiScaleTextNode
let panelSubtitleNode: MultiScaleTextNode
let usernameNodeContainer: ASDisplayNode
let usernameNodeRawContainer: ASDisplayNode
let usernameNode: MultiScaleTextNode
@ -1721,6 +1722,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.subtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
self.subtitleNode.displaysAsynchronously = false
self.panelSubtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
self.panelSubtitleNode.displaysAsynchronously = false
self.usernameNodeContainer = ASDisplayNode()
self.usernameNodeRawContainer = ASDisplayNode()
self.usernameNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
@ -1770,6 +1774,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.titleNodeContainer.addSubnode(self.titleNode)
self.regularContentNode.addSubnode(self.titleNodeContainer)
self.subtitleNodeContainer.addSubnode(self.subtitleNode)
self.subtitleNodeContainer.addSubnode(self.panelSubtitleNode)
self.regularContentNode.addSubnode(self.subtitleNodeContainer)
self.regularContentNode.addSubnode(self.subtitleNodeRawContainer)
self.usernameNodeContainer.addSubnode(self.usernameNode)
@ -1899,7 +1904,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
var initializedCredibilityIcon = false
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: PeerInfoStatusData?, isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
self.state = state
self.peer = peer
self.avatarListNode.listContainerNode.peer = peer
@ -2019,6 +2024,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var isVerified = false
let titleString: NSAttributedString
let subtitleString: NSAttributedString
var panelSubtitleString: NSAttributedString?
let usernameString: NSAttributedString
if let peer = peer, peer.isVerified {
isVerified = true
@ -2063,6 +2069,16 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
subtitleString = NSAttributedString(string: statusData.text, font: Font.regular(15.0), textColor: subtitleColor)
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
if let panelStatusData = panelStatusData {
let subtitleColor: UIColor
if panelStatusData.isActivity {
subtitleColor = presentationData.theme.list.itemAccentColor
} else {
subtitleColor = presentationData.theme.list.itemSecondaryTextColor
}
panelSubtitleString = NSAttributedString(string: panelStatusData.text, font: Font.regular(15.0), textColor: subtitleColor)
}
} else {
subtitleString = NSAttributedString(string: " ", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
@ -2090,6 +2106,12 @@ final class PeerInfoHeaderNode: ASDisplayNode {
], mainState: TitleNodeStateRegular)
self.subtitleNode.accessibilityLabel = subtitleString.string
let panelSubtitleNodeLayout = self.panelSubtitleNode.updateLayout(states: [
TitleNodeStateRegular: MultiScaleTextState(attributedText: panelSubtitleString ?? subtitleString, constrainedSize: titleConstrainedSize),
TitleNodeStateExpanded: MultiScaleTextState(attributedText: panelSubtitleString ?? subtitleString, constrainedSize: CGSize(width: titleConstrainedSize.width - 82.0, height: titleConstrainedSize.height))
], mainState: TitleNodeStateRegular)
self.panelSubtitleNode.accessibilityLabel = (panelSubtitleString ?? subtitleString).string
let usernameNodeLayout = self.usernameNode.updateLayout(states: [
TitleNodeStateRegular: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)),
TitleNodeStateExpanded: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height))
@ -2102,6 +2124,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let titleSize = titleNodeLayout[TitleNodeStateRegular]!.size
let titleExpandedSize = titleNodeLayout[TitleNodeStateExpanded]!.size
let subtitleSize = subtitleNodeLayout[TitleNodeStateRegular]!.size
let _ = panelSubtitleNodeLayout[TitleNodeStateRegular]!.size
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
if let image = self.titleCredibilityIconNode.image {
@ -2151,17 +2174,53 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let apparentTitleLockOffset = (1.0 - titleCollapseFraction) * 0.0 + titleCollapseFraction * titleMaxLockOffset
let paneAreaExpansionDistance: CGFloat = 32.0
let effectiveAreaExpansionFraction: CGFloat
if state.isEditing {
effectiveAreaExpansionFraction = 0.0
} else if isSettings {
var paneAreaExpansionDelta = (self.frame.maxY - navigationHeight) - contentOffset
paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance))
effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance
} else {
var paneAreaExpansionDelta = (paneContainerY - navigationHeight) - contentOffset
paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance))
effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance
}
self.titleNode.update(stateFractions: [
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
], transition: transition)
let subtitleAlpha: CGFloat = self.isSettings ? 1.0 - titleCollapseFraction : 1.0
let subtitleAlpha: CGFloat
var subtitleOffset: CGFloat = 0.0
let panelSubtitleAlpha: CGFloat
var panelSubtitleOffset: CGFloat = 0.0
if self.isSettings {
subtitleAlpha = 1.0 - titleCollapseFraction
panelSubtitleAlpha = 0.0
} else {
if (panelSubtitleString ?? subtitleString).string != subtitleString.string {
subtitleAlpha = 1.0 - effectiveAreaExpansionFraction
panelSubtitleAlpha = effectiveAreaExpansionFraction
subtitleOffset = -effectiveAreaExpansionFraction * 5.0
panelSubtitleOffset = (1.0 - effectiveAreaExpansionFraction) * 5.0
} else {
subtitleAlpha = 1.0
panelSubtitleAlpha = 0.0
}
}
self.subtitleNode.update(stateFractions: [
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
], alpha: subtitleAlpha, transition: transition)
self.panelSubtitleNode.update(stateFractions: [
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
], alpha: panelSubtitleAlpha, transition: transition)
self.usernameNode.update(stateFractions: [
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
@ -2315,7 +2374,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let rawSubtitleFrame = CGRect(origin: CGPoint(x: subtitleCenter.x - subtitleFrame.size.width / 2.0, y: subtitleCenter.y - subtitleFrame.size.height / 2.0), size: subtitleFrame.size)
self.subtitleNodeRawContainer.frame = rawSubtitleFrame
transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize()))
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: subtitleOffset), size: CGSize()))
transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize()))
transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
transition.updateSublayerTransformScale(node: self.titleNodeContainer, scale: titleScale)
transition.updateSublayerTransformScale(node: self.subtitleNodeContainer, scale: subtitleScale)
@ -2353,7 +2413,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
usernameCenter.x = rawTitleFrame.center.x + (usernameCenter.x - rawTitleFrame.center.x) * subtitleScale
transition.updateFrameAdditiveToCenter(node: self.usernameNodeContainer, frame: CGRect(origin: usernameCenter, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
}
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: subtitleOffset), size: CGSize()))
transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize()))
transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
transition.updateSublayerTransformScaleAdditive(node: self.titleNodeContainer, scale: titleScale)
transition.updateSublayerTransformScaleAdditive(node: self.subtitleNodeContainer, scale: subtitleScale)

View File

@ -454,6 +454,7 @@ private final class PeerInfoPendingPane {
final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
private let context: AccountContext
private let peerId: PeerId
private let isMediaOnly: Bool
weak var parentController: ViewController?
@ -478,6 +479,11 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
}
}
private let currentPaneStatusPromise = Promise<PeerInfoStatusData?>(nil)
var currentPaneStatus: Signal<PeerInfoStatusData?, NoError> {
return self.currentPaneStatusPromise.get()
}
private var currentPanes: [PeerInfoPaneKey: PeerInfoPaneWrapper] = [:]
private var pendingPanes: [PeerInfoPaneKey: PeerInfoPendingPane] = [:]
@ -498,10 +504,11 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
private var currentAvailablePanes: [PeerInfoPaneKey]?
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId) {
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, isMediaOnly: Bool) {
self.context = context
self.updatedPresentationData = updatedPresentationData
self.peerId = peerId
self.isMediaOnly = isMediaOnly
self.separatorNode = ASDisplayNode()
self.separatorNode.isLayerBacked = true
@ -539,6 +546,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring))
strongSelf.currentPaneUpdated?(true)
strongSelf.currentPaneStatusPromise.set(strongSelf.currentPane?.node.status ?? .single(nil))
}
} else if strongSelf.pendingSwitchToPaneKey != key {
strongSelf.pendingSwitchToPaneKey = key
@ -642,6 +651,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
self.transitionFraction = 0.0
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.35, curve: .spring))
self.currentPaneUpdated?(false)
self.currentPaneStatusPromise.set(self.currentPane?.node.status ?? .single(nil))
}
default:
break
@ -683,6 +694,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
self.currentAvailablePanes = availablePanes
let previousCurrentPaneKey = self.currentPaneKey
var updateCurrentPaneStatus = false
if let currentPaneKey = self.currentPaneKey, !availablePanes.contains(currentPaneKey) {
var nextCandidatePaneKey: PeerInfoPaneKey?
@ -831,6 +843,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
self.pendingSwitchToPaneKey = nil
previousPaneKey = self.currentPaneKey
self.currentPaneKey = pendingSwitchToPaneKey
updateCurrentPaneStatus = true
updatedCurrentIndex = availablePanes.firstIndex(of: pendingSwitchToPaneKey)
if let previousPaneKey = previousPaneKey, let previousIndex = availablePanes.firstIndex(of: previousPaneKey), let updatedCurrentIndex = updatedCurrentIndex {
if updatedCurrentIndex < previousIndex {
@ -900,7 +913,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
tabsOffset = currentPane.node.tabBarOffset
}
tabsOffset = max(0.0, min(tabsHeight, tabsOffset))
if isScrollingLockedAtTop {
if isScrollingLockedAtTop || self.isMediaOnly {
tabsOffset = 0.0
}
var tabsAlpha = 1.0 - tabsOffset / tabsHeight
@ -954,5 +967,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
if let previousCurrentPaneKey = previousCurrentPaneKey, self.currentPaneKey != previousCurrentPaneKey {
self.currentPaneUpdated?(true)
}
if updateCurrentPaneStatus {
self.currentPaneStatusPromise.set(self.currentPane?.node.status ?? .single(nil))
}
}
}

View File

@ -1618,7 +1618,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
self.scrollNode.canCancelAllTouchesInViews = true
self.headerNode = PeerInfoHeaderNode(context: context, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isSettings: isSettings)
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId)
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, isMediaOnly: self.isMediaOnly)
super.init()
@ -2268,12 +2268,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return
}
if let pane = strongSelf.paneContainerNode.currentPane?.node {
strongSelf.customStatusPromise.set(pane.status)
} else {
strongSelf.customStatusPromise.set(.single(nil))
}
if let (layout, navigationHeight) = strongSelf.validLayout {
if strongSelf.headerNode.isAvatarExpanded {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .spring)
@ -2293,6 +2287,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
}
self.customStatusPromise.set(self.paneContainerNode.currentPaneStatus)
self.paneContainerNode.requestExpandTabs = { [weak self] in
guard let strongSelf = self, let (_, navigationHeight) = strongSelf.validLayout else {
return false
@ -6042,11 +6038,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
if !"".isEmpty {
canZoom = false
}
/*if isZoomIn {
canZoom = pane?.availableZoomLevels().increment != nil
} else {
canZoom = pane?.availableZoomLevels().decrement != nil
}*/
return ContextMenuActionItem(text: isZoomIn ? "Zoom In" : "Zoom Out", textColor: canZoom ? .primary : .disabled, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: isZoomIn ? "Chat/Context Menu/ZoomIn" : "Chat/Context Menu/ZoomOut"), color: canZoom ? theme.contextMenu.primaryColor : theme.contextMenu.primaryColor.withMultipliedAlpha(0.4))
}, action: canZoom ? { action in
@ -6202,7 +6193,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
var contentHeight: CGFloat = 0.0
let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.customStatusData ?? self.data?.status, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive)
let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive)
let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: layout.size.width, height: headerHeight))
if additive {
transition.updateFrameAdditive(node: self.headerNode, frame: headerFrame)
@ -6451,7 +6442,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
if let (layout, navigationHeight) = self.validLayout {
if !additive {
let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.customStatusData ?? self.data?.status, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive)
let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive)
}
let paneAreaExpansionDistance: CGFloat = 32.0
@ -7323,7 +7314,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
self.headerNode.navigationTransition = PeerInfoHeaderNavigationTransition(sourceNavigationBar: bottomNavigationBar, sourceTitleView: previousTitleView, sourceTitleFrame: previousTitleFrame, sourceSubtitleFrame: previousStatusFrame, fraction: fraction)
var topHeight = topNavigationBar.backgroundNode.bounds.height
if let (layout, _) = self.screenNode.validLayout {
topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, transition: transition, additive: false)
topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, panelStatusData: nil, isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, transition: transition, additive: false)
}
let titleScale = (fraction * previousTitleNode.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.bounds.height