mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Shared media improvements
This commit is contained in:
parent
05e57b1923
commit
d28747cb00
@ -18,10 +18,11 @@ public protocol ContextActionNodeProtocol: ASDisplayNode {
|
|||||||
|
|
||||||
final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var action: ContextMenuActionItem
|
private(set) var action: ContextMenuActionItem
|
||||||
private let getController: () -> ContextControllerProtocol?
|
private let getController: () -> ContextControllerProtocol?
|
||||||
private let actionSelected: (ContextMenuActionResult) -> Void
|
private let actionSelected: (ContextMenuActionResult) -> Void
|
||||||
private let requestLayout: () -> Void
|
private let requestLayout: () -> Void
|
||||||
|
private let requestUpdateAction: (AnyHashable, ContextMenuActionItem) -> Void
|
||||||
|
|
||||||
private let backgroundNode: ASDisplayNode
|
private let backgroundNode: ASDisplayNode
|
||||||
private let highlightedBackgroundNode: ASDisplayNode
|
private let highlightedBackgroundNode: ASDisplayNode
|
||||||
@ -40,12 +41,13 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
|||||||
return true
|
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.presentationData = presentationData
|
||||||
self.action = action
|
self.action = action
|
||||||
self.getController = getController
|
self.getController = getController
|
||||||
self.actionSelected = actionSelected
|
self.actionSelected = actionSelected
|
||||||
self.requestLayout = requestLayout
|
self.requestLayout = requestLayout
|
||||||
|
self.requestUpdateAction = requestUpdateAction
|
||||||
|
|
||||||
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
|
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
|
||||||
|
|
||||||
@ -317,6 +319,37 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
|||||||
self.performAction()
|
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() {
|
func performAction() {
|
||||||
guard let controller = self.getController() else {
|
guard let controller = self.getController() else {
|
||||||
return
|
return
|
||||||
@ -326,38 +359,11 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
|||||||
dismissWithResult: { [weak self] result in
|
dismissWithResult: { [weak self] result in
|
||||||
self?.actionSelected(result)
|
self?.actionSelected(result)
|
||||||
},
|
},
|
||||||
updateAction: { [weak self] updatedAction in
|
updateAction: { [weak self] id, updatedAction in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.action = updatedAction
|
strongSelf.requestUpdateAction(id, 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()
|
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,11 +79,15 @@ private final class InnerActionsContainerNode: ASDisplayNode {
|
|||||||
self.containerNode.cornerRadius = 14.0
|
self.containerNode.cornerRadius = 14.0
|
||||||
self.containerNode.backgroundColor = presentationData.theme.contextMenu.backgroundColor
|
self.containerNode.backgroundColor = presentationData.theme.contextMenu.backgroundColor
|
||||||
|
|
||||||
|
var requestUpdateAction: ((AnyHashable, ContextMenuActionItem) -> Void)?
|
||||||
|
|
||||||
var itemNodes: [ContextItemNode] = []
|
var itemNodes: [ContextItemNode] = []
|
||||||
for i in 0 ..< items.count {
|
for i in 0 ..< items.count {
|
||||||
switch items[i] {
|
switch items[i] {
|
||||||
case let .action(action):
|
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 {
|
if i != items.count - 1 {
|
||||||
switch items[i + 1] {
|
switch items[i + 1] {
|
||||||
case .action, .custom:
|
case .action, .custom:
|
||||||
@ -117,6 +121,23 @@ private final class InnerActionsContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
super.init()
|
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.addSubnode(self.containerNode)
|
||||||
|
|
||||||
self.itemNodes.forEach({ itemNode in
|
self.itemNodes.forEach({ itemNode in
|
||||||
|
|||||||
@ -71,15 +71,16 @@ public final class ContextMenuActionItem {
|
|||||||
public final class Action {
|
public final class Action {
|
||||||
public let controller: ContextControllerProtocol
|
public let controller: ContextControllerProtocol
|
||||||
public let dismissWithResult: (ContextMenuActionResult) -> Void
|
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.controller = controller
|
||||||
self.dismissWithResult = dismissWithResult
|
self.dismissWithResult = dismissWithResult
|
||||||
self.updateAction = updateAction
|
self.updateAction = updateAction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public let id: AnyHashable?
|
||||||
public let text: String
|
public let text: String
|
||||||
public let textColor: ContextMenuActionItemTextColor
|
public let textColor: ContextMenuActionItemTextColor
|
||||||
public let textFont: ContextMenuActionItemFont
|
public let textFont: ContextMenuActionItemFont
|
||||||
@ -90,6 +91,7 @@ public final class ContextMenuActionItem {
|
|||||||
public let action: ((Action) -> Void)?
|
public let action: ((Action) -> Void)?
|
||||||
|
|
||||||
convenience public init(
|
convenience public init(
|
||||||
|
id: AnyHashable? = nil,
|
||||||
text: String,
|
text: String,
|
||||||
textColor: ContextMenuActionItemTextColor = .primary,
|
textColor: ContextMenuActionItemTextColor = .primary,
|
||||||
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
|
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
|
||||||
@ -100,6 +102,7 @@ public final class ContextMenuActionItem {
|
|||||||
action: ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?
|
action: ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?
|
||||||
) {
|
) {
|
||||||
self.init(
|
self.init(
|
||||||
|
id: id,
|
||||||
text: text,
|
text: text,
|
||||||
textColor: textColor,
|
textColor: textColor,
|
||||||
textLayout: textLayout,
|
textLayout: textLayout,
|
||||||
@ -116,6 +119,7 @@ public final class ContextMenuActionItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
|
id: AnyHashable? = nil,
|
||||||
text: String,
|
text: String,
|
||||||
textColor: ContextMenuActionItemTextColor = .primary,
|
textColor: ContextMenuActionItemTextColor = .primary,
|
||||||
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
|
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
|
||||||
@ -125,6 +129,7 @@ public final class ContextMenuActionItem {
|
|||||||
iconSource: ContextMenuActionItemIconSource? = nil,
|
iconSource: ContextMenuActionItemIconSource? = nil,
|
||||||
action: ((Action) -> Void)?
|
action: ((Action) -> Void)?
|
||||||
) {
|
) {
|
||||||
|
self.id = id
|
||||||
self.text = text
|
self.text = text
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.textFont = textFont
|
self.textFont = textFont
|
||||||
|
|||||||
@ -88,7 +88,15 @@ public final class ContextControllerSourceNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
contextGesture.activated = { [weak self] gesture, location in
|
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)
|
activated(gesture, location)
|
||||||
} else {
|
} else {
|
||||||
gesture.cancel()
|
gesture.cancel()
|
||||||
|
|||||||
@ -342,9 +342,11 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||||||
return self.displayLayer.frame
|
return self.displayLayer.frame
|
||||||
} set(value) {
|
} set(value) {
|
||||||
if let layer = self.layer {
|
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 {
|
} else if let view = self.view {
|
||||||
view.frame = value
|
view.bounds = CGRect(origin: CGPoint(), size: value.size)
|
||||||
|
view.center = value.center
|
||||||
} else {
|
} else {
|
||||||
preconditionFailure()
|
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? {
|
func item(at point: CGPoint) -> Item? {
|
||||||
guard let items = self.items, !items.items.isEmpty else {
|
guard let items = self.items, !items.items.isEmpty else {
|
||||||
return nil
|
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)?) {
|
private func updateVisibleItems(resetScrolling: Bool, synchronous: Bool, restoreScrollPosition: (y: CGFloat, index: Int)?) {
|
||||||
guard let layout = self.layout, let items = self.items else {
|
guard let layout = self.layout, let items = self.items else {
|
||||||
return
|
return
|
||||||
@ -1219,7 +1241,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||||||
self.scrollingArea.isHidden = lockScrollingAtTop
|
self.scrollingArea.isHidden = lockScrollingAtTop
|
||||||
|
|
||||||
self.tapRecognizer?.isEnabled = fixedItemHeight == nil
|
self.tapRecognizer?.isEnabled = fixedItemHeight == nil
|
||||||
self.pinchRecognizer?.isEnabled = fixedItemHeight == nil
|
self.pinchRecognizer?.isEnabled = fixedItemHeight == nil && !lockScrollingAtTop
|
||||||
|
|
||||||
if self.currentViewport == nil {
|
if self.currentViewport == nil {
|
||||||
let currentViewport = Viewport(zoomLevel: self.initialZoomLevel ?? ZoomLevel(rawValue: 3), maybeLoadHoleAnchor: { [weak self] holeAnchor, location in
|
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)
|
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) {
|
public func scrollToItem(at index: Int) {
|
||||||
guard let currentViewport = self.currentViewport else {
|
guard let currentViewport = self.currentViewport else {
|
||||||
return
|
return
|
||||||
@ -1380,4 +1409,15 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||||||
public func updateScrollingAreaTooltip(tooltip: SparseItemGridScrollingArea.DisplayTooltip) {
|
public func updateScrollingAreaTooltip(tooltip: SparseItemGridScrollingArea.DisplayTooltip) {
|
||||||
self.scrollingArea.displayTooltip = tooltip
|
self.scrollingArea.displayTooltip = tooltip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func cancelGestures() {
|
||||||
|
self.tapRecognizer?.state = .cancelled
|
||||||
|
self.pinchRecognizer?.state = .cancelled
|
||||||
|
}
|
||||||
|
|
||||||
|
public func hideScrollingArea() {
|
||||||
|
self.currentViewport?.stopScrolling()
|
||||||
|
|
||||||
|
self.scrollingArea.hideScroller()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,16 +164,28 @@ public final class TooltipComponent: Component {
|
|||||||
private var icon: ComponentHostView<Empty>?
|
private var icon: ComponentHostView<Empty>?
|
||||||
private let content: ComponentHostView<Empty>
|
private let content: ComponentHostView<Empty>
|
||||||
|
|
||||||
|
private let regularMaskImage: UIImage
|
||||||
|
private let invertedMaskImage: UIImage
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
self.backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||||
self.backgroundViewMask = UIImageView()
|
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.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
context.setFillColor(UIColor.black.cgColor)
|
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 ")
|
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>()
|
self.content = ComponentHostView<Empty>()
|
||||||
|
|
||||||
@ -235,14 +247,21 @@ public final class TooltipComponent: Component {
|
|||||||
if contentRect.minX < 0.0 {
|
if contentRect.minX < 0.0 {
|
||||||
contentRect.origin.x = component.arrowLocation.maxX
|
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.backgroundView.frame = maskedBackgroundFrame
|
||||||
self.backgroundViewMask.frame = CGRect(origin: CGPoint(), size: maskedBackgroundFrame.size)
|
|
||||||
|
|
||||||
if let iconSize = iconSize, let icon = self.icon {
|
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))
|
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 topIndicatorInset: CGFloat = indicatorVerticalInset + containerInsets.top
|
||||||
let bottomIndicatorInset: CGFloat = indicatorVerticalInset + containerInsets.bottom
|
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)))
|
let indicatorPositionFraction = min(1.0, max(0.0, contentOffset / (contentHeight - containerSize.height)))
|
||||||
|
|
||||||
@ -964,4 +983,12 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
|
|||||||
|
|
||||||
return nil
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -732,11 +732,13 @@ final class CachedPeerInvitationImporters: Codable {
|
|||||||
self.dates = dates
|
self.dates = dates
|
||||||
|
|
||||||
var abouts: [PeerId: String] = [:]
|
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 {
|
for aboutPair in aboutsArray {
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(aboutPair.key))
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(aboutPair.key))
|
||||||
abouts[peerId] = aboutPair.value
|
abouts[peerId] = aboutPair.value
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self.abouts = abouts
|
self.abouts = abouts
|
||||||
|
|
||||||
self.count = try container.decode(Int32.self, forKey: "count")
|
self.count = try container.decode(Int32.self, forKey: "count")
|
||||||
|
|||||||
@ -1055,7 +1055,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var duration: Int32?
|
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
|
duration = file.duration
|
||||||
}
|
}
|
||||||
layer.updateDuration(duration: duration)
|
layer.updateDuration(duration: duration)
|
||||||
@ -1172,6 +1172,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
|
|
||||||
weak var parentController: ViewController?
|
weak var parentController: ViewController?
|
||||||
|
|
||||||
|
private let contextGestureContainerNode: ContextControllerSourceNode
|
||||||
private let itemGrid: SparseItemGrid
|
private let itemGrid: SparseItemGrid
|
||||||
private let itemGridBinding: SparseItemGridBindingImpl
|
private let itemGridBinding: SparseItemGridBindingImpl
|
||||||
private let directMediaImageCache: DirectMediaImageCache
|
private let directMediaImageCache: DirectMediaImageCache
|
||||||
@ -1222,6 +1223,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
private let stateTag: MessageTags
|
private let stateTag: MessageTags
|
||||||
private var storedStateDisposable: Disposable?
|
private var storedStateDisposable: Disposable?
|
||||||
|
|
||||||
|
private weak var currentGestureItem: SparseItemGridDisplayItem?
|
||||||
|
|
||||||
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
|
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
@ -1230,6 +1233,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
self.contentTypePromise = ValuePromise<ContentType>(contentType)
|
self.contentTypePromise = ValuePromise<ContentType>(contentType)
|
||||||
self.stateTag = tagMaskForType(contentType)
|
self.stateTag = tagMaskForType(contentType)
|
||||||
|
|
||||||
|
self.contextGestureContainerNode = ContextControllerSourceNode()
|
||||||
self.itemGrid = SparseItemGrid()
|
self.itemGrid = SparseItemGrid()
|
||||||
self.directMediaImageCache = DirectMediaImageCache(account: context.account)
|
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.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)
|
self.storedStateDisposable = (visualMediaStoredState(postbox: context.account.postbox, peerId: peerId, messageTag: self.stateTag)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||||
@ -1509,7 +1600,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
} else if photoCount != 0 {
|
} else if photoCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(photoCount) photos", isActivity: false)
|
return PeerInfoStatusData(text: "\(photoCount) photos", isActivity: false)
|
||||||
} else if videoCount != 0 {
|
} else if videoCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(photoCount) videos", isActivity: false)
|
return PeerInfoStatusData(text: "\(videoCount) videos", isActivity: false)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1607,6 +1698,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
self.contentType = contentType
|
self.contentType = contentType
|
||||||
self.contentTypePromise.set(contentType)
|
self.contentTypePromise.set(contentType)
|
||||||
|
|
||||||
|
self.itemGrid.hideScrollingArea()
|
||||||
|
|
||||||
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, tag: tagMaskForType(self.contentType))
|
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, tag: tagMaskForType(self.contentType))
|
||||||
self.isRequestingView = false
|
self.isRequestingView = false
|
||||||
self.requestHistoryAroundVisiblePosition(synchronous: true, reloadAtTop: true)
|
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) {
|
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)
|
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)))
|
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 {
|
if let items = self.items {
|
||||||
let fixedItemHeight: CGFloat?
|
let fixedItemHeight: CGFloat?
|
||||||
|
|||||||
@ -6022,10 +6022,32 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func displayMediaGalleryContextMenu(source: ContextReferenceContentNode) {
|
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
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6034,11 +6056,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
|
|
||||||
var recurseGenerateAction: ((Bool) -> ContextMenuActionItem)?
|
var recurseGenerateAction: ((Bool) -> ContextMenuActionItem)?
|
||||||
let generateAction: (Bool) -> ContextMenuActionItem = { [weak pane] isZoomIn in
|
let generateAction: (Bool) -> ContextMenuActionItem = { [weak pane] isZoomIn in
|
||||||
var canZoom: Bool = true
|
let nextZoomLevel = isZoomIn ? pane?.availableZoomLevels().increment : pane?.availableZoomLevels().decrement
|
||||||
if !"".isEmpty {
|
let canZoom: Bool = nextZoomLevel != nil
|
||||||
canZoom = false
|
|
||||||
}
|
return ContextMenuActionItem(id: isZoomIn ? 0 : 1, text: isZoomIn ? "Zoom In" : "Zoom Out", textColor: canZoom ? .primary : .disabled, icon: { theme in
|
||||||
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))
|
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
|
}, action: canZoom ? { action in
|
||||||
guard let pane = pane, let zoomLevel = isZoomIn ? pane.availableZoomLevels().increment : pane.availableZoomLevels().decrement else {
|
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)
|
pane.updateZoomLevel(level: zoomLevel)
|
||||||
if let recurseGenerateAction = recurseGenerateAction {
|
if let recurseGenerateAction = recurseGenerateAction {
|
||||||
action.updateAction(recurseGenerateAction(isZoomIn))
|
action.updateAction(0, recurseGenerateAction(true))
|
||||||
|
action.updateAction(1, recurseGenerateAction(false))
|
||||||
}
|
}
|
||||||
} : nil)
|
} : nil)
|
||||||
}
|
}
|
||||||
@ -6059,11 +6081,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: "Show Calendar", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: "Show Calendar", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, a in
|
}, action: { _, a in
|
||||||
a(.default)
|
a(.default)
|
||||||
|
|
||||||
self?.openMediaCalendar()
|
self?.openMediaCalendar()
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
if photoCount != 0 && videoCount != 0 {
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
|
|
||||||
let showPhotos: Bool
|
let showPhotos: Bool
|
||||||
@ -6129,8 +6153,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
pane.updateContentType(contentType: updatedContentType)
|
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 {
|
guard let strongSelf = self else {
|
||||||
return .ignore
|
return .ignore
|
||||||
}
|
}
|
||||||
@ -6155,8 +6181,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
|
|
||||||
return .dismiss(consume: true)
|
return .dismiss(consume: true)
|
||||||
}
|
}
|
||||||
self.mediaGalleryContextMenu = contextController
|
strongSelf.mediaGalleryContextMenu = contextController
|
||||||
controller.presentInGlobalOverlay(contextController)
|
controller.presentInGlobalOverlay(contextController)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openMediaCalendar() {
|
private func openMediaCalendar() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user