Shared media improvements

This commit is contained in:
Ali 2021-10-24 22:27:23 +04:00
parent 05e57b1923
commit d28747cb00
9 changed files with 415 additions and 184 deletions

View File

@ -18,10 +18,11 @@ public protocol ContextActionNodeProtocol: ASDisplayNode {
final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
private var presentationData: PresentationData
private var action: ContextMenuActionItem
private(set) var action: ContextMenuActionItem
private let getController: () -> ContextControllerProtocol?
private let actionSelected: (ContextMenuActionResult) -> Void
private let requestLayout: () -> Void
private let requestUpdateAction: (AnyHashable, ContextMenuActionItem) -> Void
private let backgroundNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
@ -40,12 +41,13 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
return true
}
init(presentationData: PresentationData, action: ContextMenuActionItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, requestLayout: @escaping () -> Void) {
init(presentationData: PresentationData, action: ContextMenuActionItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, requestLayout: @escaping () -> Void, requestUpdateAction: @escaping (AnyHashable, ContextMenuActionItem) -> Void) {
self.presentationData = presentationData
self.action = action
self.getController = getController
self.actionSelected = actionSelected
self.requestLayout = requestLayout
self.requestUpdateAction = requestUpdateAction
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
@ -317,6 +319,37 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
self.performAction()
}
func updateAction(item: ContextMenuActionItem) {
self.action = item
let textColor: UIColor
switch self.action.textColor {
case .primary:
textColor = self.presentationData.theme.contextMenu.primaryColor
case .destructive:
textColor = self.presentationData.theme.contextMenu.destructiveColor
case .disabled:
textColor = self.presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.4)
}
let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize)
let titleFont: UIFont
switch self.action.textFont {
case .regular:
titleFont = textFont
case let .custom(customFont):
titleFont = customFont
}
self.textNode.attributedText = NSAttributedString(string: self.action.text, font: titleFont, textColor: textColor)
if self.action.iconSource == nil {
self.iconNode.image = self.action.icon(self.presentationData.theme)
}
self.requestLayout()
}
func performAction() {
guard let controller = self.getController() else {
return
@ -326,38 +359,11 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
dismissWithResult: { [weak self] result in
self?.actionSelected(result)
},
updateAction: { [weak self] updatedAction in
updateAction: { [weak self] id, updatedAction in
guard let strongSelf = self else {
return
}
strongSelf.action = updatedAction
let textColor: UIColor
switch strongSelf.action.textColor {
case .primary:
textColor = strongSelf.presentationData.theme.contextMenu.primaryColor
case .destructive:
textColor = strongSelf.presentationData.theme.contextMenu.destructiveColor
case .disabled:
textColor = strongSelf.presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.4)
}
let textFont = Font.regular(strongSelf.presentationData.listsFontSize.baseDisplaySize)
let titleFont: UIFont
switch strongSelf.action.textFont {
case .regular:
titleFont = textFont
case let .custom(customFont):
titleFont = customFont
}
strongSelf.textNode.attributedText = NSAttributedString(string: strongSelf.action.text, font: titleFont, textColor: textColor)
if strongSelf.action.iconSource == nil {
strongSelf.iconNode.image = strongSelf.action.icon(strongSelf.presentationData.theme)
}
strongSelf.requestLayout()
strongSelf.requestUpdateAction(id, updatedAction)
}
))
}

View File

