mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Shared media improvements
This commit is contained in:
@@ -166,9 +166,9 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
private let descriptionProgressNode: ImmediateTextNode
|
||||
public let dateNode: TextNode
|
||||
|
||||
private let extensionIconNode: ASImageNode
|
||||
public let extensionIconNode: ASImageNode
|
||||
private let extensionIconText: TextNode
|
||||
private let iconImageNode: TransformImageNode
|
||||
public let iconImageNode: TransformImageNode
|
||||
private let iconStatusNode: SemanticStatusNode
|
||||
|
||||
private let restrictionNode: ASDisplayNode
|
||||
|
||||
@@ -26,9 +26,14 @@ public protocol SparseItemGridDisplayItem: AnyObject {
|
||||
var view: SparseItemGridView? { get }
|
||||
}
|
||||
|
||||
public protocol SparseItemGridShimmerLayer: CALayer {
|
||||
func update(size: CGSize)
|
||||
}
|
||||
|
||||
public protocol SparseItemGridBinding: AnyObject {
|
||||
func createLayer() -> SparseItemGridLayer?
|
||||
func createView() -> SparseItemGridView?
|
||||
func createShimmerLayer() -> SparseItemGridShimmerLayer?
|
||||
func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: Bool)
|
||||
func unbindLayer(layer: SparseItemGridLayer)
|
||||
func scrollerTextForTag(tag: Int32) -> String?
|
||||
@@ -141,10 +146,13 @@ private final class Shimmer {
|
||||
}
|
||||
}
|
||||
|
||||
final class Layer: CALayer {
|
||||
final class Layer: CALayer, SparseItemGridShimmerLayer {
|
||||
override func action(forKey event: String) -> CAAction? {
|
||||
return nullAction
|
||||
}
|
||||
|
||||
func update(size: CGSize) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +431,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
var layout: Layout?
|
||||
var items: Items?
|
||||
var visibleItems: [AnyHashable: VisibleItem] = [:]
|
||||
var visiblePlaceholders: [Shimmer.Layer] = []
|
||||
var visiblePlaceholders: [SparseItemGridShimmerLayer] = []
|
||||
|
||||
private var scrollingArea: SparseItemGridScrollingArea?
|
||||
private var currentScrollingTag: Int32?
|
||||
@@ -435,9 +443,12 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
private var previousScrollOffset: CGFloat = 0.0
|
||||
var coveringInsetOffset: CGFloat = 0.0
|
||||
|
||||
init(zoomLevel: ZoomLevel, maybeLoadHoleAnchor: @escaping (HoleAnchor, HoleLocation) -> Void) {
|
||||
let coveringOffsetUpdated: (Viewport, ContainedViewLayoutTransition) -> Void
|
||||
|
||||
init(zoomLevel: ZoomLevel, maybeLoadHoleAnchor: @escaping (HoleAnchor, HoleLocation) -> Void, coveringOffsetUpdated: @escaping (Viewport, ContainedViewLayoutTransition) -> Void) {
|
||||
self.zoomLevel = zoomLevel
|
||||
self.maybeLoadHoleAnchor = maybeLoadHoleAnchor
|
||||
self.coveringOffsetUpdated = coveringOffsetUpdated
|
||||
|
||||
self.scrollView = UIScrollView()
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
@@ -476,7 +487,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
if !self.ignoreScrolling {
|
||||
self.updateVisibleItems(resetScrolling: false, synchronous: true, restoreScrollPosition: nil)
|
||||
|
||||
if let layout = self.layout, let items = self.items {
|
||||
if let layout = self.layout, let _ = self.items {
|
||||
let offset = scrollView.contentOffset.y
|
||||
let delta = offset - self.previousScrollOffset
|
||||
self.previousScrollOffset = offset
|
||||
@@ -495,7 +506,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
}
|
||||
if coveringInsetOffset < self.coveringInsetOffset {
|
||||
self.coveringInsetOffset = coveringInsetOffset
|
||||
items.itemBinding.coveringInsetOffsetUpdated(transition: .immediate)
|
||||
self.coveringOffsetUpdated(self, .immediate)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -511,7 +522,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
}
|
||||
if coveringInsetOffset != self.coveringInsetOffset {
|
||||
self.coveringInsetOffset = coveringInsetOffset
|
||||
items.itemBinding.coveringInsetOffsetUpdated(transition: .immediate)
|
||||
self.coveringOffsetUpdated(self, .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -539,7 +550,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
}
|
||||
|
||||
private func snapCoveringInsetOffset() {
|
||||
if let layout = self.layout, let items = self.items {
|
||||
if let layout = self.layout, let _ = self.items {
|
||||
let offset = self.scrollView.contentOffset.y
|
||||
if offset < layout.containerLayout.insets.top {
|
||||
if offset <= layout.containerLayout.insets.top / 2.0 {
|
||||
@@ -560,7 +571,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
|
||||
if coveringInsetOffset != self.coveringInsetOffset {
|
||||
self.coveringInsetOffset = coveringInsetOffset
|
||||
items.itemBinding.coveringInsetOffsetUpdated(transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
self.coveringOffsetUpdated(self, .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -680,6 +691,10 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
self.scrollView.setContentOffset(self.scrollView.contentOffset, animated: false)
|
||||
}
|
||||
|
||||
func updateShimmerLayers() {
|
||||
self.updateVisibleItems(resetScrolling: false, synchronous: false, restoreScrollPosition: nil)
|
||||
}
|
||||
|
||||
private func updateVisibleItems(resetScrolling: Bool, synchronous: Bool, restoreScrollPosition: (y: CGFloat, index: Int)?) {
|
||||
guard let layout = self.layout, let items = self.items else {
|
||||
return
|
||||
@@ -753,21 +768,39 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
if itemLayer.needsShimmer {
|
||||
let placeholderLayer: SparseItemGridShimmerLayer
|
||||
if self.visiblePlaceholders.count > usedPlaceholderCount {
|
||||
placeholderLayer = self.visiblePlaceholders[usedPlaceholderCount]
|
||||
} else {
|
||||
placeholderLayer = items.itemBinding.createShimmerLayer() ?? Shimmer.Layer()
|
||||
self.scrollView.layer.insertSublayer(placeholderLayer, at: 0)
|
||||
self.visiblePlaceholders.append(placeholderLayer)
|
||||
}
|
||||
|
||||
let itemFrame = layout.frame(at: index)
|
||||
placeholderLayer.frame = itemFrame
|
||||
self.shimmer.update(colors: shimmerColors, layer: placeholderLayer, containerSize: layout.containerLayout.size, frame: itemFrame.offsetBy(dx: 0.0, dy: -visibleBounds.minY))
|
||||
placeholderLayer.update(size: itemFrame.size)
|
||||
usedPlaceholderCount += 1
|
||||
}
|
||||
|
||||
validIds.insert(item.id)
|
||||
|
||||
itemLayer.frame = layout.frame(at: index)
|
||||
} else if layout.containerLayout.fixedItemHeight == nil {
|
||||
let placeholderLayer: Shimmer.Layer
|
||||
} else {
|
||||
let placeholderLayer: SparseItemGridShimmerLayer
|
||||
if self.visiblePlaceholders.count > usedPlaceholderCount {
|
||||
placeholderLayer = self.visiblePlaceholders[usedPlaceholderCount]
|
||||
} else {
|
||||
placeholderLayer = Shimmer.Layer()
|
||||
placeholderLayer = items.itemBinding.createShimmerLayer() ?? Shimmer.Layer()
|
||||
self.scrollView.layer.addSublayer(placeholderLayer)
|
||||
self.visiblePlaceholders.append(placeholderLayer)
|
||||
}
|
||||
let itemFrame = layout.frame(at: index)
|
||||
placeholderLayer.frame = itemFrame
|
||||
self.shimmer.update(colors: shimmerColors, layer: placeholderLayer, containerSize: layout.containerLayout.size, frame: itemFrame.offsetBy(dx: 0.0, dy: -visibleBounds.minY))
|
||||
placeholderLayer.update(size: itemFrame.size)
|
||||
usedPlaceholderCount += 1
|
||||
}
|
||||
}
|
||||
@@ -783,11 +816,6 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
} else if let view = item.view {
|
||||
view.update(size: layer.frame.size)
|
||||
}
|
||||
|
||||
if item.needsShimmer {
|
||||
let itemFrame = layer.frame
|
||||
self.shimmer.update(colors: shimmerColors, layer: item.displayLayer, containerSize: layout.containerLayout.size, frame: itemFrame.offsetBy(dx: 0.0, dy: -visibleBounds.minY))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -923,12 +951,19 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
|
||||
var currentProgress: CGFloat = 0.0
|
||||
|
||||
init(interactiveState: InteractiveState?, layout: ContainerLayout, anchorItemIndex: Int, from fromViewport: Viewport, to toViewport: Viewport) {
|
||||
var coveringInsetOffset: CGFloat {
|
||||
return self.fromViewport.coveringInsetOffset * (1.0 - self.currentProgress) + self.toViewport.coveringInsetOffset * self.currentProgress
|
||||
}
|
||||
|
||||
let coveringOffsetUpdated: (ContainedViewLayoutTransition) -> Void
|
||||
|
||||
init(interactiveState: InteractiveState?, layout: ContainerLayout, anchorItemIndex: Int, from fromViewport: Viewport, to toViewport: Viewport, coveringOffsetUpdated: @escaping (ContainedViewLayoutTransition) -> Void) {
|
||||
self.interactiveState = interactiveState
|
||||
self.layout = layout
|
||||
self.anchorItemIndex = anchorItemIndex
|
||||
self.fromViewport = fromViewport
|
||||
self.toViewport = toViewport
|
||||
self.coveringOffsetUpdated = coveringOffsetUpdated
|
||||
|
||||
super.init()
|
||||
|
||||
@@ -1006,6 +1041,8 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
} else {
|
||||
transition.updateAlpha(node: self.fromViewport, alpha: 1.0 - fromAlphaProgress)
|
||||
}
|
||||
|
||||
self.coveringOffsetUpdated(transition)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1033,12 +1070,17 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
private let loadingHoleDisposable = MetaDisposable()
|
||||
|
||||
public var coveringInsetOffset: CGFloat {
|
||||
if let currentViewport = self.currentViewport {
|
||||
if let currentViewportTransition = self.currentViewportTransition {
|
||||
return currentViewportTransition.coveringInsetOffset
|
||||
} else if let currentViewport = self.currentViewport {
|
||||
return currentViewport.coveringInsetOffset
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
public var cancelExternalContentGestures: (() -> Void)?
|
||||
|
||||
override public init() {
|
||||
self.scrollingArea = SparseItemGridScrollingArea()
|
||||
|
||||
@@ -1085,7 +1127,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
break
|
||||
self.cancelExternalContentGestures?()
|
||||
case .changed:
|
||||
let scale = recognizer.scale
|
||||
if let currentViewportTransition = self.currentViewportTransition, let interactiveState = currentViewportTransition.interactiveState {
|
||||
@@ -1138,6 +1180,8 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
|
||||
}, coveringOffsetUpdated: { [weak self] viewport, transition in
|
||||
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
|
||||
})
|
||||
|
||||
nextViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||
@@ -1146,7 +1190,9 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
self.currentViewportTransition?.removeFromSupernode()
|
||||
|
||||
let nextInteractiveState = ViewportTransition.InteractiveState(anchorLocation: anchorLocation, initialScale: startScale, targetScale: nextScale)
|
||||
let currentViewportTransition = ViewportTransition(interactiveState: nextInteractiveState, layout: containerLayout, anchorItemIndex: currentViewportTransition.anchorItemIndex, from: boundaryViewport, to: nextViewport)
|
||||
let currentViewportTransition = ViewportTransition(interactiveState: nextInteractiveState, layout: containerLayout, anchorItemIndex: currentViewportTransition.anchorItemIndex, from: boundaryViewport, to: nextViewport, coveringOffsetUpdated: { [weak self] transition in
|
||||
self?.transitionCoveringOffsetUpdated(transition: transition)
|
||||
})
|
||||
currentViewportTransition.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||
self.insertSubnode(currentViewportTransition, belowSubnode: self.scrollingArea)
|
||||
self.currentViewportTransition = currentViewportTransition
|
||||
@@ -1184,12 +1230,16 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
|
||||
}, coveringOffsetUpdated: { [weak self] viewport, transition in
|
||||
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
|
||||
})
|
||||
|
||||
nextViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||
nextViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition)
|
||||
|
||||
let currentViewportTransition = ViewportTransition(interactiveState: interactiveState, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: nextViewport)
|
||||
let currentViewportTransition = ViewportTransition(interactiveState: interactiveState, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: nextViewport, coveringOffsetUpdated: { [weak self] transition in
|
||||
self?.transitionCoveringOffsetUpdated(transition: transition)
|
||||
})
|
||||
currentViewportTransition.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||
self.insertSubnode(currentViewportTransition, belowSubnode: self.scrollingArea)
|
||||
self.currentViewportTransition = currentViewportTransition
|
||||
@@ -1249,6 +1299,8 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
|
||||
}, coveringOffsetUpdated: { [weak self] viewport, transition in
|
||||
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
|
||||
})
|
||||
self.currentViewport = currentViewport
|
||||
self.insertSubnode(currentViewport, belowSubnode: self.scrollingArea)
|
||||
@@ -1272,6 +1324,12 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
|
||||
/*#if DEBUG
|
||||
if "".isEmpty {
|
||||
return
|
||||
}
|
||||
#endif*/
|
||||
|
||||
self.isLoadingHole = true
|
||||
self.loadingHoleDisposable.set((items.itemBinding.loadHole(anchor: holeAnchor, at: location)
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
@@ -1323,6 +1381,8 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
|
||||
}, coveringOffsetUpdated: { [weak self] viewport, transition in
|
||||
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
|
||||
})
|
||||
self.currentViewport = currentViewport
|
||||
self.insertSubnode(currentViewport, belowSubnode: self.scrollingArea)
|
||||
@@ -1337,7 +1397,9 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
currentViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||
currentViewport.update(containerLayout: containerLayout, items: items, restoreScrollPosition: restoreScrollPosition)
|
||||
|
||||
let currentViewportTransition = ViewportTransition(interactiveState: nil, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: currentViewport)
|
||||
let currentViewportTransition = ViewportTransition(interactiveState: nil, layout: containerLayout, anchorItemIndex: anchorItemIndex, from: previousViewport, to: currentViewport, coveringOffsetUpdated: { [weak self] transition in
|
||||
self?.transitionCoveringOffsetUpdated(transition: transition)
|
||||
})
|
||||
currentViewportTransition.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||
self.insertSubnode(currentViewportTransition, belowSubnode: self.scrollingArea)
|
||||
self.currentViewportTransition = currentViewportTransition
|
||||
@@ -1365,6 +1427,23 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private func coveringOffsetUpdated(viewport: Viewport, transition: ContainedViewLayoutTransition) {
|
||||
guard let items = self.items else {
|
||||
return
|
||||
}
|
||||
if self.currentViewportTransition != nil {
|
||||
return
|
||||
}
|
||||
items.itemBinding.coveringInsetOffsetUpdated(transition: transition)
|
||||
}
|
||||
|
||||
private func transitionCoveringOffsetUpdated(transition: ContainedViewLayoutTransition) {
|
||||
guard let items = self.items else {
|
||||
return
|
||||
}
|
||||
items.itemBinding.coveringInsetOffsetUpdated(transition: transition)
|
||||
}
|
||||
|
||||
public func forEachVisibleItem(_ f: (SparseItemGridDisplayItem) -> Void) {
|
||||
guard let currentViewport = self.currentViewport else {
|
||||
return
|
||||
@@ -1420,4 +1499,12 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
|
||||
self.scrollingArea.hideScroller()
|
||||
}
|
||||
|
||||
public func updateShimmerLayers() {
|
||||
self.currentViewport?.updateShimmerLayers()
|
||||
if let currentViewportTransition = self.currentViewportTransition {
|
||||
currentViewportTransition.fromViewport.updateShimmerLayers()
|
||||
currentViewportTransition.toViewport.updateShimmerLayers()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -907,7 +907,7 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
|
||||
private func dismissLineTooltip() {
|
||||
if let lineTooltip = self.lineTooltip {
|
||||
self.lineTooltip = nil
|
||||
lineTooltip.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak lineTooltip] _ in
|
||||
lineTooltip.layer.animateAlpha(from: lineTooltip.alpha, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak lineTooltip] _ in
|
||||
lineTooltip?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
@@ -936,6 +936,10 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
|
||||
transition.updateSublayerTransformOffset(layer: self.dateIndicator.layer, offset: CGPoint(x: -3.0, y: 0.0))
|
||||
|
||||
displayTooltip.completed()
|
||||
|
||||
Queue.mainQueue().after(2.0, { [weak self] in
|
||||
self?.dismissLineTooltip()
|
||||
})
|
||||
}
|
||||
|
||||
private func updateLineTooltip(containerSize: CGSize) {
|
||||
|
||||
@@ -774,15 +774,6 @@ private final class ItemLayer: CALayer, SparseItemGridLayer {
|
||||
|
||||
func bind(item: VisualMediaItem) {
|
||||
self.item = item
|
||||
|
||||
self.updateShimmerLayer()
|
||||
}
|
||||
|
||||
func updateShimmerLayer() {
|
||||
if self.hasContents {
|
||||
self.removeAnimation(forKey: "shimmer")
|
||||
self.contentsRect = CGRect(origin: CGPoint(), size: CGSize(width: 1.0, height: 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
func updateDuration(duration: Int32?) {
|
||||
@@ -820,8 +811,6 @@ private final class ItemView: UIView, SparseItemGridView {
|
||||
var item: VisualMediaItem?
|
||||
var disposable: Disposable?
|
||||
|
||||
var hasContents: Bool = false
|
||||
|
||||
var messageItem: ListMessageItem?
|
||||
var messageItemNode: ListViewItemNode?
|
||||
var interaction: ListMessageItemInteraction?
|
||||
@@ -925,7 +914,55 @@ private final class ItemView: UIView, SparseItemGridView {
|
||||
}
|
||||
}
|
||||
|
||||
private final class SparseItemGridBindingImpl: SparseItemGridBinding {
|
||||
protocol ListShimmerLayerImageProvider: AnyObject {
|
||||
func getListShimmerImage(height: CGFloat) -> UIImage
|
||||
func getSeparatorColor() -> UIColor
|
||||
}
|
||||
|
||||
private final class ListShimmerLayer: CALayer, SparseItemGridShimmerLayer {
|
||||
final class OverlayLayer: CALayer {
|
||||
override func action(forKey event: String) -> CAAction? {
|
||||
return nullAction
|
||||
}
|
||||
}
|
||||
|
||||
let imageProvider: ListShimmerLayerImageProvider
|
||||
let shimmerOverlay: OverlayLayer
|
||||
let separatorLayer: OverlayLayer
|
||||
|
||||
private var validHeight: CGFloat?
|
||||
|
||||
init(imageProvider: ListShimmerLayerImageProvider) {
|
||||
self.imageProvider = imageProvider
|
||||
self.shimmerOverlay = OverlayLayer()
|
||||
self.separatorLayer = OverlayLayer()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSublayer(self.shimmerOverlay)
|
||||
self.addSublayer(self.separatorLayer)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func action(forKey event: String) -> CAAction? {
|
||||
return nullAction
|
||||
}
|
||||
|
||||
func update(size: CGSize) {
|
||||
if self.validHeight != size.height {
|
||||
self.validHeight = size.height
|
||||
ASDisplayNodeSetResizableContents(self.shimmerOverlay, self.imageProvider.getListShimmerImage(height: size.height))
|
||||
self.separatorLayer.backgroundColor = self.imageProvider.getSeparatorColor().cgColor
|
||||
}
|
||||
self.shimmerOverlay.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.separatorLayer.frame = CGRect(origin: CGPoint(x: 65.0, y: size.height - UIScreenPixel), size: CGSize(width: size.width - 65.0, height: UIScreenPixel))
|
||||
}
|
||||
}
|
||||
|
||||
private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimmerLayerImageProvider {
|
||||
let context: AccountContext
|
||||
let chatLocation: ChatLocation
|
||||
let directMediaImageCache: DirectMediaImageCache
|
||||
@@ -942,6 +979,9 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
|
||||
var coveringInsetOffsetUpdatedImpl: ((ContainedViewLayoutTransition) -> Void)?
|
||||
var onBeginFastScrollingImpl: (() -> Void)?
|
||||
var getShimmerColorsImpl: (() -> SparseItemGrid.ShimmerColors)?
|
||||
var updateShimmerLayersImpl: (() -> Void)?
|
||||
|
||||
private var shimmerImages: [CGFloat: UIImage] = [:]
|
||||
|
||||
init(context: AccountContext, chatLocation: ChatLocation, useListItems: Bool, listItemInteraction: ListMessageItemInteraction, chatControllerInteraction: ChatControllerInteraction, directMediaImageCache: DirectMediaImageCache) {
|
||||
self.context = context
|
||||
@@ -958,6 +998,95 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
|
||||
self.chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners, animatedEmojiScale: 1.0)
|
||||
}
|
||||
|
||||
func getListShimmerImage(height: CGFloat) -> UIImage {
|
||||
if let image = self.shimmerImages[height] {
|
||||
return image
|
||||
} else {
|
||||
let fakeFile = TelegramMediaFile(
|
||||
fileId: MediaId(namespace: 0, id: 1),
|
||||
partialReference: nil,
|
||||
resource: EmptyMediaResource(),
|
||||
previewRepresentations: [],
|
||||
videoThumbnails: [],
|
||||
immediateThumbnailData: nil,
|
||||
mimeType: "image/jpeg",
|
||||
size: nil,
|
||||
attributes: [.FileName(fileName: "file")]
|
||||
)
|
||||
let fakeMessage = Message(
|
||||
stableId: 1,
|
||||
stableVersion: 1,
|
||||
id: MessageId(peerId: PeerId(namespace: PeerId.Namespace._internalFromInt32Value(0), id: PeerId.Id._internalFromInt64Value(1)), namespace: 0, id: 1),
|
||||
globallyUniqueId: nil,
|
||||
groupingKey: nil,
|
||||
groupInfo: nil,
|
||||
threadId: nil,
|
||||
timestamp: 1, flags: [],
|
||||
tags: [],
|
||||
globalTags: [],
|
||||
localTags: [],
|
||||
forwardInfo: nil,
|
||||
author: nil,
|
||||
text: "",
|
||||
attributes: [],
|
||||
media: [fakeFile],
|
||||
peers: SimpleDictionary<PeerId, Peer>(),
|
||||
associatedMessages: SimpleDictionary<MessageId, Message>(),
|
||||
associatedMessageIds: []
|
||||
)
|
||||
let messageItem = ListMessageItem(
|
||||
presentationData: self.chatPresentationData,
|
||||
context: self.context,
|
||||
chatLocation: self.chatLocation,
|
||||
interaction: self.listItemInteraction,
|
||||
message: fakeMessage,
|
||||
selection: .none,
|
||||
displayHeader: false
|
||||
)
|
||||
|
||||
var itemNode: ListViewItemNode?
|
||||
messageItem.nodeConfiguredForParams(async: { f in f() }, params: ListViewItemLayoutParams(width: 400.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 0.0), synchronousLoads: false, previousItem: nil, nextItem: nil, completion: { node, apply in
|
||||
itemNode = node
|
||||
apply().1(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
|
||||
guard let fileItemNode = itemNode as? ListMessageFileItemNode else {
|
||||
return UIImage()
|
||||
}
|
||||
|
||||
let image = generateImage(CGSize(width: 320.0, height: height), rotatedContext: { size, context in
|
||||
UIGraphicsPushContext(context)
|
||||
|
||||
context.setFillColor(self.chatPresentationData.theme.theme.list.plainBackgroundColor.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
|
||||
func fillRoundedRect(rect: CGRect, radius: CGFloat) {
|
||||
UIBezierPath(roundedRect: rect, byRoundingCorners: [.topLeft, .topRight, .bottomLeft, .bottomRight], cornerRadii: CGSize(width: radius, height: radius)).fill()
|
||||
}
|
||||
|
||||
let lineHeight: CGFloat = 8.0
|
||||
let titleOrigin = CGPoint(x: fileItemNode.titleNode.frame.minX, y: fileItemNode.titleNode.frame.midY)
|
||||
let dateOrigin = CGPoint(x: fileItemNode.descriptionNode.frame.minX, y: fileItemNode.descriptionNode.frame.midY)
|
||||
|
||||
fillRoundedRect(rect: CGRect(origin: CGPoint(x: titleOrigin.x, y: titleOrigin.y - lineHeight / 2.0), size: CGSize(width: 160.0, height: lineHeight)), radius: lineHeight / 2.0)
|
||||
fillRoundedRect(rect: CGRect(origin: CGPoint(x: dateOrigin.x, y: dateOrigin.y - lineHeight / 2.0), size: CGSize(width: 220.0, height: lineHeight)), radius: lineHeight / 2.0)
|
||||
|
||||
fillRoundedRect(rect: fileItemNode.extensionIconNode.frame, radius: 6.0)
|
||||
|
||||
UIGraphicsPopContext()
|
||||
})!.stretchableImage(withLeftCapWidth: 299, topCapHeight: 0)
|
||||
self.shimmerImages[height] = image
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
||||
func getSeparatorColor() -> UIColor {
|
||||
return self.chatPresentationData.theme.theme.list.itemPlainSeparatorColor
|
||||
}
|
||||
|
||||
func createLayer() -> SparseItemGridLayer? {
|
||||
if self.useListItems {
|
||||
return nil
|
||||
@@ -972,6 +1101,14 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
|
||||
return ItemView()
|
||||
}
|
||||
|
||||
func createShimmerLayer() -> SparseItemGridShimmerLayer? {
|
||||
if self.useListItems {
|
||||
let layer = ListShimmerLayer(imageProvider: self)
|
||||
return layer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func bindLayers(items: [SparseItemGrid.Item], layers: [SparseItemGridDisplayItem], synchronous: Bool) {
|
||||
for i in 0 ..< items.count {
|
||||
guard let item = items[i] as? VisualMediaItem else {
|
||||
@@ -1034,22 +1171,30 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
|
||||
}
|
||||
if let loadSignal = result.loadSignal {
|
||||
layer.disposable = (loadSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak layer] image in
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak layer] image in
|
||||
guard let layer = layer else {
|
||||
return
|
||||
}
|
||||
let copyLayer = ItemLayer()
|
||||
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
|
||||
copyLayer?.removeFromSuperlayer()
|
||||
})
|
||||
if layer.hasContents {
|
||||
let copyLayer = ItemLayer()
|
||||
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
|
||||
copyLayer?.removeFromSuperlayer()
|
||||
})
|
||||
|
||||
layer.contents = image?.cgImage
|
||||
layer.hasContents = true
|
||||
layer.updateShimmerLayer()
|
||||
layer.contents = image?.cgImage
|
||||
layer.hasContents = true
|
||||
} else {
|
||||
layer.contents = image?.cgImage
|
||||
layer.hasContents = true
|
||||
|
||||
layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
self?.updateShimmerLayersImpl?()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1293,7 +1438,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if count < 1 || true {
|
||||
if count < 1 {
|
||||
//TODO:localize
|
||||
strongSelf.itemGrid.updateScrollingAreaTooltip(tooltip: SparseItemGridScrollingArea.DisplayTooltip(animation: "anim_infotip", text: "You can hold and move this bar for faster scrolling", completed: {
|
||||
guard let strongSelf = self else {
|
||||
@@ -1388,6 +1533,14 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
|
||||
return SparseItemGrid.ShimmerColors(background: backgroundColor.argb, foreground: foregroundColor.argb)
|
||||
}
|
||||
|
||||
self.itemGridBinding.updateShimmerLayersImpl = { [weak self] in
|
||||
self?.itemGrid.updateShimmerLayers()
|
||||
}
|
||||
|
||||
self.itemGrid.cancelExternalContentGestures = { [weak self] in
|
||||
self?.contextGestureContainerNode.cancelGesture()
|
||||
}
|
||||
|
||||
self._itemInteraction = VisualMediaItemInteraction(
|
||||
openMessage: { [weak self] message in
|
||||
|
||||
@@ -417,14 +417,8 @@ private final class PeerInfoPendingPane {
|
||||
paneNode = visualPaneNode
|
||||
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .music)
|
||||
case .gifs:
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .gifs)
|
||||
let visualPaneNode = PeerInfoGifPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .gifs)
|
||||
paneNode = visualPaneNode
|
||||
visualPaneNode.openCurrentDate = {
|
||||
openMediaCalendar()
|
||||
}
|
||||
visualPaneNode.paneDidScroll = {
|
||||
paneDidScroll()
|
||||
}
|
||||
case .groupsInCommon:
|
||||
paneNode = PeerInfoGroupsInCommonPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, openPeerContextAction: openPeerContextAction, groupsInCommonContext: data.groupsInCommon!)
|
||||
case .members:
|
||||
|
||||
1341
submodules/TelegramUI/Sources/PeerInfoGifPaneNode.swift
Normal file
1341
submodules/TelegramUI/Sources/PeerInfoGifPaneNode.swift
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user