Shared media improvements

This commit is contained in:
Ali 2021-10-28 01:53:11 +04:00
parent 53b4e8fd32
commit e8ec9b85fc
11 changed files with 245 additions and 96 deletions

View File

@ -411,7 +411,7 @@ private func peerAvatar(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) -
}
}
let cachedPath = mediaBox.cachedRepresentationPathForId("lettersAvatar-\(peer.displayLetters.joined(separator: ","))", representationId: "intents.png", keepDuration: .shortLived)
let cachedPath = mediaBox.cachedRepresentationPathForId("lettersAvatar2-\(peer.displayLetters.joined(separator: ","))", representationId: "intents.png", keepDuration: .shortLived)
if let _ = fileSize(cachedPath) {
return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
} else {

View File

@ -19,6 +19,7 @@ swift_library(
"//submodules/AccountContext:AccountContext",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/PhotoResources:PhotoResources",
"//submodules/DirectMediaImageCache:DirectMediaImageCache"
],
visibility = [
"//visibility:public",

View File

@ -9,24 +9,27 @@ import AccountContext
import TelegramPresentationData
import ComponentFlow
import PhotoResources
import DirectMediaImageCache
private final class MediaPreviewView: UIView {
private let context: AccountContext
private let message: EngineMessage
private let media: EngineMedia
private let imageCache: DirectMediaImageCache
private let imageView: TransformImageView
private let imageView: UIImageView
private var requestedImage: Bool = false
private var disposable: Disposable?
init(context: AccountContext, message: EngineMessage, media: EngineMedia) {
init(context: AccountContext, message: EngineMessage, media: EngineMedia, imageCache: DirectMediaImageCache) {
self.context = context
self.message = message
self.media = media
self.imageCache = imageCache
self.imageView = TransformImageView()
self.imageView.contentAnimations = .subsequentUpdates
self.imageView = UIImageView()
self.imageView.contentMode = .scaleToFill
super.init(frame: CGRect())
@ -42,7 +45,53 @@ private final class MediaPreviewView: UIView {
}
func updateLayout(size: CGSize, synchronousLoads: Bool) {
var dimensions = CGSize(width: 100.0, height: 100.0)
let processImage: (UIImage) -> UIImage = { image in
return generateImage(size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.addEllipse(in: CGRect(origin: CGPoint(), size: size))
context.clip()
UIGraphicsPushContext(context)
image.draw(in: CGRect(origin: CGPoint(), size: size))
UIGraphicsPopContext()
})!
}
if !self.requestedImage {
self.requestedImage = true
if let result = self.imageCache.getImage(message: self.message._asMessage(), media: self.media._asMedia(), width: 100, possibleWidths: [100], synchronous: false) {
if let image = result.image {
self.imageView.image = processImage(image)
}
if let signal = result.loadSignal {
self.disposable = (signal
|> map { image in
return image.flatMap(processImage)
}
|> deliverOnMainQueue).start(next: { [weak self] image in
guard let strongSelf = self else {
return
}
if let image = image {
if strongSelf.imageView.image != nil {
let tempView = UIImageView()
tempView.image = strongSelf.imageView.image
tempView.frame = strongSelf.imageView.frame
tempView.contentMode = strongSelf.imageView.contentMode
strongSelf.addSubview(tempView)
tempView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak tempView] _ in
tempView?.removeFromSuperview()
})
}
strongSelf.imageView.image = image
}
})
}
}
}
self.imageView.frame = CGRect(origin: CGPoint(), size: size)
/*var dimensions = CGSize(width: 100.0, height: 100.0)
if case let .image(image) = self.media {
if let largest = largestImageRepresentation(image.representations) {
dimensions = largest.dimensions.cgSize
@ -66,7 +115,7 @@ private final class MediaPreviewView: UIView {
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()
apply()*/
}
}
@ -250,6 +299,20 @@ private final class ImageCache: Equatable {
}
}
private final class DayEnvironment: Equatable {
let imageCache: ImageCache
let directImageCache: DirectMediaImageCache
init(imageCache: ImageCache, directImageCache: DirectMediaImageCache) {
self.imageCache = imageCache
self.directImageCache = directImageCache
}
static func ==(lhs: DayEnvironment, rhs: DayEnvironment) -> Bool {
return lhs === rhs
}
}
private final class ImageComponent: Component {
let image: UIImage?
@ -292,7 +355,7 @@ private final class ImageComponent: Component {
}
private final class DayComponent: Component {
typealias EnvironmentType = ImageCache
typealias EnvironmentType = DayEnvironment
enum DaySelection {
case none
@ -410,7 +473,9 @@ private final class DayComponent: Component {
self.action?()
}
func update(component: DayComponent, availableSize: CGSize, environment: Environment<ImageCache>, transition: Transition) -> CGSize {
func update(component: DayComponent, availableSize: CGSize, environment: Environment<DayEnvironment>, transition: Transition) -> CGSize {
let isFirstTime = self.action == nil
self.action = component.action
self.index = component.media?.message.index
self.isHighlightingEnabled = component.isEnabled && component.media != nil && !component.isSelecting
@ -418,23 +483,26 @@ private final class DayComponent: Component {
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]
let dayEnvironment = environment[DayEnvironment.self].value
if component.media != nil {
self.highlightView.image = imageCache.value.filledCircle(diameter: diameter, innerDiameter: nil, color: UIColor(white: 0.0, alpha: 0.2))
self.highlightView.image = dayEnvironment.imageCache.filledCircle(diameter: diameter, innerDiameter: nil, color: UIColor(white: 0.0, alpha: 0.2))
} else {
self.highlightView.image = nil
}
var animateMediaIn = false
if self.currentMedia != component.media {
self.currentMedia = component.media
if let mediaPreviewView = self.mediaPreviewView {
self.mediaPreviewView = nil
mediaPreviewView.removeFromSuperview()
} else {
animateMediaIn = !isFirstTime
}
if let media = component.media {
let mediaPreviewView = MediaPreviewView(context: component.context, message: media.message, media: media.media)
let mediaPreviewView = MediaPreviewView(context: component.context, message: media.message, media: media.media, imageCache: dayEnvironment.directImageCache)
mediaPreviewView.isUserInteractionEnabled = false
self.mediaPreviewView = mediaPreviewView
self.button.insertSubview(mediaPreviewView, belowSubview: self.highlightView)
@ -490,9 +558,9 @@ private final class DayComponent: Component {
}
selectionView.frame = contentFrame
if self.mediaPreviewView != nil {
selectionView.image = imageCache.value.filledCircle(diameter: diameter, innerDiameter: diameter - 2.0 * 2.0, color: component.theme.list.itemCheckColors.fillColor)
selectionView.image = dayEnvironment.imageCache.filledCircle(diameter: diameter, innerDiameter: diameter - 2.0 * 2.0, color: component.theme.list.itemCheckColors.fillColor)
} else {
selectionView.image = imageCache.value.filledCircle(diameter: diameter, innerDiameter: nil, color: component.theme.list.itemCheckColors.fillColor)
selectionView.image = dayEnvironment.imageCache.filledCircle(diameter: diameter, innerDiameter: nil, color: component.theme.list.itemCheckColors.fillColor)
}
case .middle, .none:
if let selectionView = self.selectionView {
@ -509,7 +577,7 @@ private final class DayComponent: Component {
contentScale = 1.0
}
let titleImage = imageCache.value.text(fontSize: titleFontSize, isSemibold: titleFontIsSemibold, color: titleColor, string: component.title)
let titleImage = dayEnvironment.imageCache.text(fontSize: titleFontSize, isSemibold: titleFontIsSemibold, color: titleColor, string: component.title)
self.titleView.image = titleImage
let titleSize = titleImage.size
@ -524,6 +592,10 @@ private final class DayComponent: Component {
mediaPreviewView.updateLayout(size: contentFrame.size, synchronousLoads: false)
mediaPreviewView.layer.sublayerTransform = CATransform3DMakeScale(contentScale, contentScale, 1.0)
if animateMediaIn {
mediaPreviewView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
return availableSize
@ -534,13 +606,13 @@ private final class DayComponent: Component {
return View()
}
func update(view: View, availableSize: CGSize, environment: Environment<ImageCache>, transition: Transition) -> CGSize {
func update(view: View, availableSize: CGSize, environment: Environment<DayEnvironment>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, environment: environment, transition: transition)
}
}
private final class MonthComponent: CombinedComponent {
typealias EnvironmentType = ImageCache
typealias EnvironmentType = DayEnvironment
let context: AccountContext
let model: MonthModel
@ -593,7 +665,7 @@ private final class MonthComponent: CombinedComponent {
static var body: Body {
let title = Child(Text.self)
let weekdayTitles = ChildMap(environment: Empty.self, keyedBy: Int.self)
let days = ChildMap(environment: ImageCache.self, keyedBy: Int.self)
let days = ChildMap(environment: DayEnvironment.self, keyedBy: Int.self)
let selections = ChildMap(environment: Empty.self, keyedBy: Int.self)
return { context in
@ -673,7 +745,7 @@ private final class MonthComponent: CombinedComponent {
}
)),
environment: {
context.environment[ImageCache.self]
context.environment[DayEnvironment.self]
},
availableSize: CGSize(width: usableWeekdayWidth, height: weekdaySize),
transition: .immediate
@ -739,7 +811,7 @@ private final class MonthComponent: CombinedComponent {
if let selectedDays = context.component.selectedDays {
for (lineIndex, selection) in selectionsByLine {
let imageCache = context.environment[ImageCache.self]
let dayEnvironment = context.environment[DayEnvironment.self].value
let dayItemSize = updatedDays[0].size
let deltaWidth = floor((weekdayWidth - dayItemSize.width) / 2.0)
@ -766,7 +838,7 @@ private final class MonthComponent: CombinedComponent {
let selectionRect = CGRect(origin: CGPoint(x: minX, y: minY), size: CGSize(width: maxX - minX, height: maxY - minY))
let selection = selections[lineIndex].update(
component: AnyComponent(ImageComponent(image: imageCache.value.monthSelection(leftRadius: leftRadius, rightRadius: rightRadius, maxRadius: dayItemSize.width, color: monthSelectionColor))),
component: AnyComponent(ImageComponent(image: dayEnvironment.imageCache.monthSelection(leftRadius: leftRadius, rightRadius: rightRadius, maxRadius: dayItemSize.width, color: monthSelectionColor))),
availableSize: selectionRect.size,
transition: .immediate
)
@ -883,10 +955,10 @@ public final class CalendarMessageScreen: ViewController {
private let calendarSource: SparseMessageCalendar
private var months: [MonthModel] = []
private var monthViews: [Int: ComponentHostView<ImageCache>] = [:]
private var monthViews: [Int: ComponentHostView<DayEnvironment>] = [:]
private let contextGestureContainerNode: ContextControllerSourceNode
private let imageCache = ImageCache()
private let dayEnvironment: DayEnvironment
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
private var scrollLayout: (width: CGFloat, contentHeight: CGFloat, frames: [Int: CGRect])?
@ -933,6 +1005,8 @@ public final class CalendarMessageScreen: ViewController {
self.scrollView.indicatorStyle = .black
}
self.dayEnvironment = DayEnvironment(imageCache: ImageCache(), directImageCache: DirectMediaImageCache(account: context.account))
super.init()
self.contextGestureContainerNode.shouldBegin = { [weak self] point in
@ -1403,8 +1477,7 @@ public final class CalendarMessageScreen: ViewController {
var contentHeight: CGFloat = layout.intrinsicInsets.bottom
var frames: [Int: CGRect] = [:]
let measureView = ComponentHostView<ImageCache>()
let imageCache = ImageCache()
let measureView = ComponentHostView<DayEnvironment>()
for i in 0 ..< self.months.count {
let monthSize = measureView.update(
transition: .immediate,
@ -1419,7 +1492,7 @@ public final class CalendarMessageScreen: ViewController {
selectedDays: nil
)),
environment: {
imageCache
self.dayEnvironment
},
containerSize: CGSize(width: layout.size.width, height: 10000.0
))
@ -1458,7 +1531,7 @@ public final class CalendarMessageScreen: ViewController {
}
validMonths.insert(i)
let monthView: ComponentHostView<ImageCache>
let monthView: ComponentHostView<DayEnvironment>
if let current = self.monthViews[i] {
monthView = current
} else {
@ -1516,7 +1589,7 @@ public final class CalendarMessageScreen: ViewController {
selectedDays: self.selectionState?.dayRange
)),
environment: {
self.imageCache
self.dayEnvironment
},
containerSize: CGSize(width: width, height: 10000.0
))
@ -1582,14 +1655,14 @@ public final class CalendarMessageScreen: ViewController {
updatedMedia[i] = [:]
}
for day in 0 ..< self.months[i].numberOfDays {
let firstDayTimestamp = Int32(self.months[i].firstDay.timeIntervalSince1970)
let firstDayTimestamp = Int32(self.months[i].firstDay.timeIntervalSince1970)
for day in 0 ..< self.months[i].numberOfDays {
let dayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(day)
let nextDayTimestamp = firstDayTimestamp + 24 * 60 * 60 * Int32(day - 1)
let nextDayTimestamp = dayTimestamp + 24 * 60 * 60
for message in messageMap {
if message.timestamp <= dayTimestamp && message.timestamp >= nextDayTimestamp {
if message.timestamp >= dayTimestamp && message.timestamp < nextDayTimestamp {
mediaLoop: for media in message.media {
switch media {
case _ as TelegramMediaImage, _ as TelegramMediaFile:
@ -1644,9 +1717,9 @@ public final class CalendarMessageScreen: ViewController {
//TODO:localize
self.navigationItem.setTitle("Calendar", animated: false)
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
/*if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Select, style: .plain, target: self, action: #selector(self.toggleSelectPressed)), animated: false)
}
}*/
}
required public init(coder aDecoder: NSCoder) {

View File

@ -1111,7 +1111,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.currentItems = items
self.currentActionsMinHeight = minHeight
let previousActionsContainerNode = self.actionsContainerNode
let previousActionsContainerFrame = previousActionsContainerNode.view.convert(previousActionsContainerNode.bounds, to: self.view)
self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: items, getController: { [weak self] in
@ -1690,9 +1690,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
switch maybePassthrough {
case .ignore:
break
case let .dismiss(consume):
case let .dismiss(consume, hitTestResult):
self.getController()?.dismiss(completion: nil)
if let hitTestResult = hitTestResult {
return hitTestResult
}
if !consume {
return nil
}
@ -1862,7 +1865,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
public enum HandledTouchEvent {
case ignore
case dismiss(consume: Bool)
case dismiss(consume: Bool, result: UIView?)
}
public var passthroughTouchEvent: ((UIView, CGPoint) -> HandledTouchEvent)?

View File

@ -101,6 +101,9 @@ public final class ConstantDisplayLinkAnimator {
self.displayLink = CADisplayLink(target: DisplayLinkTarget({ [weak self] in
self?.tick()
}), selector: #selector(DisplayLinkTarget.event))
if #available(iOS 15.0, *) {
self.displayLink?.preferredFrameRateRange = CAFrameRateRange(minimum: 60.0, maximum: 120.0, preferred: 120.0)
}
self.displayLink.isPaused = true
self.displayLink.add(to: RunLoop.main, forMode: .common)
}

View File

@ -426,7 +426,7 @@ public final class SparseItemGrid: ASDisplayNode {
let zoomLevel: ZoomLevel
private let scrollView: UIScrollView
let scrollView: UIScrollView
private let shimmer: Shimmer
var theme: PresentationTheme
@ -448,6 +448,8 @@ public final class SparseItemGrid: ASDisplayNode {
let coveringOffsetUpdated: (Viewport, ContainedViewLayoutTransition) -> Void
private var decelerationAnimator: ConstantDisplayLinkAnimator?
init(theme: PresentationTheme, zoomLevel: ZoomLevel, maybeLoadHoleAnchor: @escaping (HoleAnchor, HoleLocation) -> Void, coveringOffsetUpdated: @escaping (Viewport, ContainedViewLayoutTransition) -> Void) {
self.theme = theme
self.zoomLevel = zoomLevel
@ -487,6 +489,10 @@ public final class SparseItemGrid: ASDisplayNode {
@objc func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.items?.itemBinding.didScroll()
if let decelerationAnimator = self.decelerationAnimator {
self.decelerationAnimator = nil
decelerationAnimator.invalidate()
}
}
@objc func scrollViewDidScroll(_ scrollView: UIScrollView) {
@ -638,10 +644,16 @@ public final class SparseItemGrid: ASDisplayNode {
}
func anchorItem(at point: CGPoint) -> Item? {
guard let items = self.items, !items.items.isEmpty else {
guard let items = self.items, !items.items.isEmpty, let layout = self.layout else {
return nil
}
if layout.containerLayout.lockScrollingAtTop {
if let item = items.item(at: 0) {
return item
}
}
let localPoint = self.scrollView.convert(point, from: self.view)
var closestItem: (CGFloat, AnyHashable)?
@ -714,6 +726,49 @@ public final class SparseItemGrid: ASDisplayNode {
self.scrollView.setContentOffset(self.scrollView.contentOffset, animated: false)
}
func transferVelocity(_ velocity: CGFloat) {
if velocity <= 0.0 {
return
}
self.decelerationAnimator?.isPaused = true
let startTime = CACurrentMediaTime()
var currentOffset = self.scrollView.contentOffset
let decelerationRate: CGFloat = 0.998
self.scrollViewDidEndDragging(self.scrollView, willDecelerate: true)
self.decelerationAnimator = ConstantDisplayLinkAnimator(update: { [weak self] in
guard let strongSelf = self else {
return
}
let t = CACurrentMediaTime() - startTime
var currentVelocity = velocity * 15.0 * CGFloat(pow(Double(decelerationRate), 1000.0 * t))
currentOffset.y += currentVelocity
let maxOffset = strongSelf.scrollView.contentSize.height - strongSelf.scrollView.bounds.height
if currentOffset.y >= maxOffset {
currentOffset.y = maxOffset
currentVelocity = 0.0
}
if currentOffset.y < 0.0 {
currentOffset.y = 0.0
currentVelocity = 0.0
}
var didEnd = false
if abs(currentVelocity) < 0.1 {
strongSelf.decelerationAnimator?.isPaused = true
strongSelf.decelerationAnimator = nil
didEnd = true
}
var contentOffset = strongSelf.scrollView.contentOffset
contentOffset.y = floorToScreenPixels(currentOffset.y)
strongSelf.scrollView.setContentOffset(contentOffset, animated: false)
strongSelf.scrollViewDidScroll(strongSelf.scrollView)
if didEnd {
strongSelf.scrollViewDidEndDecelerating(strongSelf.scrollView)
}
})
self.decelerationAnimator?.isPaused = false
}
private func updateVisibleItems(resetScrolling: Bool, synchronous: Bool, restoreScrollPosition: (y: CGFloat, index: Int)?) {
guard let layout = self.layout, let items = self.items else {
return
@ -951,7 +1006,7 @@ public final class SparseItemGrid: ASDisplayNode {
containerInsets: layout.containerLayout.insets,
contentHeight: contentHeight,
contentOffset: self.scrollView.bounds.minY,
isScrolling: self.scrollView.isDragging || self.scrollView.isDecelerating,
isScrolling: self.scrollView.isDragging || self.scrollView.isDecelerating || self.decelerationAnimator != nil,
dateString: dateString ?? "",
theme: self.theme,
transition: .immediate
@ -1006,13 +1061,15 @@ public final class SparseItemGrid: ASDisplayNode {
let previousProgress = self.currentProgress
self.currentProgress = progress
if let fromItem = self.fromViewport.anchorItem(at: fromAnchorFrame.center), let fromFrame = self.fromViewport.frameForItem(at: fromItem.index) {
let fixedAnchorPoint = CGPoint(x: fromAnchorFrame.minX + 1.0, y: fromAnchorFrame.minY + 1.0)
if let fromItem = self.fromViewport.anchorItem(at: fixedAnchorPoint), let fromFrame = self.fromViewport.frameForItem(at: fromItem.index) {
fromAnchorFrame.origin.y = fromFrame.midY
fromAnchorFrame.origin.x = fromFrame.midX
fromAnchorFrame.size.width = 0.0
}
if let toItem = self.toViewport.anchorItem(at: fromAnchorFrame.center), let toFrame = self.toViewport.frameForItem(at: toItem.index) {
if let toItem = self.toViewport.anchorItem(at: fixedAnchorPoint), let toFrame = self.toViewport.frameForItem(at: toItem.index) {
toAnchorFrame.origin.y = toFrame.midY
toAnchorFrame.origin.x = toFrame.midX
toAnchorFrame.size.width = 0.0
@ -1056,7 +1113,7 @@ public final class SparseItemGrid: ASDisplayNode {
completion()
})
let fromAlphaStartProgress: CGFloat = 0.6
let fromAlphaStartProgress: CGFloat = 0.7
let fromAlphaEndProgress: CGFloat = 1.0
let fromAlphaProgress = max(0.0, progress - fromAlphaStartProgress) / (fromAlphaEndProgress - fromAlphaStartProgress)
@ -1105,6 +1162,7 @@ public final class SparseItemGrid: ASDisplayNode {
}
public var cancelExternalContentGestures: (() -> Void)?
public var zoomLevelUpdated: ((ZoomLevel) -> Void)?
public init(theme: PresentationTheme) {
self.theme = theme
@ -1288,7 +1346,9 @@ public final class SparseItemGrid: ASDisplayNode {
let previousViewport = strongSelf.currentViewport
strongSelf.currentViewport = progress < 0.5 ? currentViewportTransition.fromViewport : currentViewportTransition.toViewport
let updatedViewport = progress < 0.5 ? currentViewportTransition.fromViewport : currentViewportTransition.toViewport
strongSelf.currentViewport = updatedViewport
strongSelf.zoomLevelUpdated?(updatedViewport.zoomLevel)
if let previousViewport = previousViewport, previousViewport !== strongSelf.currentViewport {
previousViewport.removeFromSupernode()
@ -1319,7 +1379,7 @@ public final class SparseItemGrid: ASDisplayNode {
self.scrollingArea.isHidden = lockScrollingAtTop
self.tapRecognizer?.isEnabled = fixedItemHeight == nil
self.pinchRecognizer?.isEnabled = fixedItemHeight == nil && !lockScrollingAtTop
self.pinchRecognizer?.isEnabled = fixedItemHeight == nil
if self.currentViewport == nil {
let currentViewport = Viewport(theme: self.theme, zoomLevel: self.initialZoomLevel ?? ZoomLevel(rawValue: 3), maybeLoadHoleAnchor: { [weak self] holeAnchor, location in
@ -1544,4 +1604,30 @@ public final class SparseItemGrid: ASDisplayNode {
itemShimmerLayer.removeFromSuperlayer()
}
}
public func hitTestResultForScrolling() -> UIView? {
if let _ = self.currentViewportTransition {
return nil
} else if let currentViewport = self.currentViewport {
return currentViewport.scrollView
} else {
return nil
}
}
public func brieflyDisableTouchActions() {
let tapEnabled = self.tapRecognizer?.isEnabled ?? true
self.tapRecognizer?.isEnabled = false
let pinchEnabled = self.pinchRecognizer?.isEnabled ?? true
self.pinchRecognizer?.isEnabled = false
DispatchQueue.main.async {
self.tapRecognizer?.isEnabled = tapEnabled
self.pinchRecognizer?.isEnabled = pinchEnabled
}
}
public func transferVelocity(_ velocity: CGFloat) {
self.currentViewport?.transferVelocity(velocity)
}
}

View File

@ -769,7 +769,7 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
self.offsetBarTimer = nil
let transition: ContainedViewLayoutTransition = .animated(duration: 0.1, curve: .easeInOut)
transition.updateSublayerTransformOffset(layer: self.dateIndicator.layer, offset: self.beganAtDateIndicator ? CGPoint(x: -80.0, y: 0.0) : CGPoint(x: -3.0, y: 0.0))
transition.updateSublayerTransformOffset(layer: self.dateIndicator.layer, offset: CGPoint(x: -80.0, y: 0.0))
self.updateLineIndicator(transition: transition)
}

View File

@ -670,8 +670,8 @@ public final class SparseMessageCalendar {
var messagesByDay: [Int32: Message] = [:]
for period in periods {
switch period {
case let .searchResultsCalendarPeriod(date, minMsgId, maxMsgId, _):
if let message = transaction.getMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: maxMsgId)) {
case let .searchResultsCalendarPeriod(date, minMsgId, _, _):
if let message = transaction.getMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: minMsgId)) {
messagesByDay[date] = message
}
if let minMessageIdValue = minMessageId {

View File

@ -676,7 +676,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
PresentationAppIcon(name: "BlackFilledIcon", imageName: "BlackFilledIcon")
]
if buildConfig.isInternalBuild {
icons.append(PresentationAppIcon(name: "WhiteFilled", imageName: "WhiteFilledIcon"))
icons.append(PresentationAppIcon(name: "WhiteFilledIcon", imageName: "WhiteFilledIcon"))
}
return icons
} else {

View File

@ -1554,6 +1554,13 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
self.itemGrid.cancelExternalContentGestures = { [weak self] in
self?.contextGestureContainerNode.cancelGesture()
}
self.itemGrid.zoomLevelUpdated = { [weak self] zoomLevel in
guard let strongSelf = self else {
return
}
let _ = updateVisualMediaStoredState(postbox: strongSelf.context.account.postbox, peerId: strongSelf.peerId, messageTag: strongSelf.stateTag, state: VisualMediaStoredState(zoomLevel: Int32(zoomLevel.rawValue))).start()
}
self._itemInteraction = VisualMediaItemInteraction(
openMessage: { [weak self] message in
@ -1644,7 +1651,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
}
let rect = strongSelf.itemGrid.frameForItem(layer: itemLayer)
let proxyNode = ASDisplayNode()
/*let proxyNode = ASDisplayNode()
proxyNode.frame = rect
proxyNode.contents = itemLayer.contents
proxyNode.isHidden = true
@ -1656,9 +1663,9 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
Queue.mainQueue().after(1.0, {
escapeNotification.keep()
})
})*/
strongSelf.chatControllerInteraction.openMessageContextActions(message, proxyNode, proxyNode.bounds, gesture)
strongSelf.chatControllerInteraction.openMessageContextActions(message, strongSelf, rect, gesture)
strongSelf.itemGrid.cancelGestures()
}
@ -1957,6 +1964,14 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
func scrollToTop() -> Bool {
return self.itemGrid.scrollToTop()
}
func hitTestResultForScrolling() -> UIView? {
return self.itemGrid.hitTestResultForScrolling()
}
func brieflyDisableTouchActions() {
self.itemGrid.brieflyDisableTouchActions()
}
func findLoadedMessage(id: MessageId) -> Message? {
guard let items = self.items else {
@ -1991,45 +2006,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
}
func transferVelocity(_ velocity: CGFloat) {
/*if velocity > 0.0 {
self.decelerationAnimator?.isPaused = true
let startTime = CACurrentMediaTime()
var currentOffset = self.scrollNode.view.contentOffset
let decelerationRate: CGFloat = 0.998
self.scrollViewDidEndDragging(self.scrollNode.view, willDecelerate: true)
self.decelerationAnimator = ConstantDisplayLinkAnimator(update: { [weak self] in
guard let strongSelf = self else {
return
}
let t = CACurrentMediaTime() - startTime
var currentVelocity = velocity * 15.0 * CGFloat(pow(Double(decelerationRate), 1000.0 * t))
currentOffset.y += currentVelocity
let maxOffset = strongSelf.scrollNode.view.contentSize.height - strongSelf.scrollNode.bounds.height
if currentOffset.y >= maxOffset {
currentOffset.y = maxOffset
currentVelocity = 0.0
}
if currentOffset.y < 0.0 {
currentOffset.y = 0.0
currentVelocity = 0.0
}
var didEnd = false
if abs(currentVelocity) < 0.1 {
strongSelf.decelerationAnimator?.isPaused = true
strongSelf.decelerationAnimator = nil
didEnd = true
}
var contentOffset = strongSelf.scrollNode.view.contentOffset
contentOffset.y = floorToScreenPixels(currentOffset.y)
strongSelf.scrollNode.view.setContentOffset(contentOffset, animated: false)
strongSelf.scrollViewDidScroll(strongSelf.scrollNode.view)
if didEnd {
strongSelf.scrollViewDidEndDecelerating(strongSelf.scrollNode.view)
}
})
self.decelerationAnimator?.isPaused = false
}*/
self.itemGrid.transferVelocity(velocity)
}
func cancelPreviewGestures() {

View File

@ -2059,7 +2059,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
switch previewData {
case let .gallery(gallery):
gallery.setHintWillBePresentedInPreviewingContext(true)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node)), items: items |> map { ContextController.Items(items: $0) }, gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node, sourceRect: rect)), items: items |> map { ContextController.Items(items: $0) }, gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController)
case .instantPage:
break
@ -6120,9 +6120,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
items.append(.action(generateAction(true)))
items.append(.action(generateAction(false)))
var ignoreNextActions = false
items.append(.action(ContextMenuActionItem(text: "Show Calendar", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor)
}, action: { _, a in
if ignoreNextActions {
return
}
ignoreNextActions = true
a(.default)
self?.openMediaCalendar()
@ -6204,14 +6209,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil)
guard let localResult = strongSelf.hitTest(localPoint, with: nil) else {
return .dismiss(consume: true)
return .dismiss(consume: true, result: nil)
}
var testView: UIView? = localResult
while true {
if let testViewValue = testView {
if testViewValue.asyncdisplaykit_node is PeerInfoVisualMediaPaneNode {
return .dismiss(consume: false)
if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode {
node.brieflyDisableTouchActions()
return .dismiss(consume: false, result: nil)
} else {
testView = testViewValue.superview
}
@ -6220,7 +6226,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
}
return .dismiss(consume: true)
return .dismiss(consume: true, result: nil)
}
strongSelf.mediaGalleryContextMenu = contextController
controller.presentInGlobalOverlay(contextController)