@ -79,11 +79,15 @@ private final class InnerActionsContainerNode: ASDisplayNode {
self.containerNode.cornerRadius = 14.0
self.containerNode.backgroundColor = presentationData.theme.contextMenu.backgroundColor
var requestUpdateAction: ((AnyHashable, ContextMenuActionItem) -> Void)?
var itemNodes: [ContextItemNode] = []
for i in 0 ..< items.count {
switch items[i] {
case let .action(action):
itemNodes.append(.action(ContextActionNode(presentationData: presentationData, action: action, getController: getController, actionSelected: actionSelected, requestLayout: requestLayout)))
itemNodes.append(.action(ContextActionNode(presentationData: presentationData, action: action, getController: getController, actionSelected: actionSelected, requestLayout: requestLayout, requestUpdateAction: { id, action in
requestUpdateAction?(id, action)
})))
if i != items.count - 1 {
switch items[i + 1] {
case .action, .custom:
@ -117,6 +121,23 @@ private final class InnerActionsContainerNode: ASDisplayNode {
super.init()
requestUpdateAction = { [weak self] id, action in
guard let strongSelf = self else {
return
}
loop: for itemNode in strongSelf.itemNodes {
switch itemNode {
case let .action(contextActionNode):
if contextActionNode.action.id == id {
contextActionNode.updateAction(item: action)
break loop
}
default:
break
}
}
}
self.addSubnode(self.containerNode)
self.itemNodes.forEach({ itemNode in

View File

@ -71,15 +71,16 @@ public final class ContextMenuActionItem {
public final class Action {
public let controller: ContextControllerProtocol
public let dismissWithResult: (ContextMenuActionResult) -> Void
public let updateAction: (ContextMenuActionItem) -> Void
public let updateAction: (AnyHashable, ContextMenuActionItem) -> Void
init(controller: ContextControllerProtocol, dismissWithResult: @escaping (ContextMenuActionResult) -> Void, updateAction: @escaping (ContextMenuActionItem) -> Void) {
init(controller: ContextControllerProtocol, dismissWithResult: @escaping (ContextMenuActionResult) -> Void, updateAction: @escaping (AnyHashable, ContextMenuActionItem) -> Void) {
self.controller = controller
self.dismissWithResult = dismissWithResult
self.updateAction = updateAction
}
}
public let id: AnyHashable?
public let text: String
public let textColor: ContextMenuActionItemTextColor
public let textFont: ContextMenuActionItemFont
@ -90,6 +91,7 @@ public final class ContextMenuActionItem {
public let action: ((Action) -> Void)?
convenience public init(
id: AnyHashable? = nil,
text: String,
textColor: ContextMenuActionItemTextColor = .primary,
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
@ -100,6 +102,7 @@ public final class ContextMenuActionItem {
action: ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?
) {
self.init(
id: id,
text: text,
textColor: textColor,
textLayout: textLayout,
@ -116,6 +119,7 @@ public final class ContextMenuActionItem {
}
public init(
id: AnyHashable? = nil,
text: String,
textColor: ContextMenuActionItemTextColor = .primary,
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
@ -125,6 +129,7 @@ public final class ContextMenuActionItem {
iconSource: ContextMenuActionItemIconSource? = nil,
action: ((Action) -> Void)?
) {
self.id = id
self.text = text
self.textColor = textColor
self.textFont = textFont

View File

@ -88,7 +88,15 @@ public final class ContextControllerSourceNode: ASDisplayNode {
}
}
contextGesture.activated = { [weak self] gesture, location in
if let activated = self?.activated {
guard let strongSelf = self else {
gesture.cancel()
return
}
if let customActivationProgress = strongSelf.customActivationProgress {
customActivationProgress(0.0, .ended(0.0))
}
if let activated = strongSelf.activated {
activated(gesture, location)
} else {
gesture.cancel()

View File

@ -342,9 +342,11 @@ public final class SparseItemGrid: ASDisplayNode {
return self.displayLayer.frame
} set(value) {
if let layer = self.layer {
layer.frame = value
layer.bounds = CGRect(origin: CGPoint(), size: value.size)
layer.position = value.center
} else if let view = self.view {
view.frame = value
view.bounds = CGRect(origin: CGPoint(), size: value.size)
view.center = value.center
} else {
preconditionFailure()
}
@ -564,6 +566,22 @@ public final class SparseItemGrid: ASDisplayNode {
}
}
func visualItem(at point: CGPoint) -> SparseItemGridDisplayItem? {
guard let items = self.items, !items.items.isEmpty else {
return nil
}
let localPoint = self.scrollView.convert(point, from: self.view)
for (_, visibleItem) in self.visibleItems {
if visibleItem.frame.contains(localPoint) {
return visibleItem
}
}
return nil
}
func item(at point: CGPoint) -> Item? {
guard let items = self.items, !items.items.isEmpty else {
return nil
@ -658,6 +676,10 @@ public final class SparseItemGrid: ASDisplayNode {
}
}
func stopScrolling() {
self.scrollView.setContentOffset(self.scrollView.contentOffset, animated: false)
}
private func updateVisibleItems(resetScrolling: Bool, synchronous: Bool, restoreScrollPosition: (y: CGFloat, index: Int)?) {
guard let layout = self.layout, let items = self.items else {
return
@ -1219,7 +1241,7 @@ public final class SparseItemGrid: ASDisplayNode {
self.scrollingArea.isHidden = lockScrollingAtTop
self.tapRecognizer?.isEnabled = fixedItemHeight == nil
self.pinchRecognizer?.isEnabled = fixedItemHeight == nil
self.pinchRecognizer?.isEnabled = fixedItemHeight == nil && !lockScrollingAtTop
if self.currentViewport == nil {
let currentViewport = Viewport(zoomLevel: self.initialZoomLevel ?? ZoomLevel(rawValue: 3), maybeLoadHoleAnchor: { [weak self] holeAnchor, location in
@ -1359,6 +1381,13 @@ public final class SparseItemGrid: ASDisplayNode {
return self.view.convert(currentViewport.frameForItem(layer: layer), from: currentViewport.view)
}
public func item(at point: CGPoint) -> SparseItemGridDisplayItem? {
guard let currentViewport = self.currentViewport else {
return nil
}
return currentViewport.visualItem(at: point)
}
public func scrollToItem(at index: Int) {
guard let currentViewport = self.currentViewport else {
return
@ -1380,4 +1409,15 @@ public final class SparseItemGrid: ASDisplayNode {
public func updateScrollingAreaTooltip(tooltip: SparseItemGridScrollingArea.DisplayTooltip) {
self.scrollingArea.displayTooltip = tooltip
}
public func cancelGestures() {
self.tapRecognizer?.state = .cancelled
self.pinchRecognizer?.state = .cancelled
}
public func hideScrollingArea() {
self.currentViewport?.stopScrolling()
self.scrollingArea.hideScroller()
}
}

View File

@ -164,16 +164,28 @@ public final class TooltipComponent: Component {
private var icon: ComponentHostView<Empty>?
private let content: ComponentHostView<Empty>
private let regularMaskImage: UIImage
private let invertedMaskImage: UIImage
init() {
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
self.regularMaskImage = 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)
})!.stretchableImage(withLeftCapWidth: 16, topCapHeight: 33)
self.invertedMaskImage = generateImage(CGSize(width: 42.0, height: 42.0), contextGenerator: { 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: 9)
self.backgroundViewMask.image = self.regularMaskImage
self.content = ComponentHostView<Empty>()
@ -235,14 +247,21 @@ public final class TooltipComponent: Component {
if contentRect.minX < 0.0 {
contentRect.origin.x = component.arrowLocation.maxX
}
if contentRect.minY < 0.0 {
contentRect.origin.y = component.arrowLocation.minY - contentRect.height
let maskedBackgroundFrame: CGRect
if contentRect.maxY > availableSize.height {
self.backgroundViewMask.image = self.invertedMaskImage
contentRect.origin.y = component.arrowLocation.minY - contentRect.height - 4.0
maskedBackgroundFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: contentRect.minY - 4.0 + 3.0), size: CGSize(width: contentRect.width + 4.0, height: contentRect.height + 8.0))
self.backgroundViewMask.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: maskedBackgroundFrame.size)
} else {
self.backgroundViewMask.image = self.regularMaskImage
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.backgroundViewMask.frame = CGRect(origin: CGPoint(), size: maskedBackgroundFrame.size)
}
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))
@ -800,7 +819,7 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
let topIndicatorInset: CGFloat = indicatorVerticalInset + containerInsets.top
let bottomIndicatorInset: CGFloat = indicatorVerticalInset + containerInsets.bottom
let scrollIndicatorHeight = max(44.0, ceil(scrollIndicatorHeightFraction * containerSize.height))
let scrollIndicatorHeight: CGFloat = 44.0
let indicatorPositionFraction = min(1.0, max(0.0, contentOffset / (contentHeight - containerSize.height)))
@ -964,4 +983,12 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
return nil
}
public func hideScroller() {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)
transition.updateAlpha(layer: self.dateIndicator.layer, alpha: 0.0)
transition.updateAlpha(layer: self.lineIndicator.layer, alpha: 0.0)
self.dismissLineTooltip()
}
}

View File

@ -732,11 +732,13 @@ final class CachedPeerInvitationImporters: Codable {
self.dates = dates
var abouts: [PeerId: String] = [:]
let aboutsArray = try container.decode([DictionaryPair].self, forKey: "abouts")
let aboutsArray = try container.decodeIfPresent([DictionaryPair].self, forKey: "abouts")
if let aboutsArray = aboutsArray {
for aboutPair in aboutsArray {
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(aboutPair.key))
abouts[peerId] = aboutPair.value
}
}
self.abouts = abouts
self.count = try container.decode(Int32.self, forKey: "count")

View File

@ -1055,7 +1055,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
}
var duration: Int32?
if layer.bounds.width > 80.0, let file = selectedMedia as? TelegramMediaFile {
if layer.bounds.width > 80.0, let file = selectedMedia as? TelegramMediaFile, !file.isAnimated {
duration = file.duration
}
layer.updateDuration(duration: duration)
@ -1172,6 +1172,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
weak var parentController: ViewController?
private let contextGestureContainerNode: ContextControllerSourceNode
private let itemGrid: SparseItemGrid
private let itemGridBinding: SparseItemGridBindingImpl
private let directMediaImageCache: DirectMediaImageCache
@ -1222,6 +1223,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
private let stateTag: MessageTags
private var storedStateDisposable: Disposable?
private weak var currentGestureItem: SparseItemGridDisplayItem?
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
self.context = context
self.peerId = peerId
@ -1230,6 +1233,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
self.contentTypePromise = ValuePromise<ContentType>(contentType)
self.stateTag = tagMaskForType(contentType)
self.contextGestureContainerNode = ContextControllerSourceNode()
self.itemGrid = SparseItemGrid()
self.directMediaImageCache = DirectMediaImageCache(account: context.account)
@ -1398,7 +1402,94 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
)
self.itemInteraction.selectedMessageIds = chatControllerInteraction.selectionState.flatMap { $0.selectedIds }
self.addSubnode(self.itemGrid)
self.contextGestureContainerNode.isGestureEnabled = !useListItems
self.contextGestureContainerNode.addSubnode(self.itemGrid)
self.addSubnode(self.contextGestureContainerNode)
self.contextGestureContainerNode.shouldBegin = { [weak self] point in
guard let strongSelf = self else {
return false
}
guard let item = strongSelf.itemGrid.item(at: point) else {
return false
}
strongSelf.currentGestureItem = item
return true
}
self.contextGestureContainerNode.customActivationProgress = { [weak self] progress, update in
guard let strongSelf = self, let currentGestureItem = strongSelf.currentGestureItem else {
return
}
guard let itemLayer = currentGestureItem.layer else {
return
}
let targetContentRect = CGRect(origin: CGPoint(), size: itemLayer.bounds.size)
let scaleSide = itemLayer.bounds.width
let minScale: CGFloat = max(0.7, (scaleSide - 15.0) / scaleSide)
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
let originalCenterOffsetX: CGFloat = itemLayer.bounds.width / 2.0 - targetContentRect.midX
let scaledCenterOffsetX: CGFloat = originalCenterOffsetX * currentScale
let originalCenterOffsetY: CGFloat = itemLayer.bounds.height / 2.0 - targetContentRect.midY
let scaledCenterOffsetY: CGFloat = originalCenterOffsetY * currentScale
let scaleMidX: CGFloat = scaledCenterOffsetX - originalCenterOffsetX
let scaleMidY: CGFloat = scaledCenterOffsetY - originalCenterOffsetY
switch update {
case .update:
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
itemLayer.transform = sublayerTransform
case .begin:
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
itemLayer.transform = sublayerTransform
case .ended:
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
let previousTransform = itemLayer.transform
itemLayer.transform = sublayerTransform
itemLayer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: sublayerTransform), keyPath: "transform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
}
}
self.contextGestureContainerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let currentGestureItem = strongSelf.currentGestureItem else {
return
}
strongSelf.currentGestureItem = nil
guard let itemLayer = currentGestureItem.layer as? ItemLayer else {
return
}
guard let message = itemLayer.item?.message else {
return
}
let rect = strongSelf.itemGrid.frameForItem(layer: itemLayer)
let proxyNode = ASDisplayNode()
proxyNode.frame = rect
proxyNode.contents = itemLayer.contents
proxyNode.isHidden = true
strongSelf.addSubnode(proxyNode)
let escapeNotification = EscapeNotification {
proxyNode.removeFromSupernode()
}
Queue.mainQueue().after(1.0, {
escapeNotification.keep()
})
strongSelf.chatControllerInteraction.openMessageContextActions(message, proxyNode, proxyNode.bounds, gesture)
strongSelf.itemGrid.cancelGestures()
}
self.storedStateDisposable = (visualMediaStoredState(postbox: context.account.postbox, peerId: peerId, messageTag: self.stateTag)
|> deliverOnMainQueue).start(next: { [weak self] value in
@ -1509,7 +1600,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
} else if photoCount != 0 {
return PeerInfoStatusData(text: "\(photoCount) photos", isActivity: false)
} else if videoCount != 0 {
return PeerInfoStatusData(text: "\(photoCount) videos", isActivity: false)
return PeerInfoStatusData(text: "\(videoCount) videos", isActivity: false)
} else {
return nil
}
@ -1607,6 +1698,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
self.contentType = contentType
self.contentTypePromise.set(contentType)
self.itemGrid.hideScrollingArea()
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, tag: tagMaskForType(self.contentType))
self.isRequestingView = false
self.requestHistoryAroundVisiblePosition(synchronous: true, reloadAtTop: true)
@ -1836,6 +1929,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData)
transition.updateFrame(node: self.contextGestureContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.itemGrid, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
if let items = self.items {
let fixedItemHeight: CGFloat?

View File

@ -6022,10 +6022,32 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
private func displayMediaGalleryContextMenu(source: ContextReferenceContentNode) {
guard let controller = self.controller else {
let summaryTags: [MessageTags] = [.photo, .video]
let peerId = self.peerId
let _ = (context.account.postbox.combinedView(keys: summaryTags.map { tag in
return PostboxViewKey.historyTagSummaryView(tag: tag, peerId: peerId, namespace: Namespaces.Message.Cloud)
})
|> deliverOnMainQueue).start(next: { [weak self] views in
guard let strongSelf = self else {
return
}
guard let pane = self.paneContainerNode.currentPane?.node as? PeerInfoVisualMediaPaneNode else {
var mediaCount: [MessageTags: Int32] = [:]
for tag in summaryTags {
if let view = views.views[PostboxViewKey.historyTagSummaryView(tag: tag, peerId: peerId, namespace: Namespaces.Message.Cloud)] as? MessageHistoryTagSummaryView {
mediaCount[tag] = view.count ?? 0
} else {
mediaCount[tag] = 0
}
}
let photoCount: Int32 = mediaCount[.photo] ?? 0
let videoCount: Int32 = mediaCount[.video] ?? 0
guard let controller = strongSelf.controller else {
return
}
guard let pane = strongSelf.paneContainerNode.currentPane?.node as? PeerInfoVisualMediaPaneNode else {
return
}
@ -6034,11 +6056,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
var recurseGenerateAction: ((Bool) -> ContextMenuActionItem)?
let generateAction: (Bool) -> ContextMenuActionItem = { [weak pane] isZoomIn in
var canZoom: Bool = true
if !"".isEmpty {
canZoom = false
}
return ContextMenuActionItem(text: isZoomIn ? "Zoom In" : "Zoom Out", textColor: canZoom ? .primary : .disabled, icon: { theme in
let nextZoomLevel = isZoomIn ? pane?.availableZoomLevels().increment : pane?.availableZoomLevels().decrement
let canZoom: Bool = nextZoomLevel != nil
return ContextMenuActionItem(id: isZoomIn ? 0 : 1, 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
guard let pane = pane, let zoomLevel = isZoomIn ? pane.availableZoomLevels().increment : pane.availableZoomLevels().decrement else {
@ -6046,7 +6067,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
pane.updateZoomLevel(level: zoomLevel)
if let recurseGenerateAction = recurseGenerateAction {
action.updateAction(recurseGenerateAction(isZoomIn))
action.updateAction(0, recurseGenerateAction(true))
action.updateAction(1, recurseGenerateAction(false))
}
} : nil)
}
@ -6059,11 +6081,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
items.append(.action(ContextMenuActionItem(text: "Show Calendar", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
}, action: { _, a in
a(.default)
self?.openMediaCalendar()
})))
if photoCount != 0 && videoCount != 0 {
items.append(.separator)
let showPhotos: Bool
@ -6129,8 +6153,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
pane.updateContentType(contentType: updatedContentType)
})))
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(items: items)), gesture: nil)
contextController.passthroughTouchEvent = { [weak self] sourceView, point in
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(items: items)), gesture: nil)
contextController.passthroughTouchEvent = { sourceView, point in
guard let strongSelf = self else {
return .ignore
}
@ -6155,8 +6181,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return .dismiss(consume: true)
}
self.mediaGalleryContextMenu = contextController
strongSelf.mediaGalleryContextMenu = contextController
controller.presentInGlobalOverlay(contextController)
})
}
private func openMediaCalendar() {