mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Shared media update
This commit is contained in:
parent
074df6febf
commit
c0fb52283d
@ -11,3 +11,18 @@ final class EscapeGuard {
|
|||||||
self.status.isDeallocated = true
|
self.status.isDeallocated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class EscapeNotification: NSObject {
|
||||||
|
let deallocated: () -> Void
|
||||||
|
|
||||||
|
public init(_ deallocated: @escaping () -> Void) {
|
||||||
|
self.deallocated = deallocated
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.deallocated()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func keep() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,9 +17,11 @@ public protocol ContextActionNodeProtocol: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
||||||
private let action: ContextMenuActionItem
|
private var presentationData: PresentationData
|
||||||
|
private 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 backgroundNode: ASDisplayNode
|
private let backgroundNode: ASDisplayNode
|
||||||
private let highlightedBackgroundNode: ASDisplayNode
|
private let highlightedBackgroundNode: ASDisplayNode
|
||||||
@ -38,10 +40,12 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
init(presentationData: PresentationData, action: ContextMenuActionItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
|
init(presentationData: PresentationData, action: ContextMenuActionItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, requestLayout: @escaping () -> Void) {
|
||||||
|
self.presentationData = presentationData
|
||||||
self.action = action
|
self.action = action
|
||||||
self.getController = getController
|
self.getController = getController
|
||||||
self.actionSelected = actionSelected
|
self.actionSelected = actionSelected
|
||||||
|
self.requestLayout = requestLayout
|
||||||
|
|
||||||
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
|
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
|
||||||
|
|
||||||
@ -267,6 +271,8 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateTheme(presentationData: PresentationData) {
|
func updateTheme(presentationData: PresentationData) {
|
||||||
|
self.presentationData = presentationData
|
||||||
|
|
||||||
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor
|
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor
|
||||||
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
||||||
|
|
||||||
@ -315,9 +321,45 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
|||||||
guard let controller = self.getController() else {
|
guard let controller = self.getController() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.action.action?(controller, { [weak self] result in
|
self.action.action?(ContextMenuActionItem.Action(
|
||||||
|
controller: controller,
|
||||||
|
dismissWithResult: { [weak self] result in
|
||||||
self?.actionSelected(result)
|
self?.actionSelected(result)
|
||||||
})
|
},
|
||||||
|
updateAction: { [weak self] 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()
|
||||||
|
}
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setIsHighlighted(_ value: Bool) {
|
func setIsHighlighted(_ value: Bool) {
|
||||||
|
@ -69,7 +69,7 @@ private final class InnerActionsContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(presentationData: PresentationData, items: [ContextMenuItem], getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) {
|
init(presentationData: PresentationData, items: [ContextMenuItem], getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, requestLayout: @escaping () -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.feedbackTap = feedbackTap
|
self.feedbackTap = feedbackTap
|
||||||
self.blurBackground = blurBackground
|
self.blurBackground = blurBackground
|
||||||
@ -83,7 +83,7 @@ private final class InnerActionsContainerNode: ASDisplayNode {
|
|||||||
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)))
|
itemNodes.append(.action(ContextActionNode(presentationData: presentationData, action: action, getController: getController, actionSelected: actionSelected, requestLayout: requestLayout)))
|
||||||
if i != items.count - 1 {
|
if i != items.count - 1 {
|
||||||
switch items[i + 1] {
|
switch items[i + 1] {
|
||||||
case .action, .custom:
|
case .action, .custom:
|
||||||
@ -469,7 +469,7 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
|||||||
return self.additionalActionsNode != nil
|
return self.additionalActionsNode != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
init(presentationData: PresentationData, items: ContextController.Items, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) {
|
init(presentationData: PresentationData, items: ContextController.Items, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, requestLayout: @escaping () -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) {
|
||||||
self.blurBackground = blurBackground
|
self.blurBackground = blurBackground
|
||||||
self.shadowNode = ASImageNode()
|
self.shadowNode = ASImageNode()
|
||||||
self.shadowNode.displaysAsynchronously = false
|
self.shadowNode.displaysAsynchronously = false
|
||||||
@ -488,14 +488,14 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
|||||||
additionalShadowNode.isHidden = true
|
additionalShadowNode.isHidden = true
|
||||||
self.additionalShadowNode = additionalShadowNode
|
self.additionalShadowNode = additionalShadowNode
|
||||||
|
|
||||||
self.additionalActionsNode = InnerActionsContainerNode(presentationData: presentationData, items: [firstItem], getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground)
|
self.additionalActionsNode = InnerActionsContainerNode(presentationData: presentationData, items: [firstItem], getController: getController, actionSelected: actionSelected, requestLayout: requestLayout, feedbackTap: feedbackTap, blurBackground: blurBackground)
|
||||||
items.items.removeFirst()
|
items.items.removeFirst()
|
||||||
} else {
|
} else {
|
||||||
self.additionalShadowNode = nil
|
self.additionalShadowNode = nil
|
||||||
self.additionalActionsNode = nil
|
self.additionalActionsNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self.actionsNode = InnerActionsContainerNode(presentationData: presentationData, items: items.items, getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground)
|
self.actionsNode = InnerActionsContainerNode(presentationData: presentationData, items: items.items, getController: getController, actionSelected: actionSelected, requestLayout: requestLayout, feedbackTap: feedbackTap, blurBackground: blurBackground)
|
||||||
if let tip = items.tip {
|
if let tip = items.tip {
|
||||||
let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip)
|
let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip)
|
||||||
textSelectionTipNode.isUserInteractionEnabled = false
|
textSelectionTipNode.isUserInteractionEnabled = false
|
||||||
|
@ -68,6 +68,18 @@ public struct ContextMenuActionBadge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class ContextMenuActionItem {
|
public final class ContextMenuActionItem {
|
||||||
|
public final class Action {
|
||||||
|
public let controller: ContextControllerProtocol
|
||||||
|
public let dismissWithResult: (ContextMenuActionResult) -> Void
|
||||||
|
public let updateAction: (ContextMenuActionItem) -> Void
|
||||||
|
|
||||||
|
init(controller: ContextControllerProtocol, dismissWithResult: @escaping (ContextMenuActionResult) -> Void, updateAction: @escaping (ContextMenuActionItem) -> Void) {
|
||||||
|
self.controller = controller
|
||||||
|
self.dismissWithResult = dismissWithResult
|
||||||
|
self.updateAction = updateAction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public let text: String
|
public let text: String
|
||||||
public let textColor: ContextMenuActionItemTextColor
|
public let textColor: ContextMenuActionItemTextColor
|
||||||
public let textFont: ContextMenuActionItemFont
|
public let textFont: ContextMenuActionItemFont
|
||||||
@ -75,9 +87,44 @@ public final class ContextMenuActionItem {
|
|||||||
public let badge: ContextMenuActionBadge?
|
public let badge: ContextMenuActionBadge?
|
||||||
public let icon: (PresentationTheme) -> UIImage?
|
public let icon: (PresentationTheme) -> UIImage?
|
||||||
public let iconSource: ContextMenuActionItemIconSource?
|
public let iconSource: ContextMenuActionItemIconSource?
|
||||||
public let action: ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?
|
public let action: ((Action) -> Void)?
|
||||||
|
|
||||||
public init(text: String, textColor: ContextMenuActionItemTextColor = .primary, textLayout: ContextMenuActionItemTextLayout = .twoLinesMax, textFont: ContextMenuActionItemFont = .regular, badge: ContextMenuActionBadge? = nil, icon: @escaping (PresentationTheme) -> UIImage?, iconSource: ContextMenuActionItemIconSource? = nil, action: ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?) {
|
convenience public init(
|
||||||
|
text: String,
|
||||||
|
textColor: ContextMenuActionItemTextColor = .primary,
|
||||||
|
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
|
||||||
|
textFont: ContextMenuActionItemFont = .regular,
|
||||||
|
badge: ContextMenuActionBadge? = nil,
|
||||||
|
icon: @escaping (PresentationTheme) -> UIImage?,
|
||||||
|
iconSource: ContextMenuActionItemIconSource? = nil,
|
||||||
|
action: ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
text: text,
|
||||||
|
textColor: textColor,
|
||||||
|
textLayout: textLayout,
|
||||||
|
textFont: textFont,
|
||||||
|
badge: badge,
|
||||||
|
icon: icon,
|
||||||
|
iconSource: iconSource,
|
||||||
|
action: action.flatMap { action in
|
||||||
|
return { impl in
|
||||||
|
action(impl.controller, impl.dismissWithResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(
|
||||||
|
text: String,
|
||||||
|
textColor: ContextMenuActionItemTextColor = .primary,
|
||||||
|
textLayout: ContextMenuActionItemTextLayout = .twoLinesMax,
|
||||||
|
textFont: ContextMenuActionItemFont = .regular,
|
||||||
|
badge: ContextMenuActionBadge? = nil,
|
||||||
|
icon: @escaping (PresentationTheme) -> UIImage?,
|
||||||
|
iconSource: ContextMenuActionItemIconSource? = nil,
|
||||||
|
action: ((Action) -> Void)?
|
||||||
|
) {
|
||||||
self.text = text
|
self.text = text
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.textFont = textFont
|
self.textFont = textFont
|
||||||
@ -215,6 +262,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
self.contentContainerNode = ContextContentContainerNode()
|
self.contentContainerNode = ContextContentContainerNode()
|
||||||
|
|
||||||
var feedbackTap: (() -> Void)?
|
var feedbackTap: (() -> Void)?
|
||||||
|
var updateLayout: (() -> Void)?
|
||||||
|
|
||||||
var blurBackground = true
|
var blurBackground = true
|
||||||
if case .reference = source {
|
if case .reference = source {
|
||||||
@ -228,6 +276,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
return controller
|
return controller
|
||||||
}, actionSelected: { result in
|
}, actionSelected: { result in
|
||||||
beginDismiss(result)
|
beginDismiss(result)
|
||||||
|
}, requestLayout: {
|
||||||
|
updateLayout?()
|
||||||
}, feedbackTap: {
|
}, feedbackTap: {
|
||||||
feedbackTap?()
|
feedbackTap?()
|
||||||
}, blurBackground: blurBackground)
|
}, blurBackground: blurBackground)
|
||||||
@ -238,6 +288,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
self?.hapticFeedback.tap()
|
self?.hapticFeedback.tap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateLayout = { [weak self] in
|
||||||
|
self?.updateLayout()
|
||||||
|
}
|
||||||
|
|
||||||
self.scrollNode.view.delegate = self
|
self.scrollNode.view.delegate = self
|
||||||
|
|
||||||
if blurBackground {
|
if blurBackground {
|
||||||
@ -1059,6 +1113,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
return self?.getController()
|
return self?.getController()
|
||||||
}, actionSelected: { [weak self] result in
|
}, actionSelected: { [weak self] result in
|
||||||
self?.beginDismiss(result)
|
self?.beginDismiss(result)
|
||||||
|
}, requestLayout: { [weak self] in
|
||||||
|
self?.updateLayout()
|
||||||
}, feedbackTap: { [weak self] in
|
}, feedbackTap: { [weak self] in
|
||||||
self?.hapticFeedback.tap()
|
self?.hapticFeedback.tap()
|
||||||
}, blurBackground: self.blurBackground)
|
}, blurBackground: self.blurBackground)
|
||||||
@ -1087,6 +1143,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateLayout() {
|
||||||
|
if let layout = self.validLayout {
|
||||||
|
self.updateLayout(layout: layout, transition: .immediate, previousActionsContainerNode: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?, previousActionsContainerFrame: CGRect? = nil, previousActionsTransition: ContextController.PreviousActionsTransition = .scale) {
|
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?, previousActionsContainerFrame: CGRect? = nil, previousActionsTransition: ContextController.PreviousActionsTransition = .scale) {
|
||||||
if self.isAnimatingOut {
|
if self.isAnimatingOut {
|
||||||
return
|
return
|
||||||
@ -1573,10 +1635,13 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let mappedPoint = self.view.convert(point, to: self.scrollNode.view)
|
let mappedPoint = self.view.convert(point, to: self.scrollNode.view)
|
||||||
|
var maybePassthrough = false
|
||||||
if let maybeContentNode = self.contentContainerNode.contentNode {
|
if let maybeContentNode = self.contentContainerNode.contentNode {
|
||||||
switch maybeContentNode {
|
switch maybeContentNode {
|
||||||
case .reference:
|
case .reference:
|
||||||
break
|
if let controller = self.getController() as? ContextController {
|
||||||
|
maybePassthrough = controller.passthroughTouchEvents
|
||||||
|
}
|
||||||
case let .extracted(contentParentNode, _):
|
case let .extracted(contentParentNode, _):
|
||||||
if case let .extracted(source) = self.source {
|
if case let .extracted(source) = self.source {
|
||||||
if !source.ignoreContentTouches {
|
if !source.ignoreContentTouches {
|
||||||
@ -1616,6 +1681,11 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
return self.actionsContainerNode.hitTest(self.view.convert(point, to: self.actionsContainerNode.view), with: event)
|
return self.actionsContainerNode.hitTest(self.view.convert(point, to: self.actionsContainerNode.view), with: event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if maybePassthrough {
|
||||||
|
self.getController()?.dismiss(completion: nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return self.dismissNode.view
|
return self.dismissNode.view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1777,6 +1847,8 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
|||||||
public var useComplexItemsTransitionAnimation = false
|
public var useComplexItemsTransitionAnimation = false
|
||||||
public var immediateItemsTransitionAnimation = false
|
public var immediateItemsTransitionAnimation = false
|
||||||
|
|
||||||
|
public var passthroughTouchEvents = false
|
||||||
|
|
||||||
private var shouldBeDismissedDisposable: Disposable?
|
private var shouldBeDismissedDisposable: Disposable?
|
||||||
|
|
||||||
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<ContextController.Items, NoError>, recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil) {
|
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<ContextController.Items, NoError>, recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil) {
|
||||||
|
@ -72,10 +72,13 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
var feedbackTapImpl: (() -> Void)?
|
var feedbackTapImpl: (() -> Void)?
|
||||||
var activatedActionImpl: (() -> Void)?
|
var activatedActionImpl: (() -> Void)?
|
||||||
|
var requestLayoutImpl: (() -> Void)?
|
||||||
self.actionsContainerNode = ContextActionsContainerNode(presentationData: presentationData, items: ContextController.Items(items: content.menuItems()), getController: { [weak controller] in
|
self.actionsContainerNode = ContextActionsContainerNode(presentationData: presentationData, items: ContextController.Items(items: content.menuItems()), getController: { [weak controller] in
|
||||||
return controller
|
return controller
|
||||||
}, actionSelected: { result in
|
}, actionSelected: { result in
|
||||||
activatedActionImpl?()
|
activatedActionImpl?()
|
||||||
|
}, requestLayout: {
|
||||||
|
requestLayoutImpl?()
|
||||||
}, feedbackTap: {
|
}, feedbackTap: {
|
||||||
feedbackTapImpl?()
|
feedbackTapImpl?()
|
||||||
}, blurBackground: true)
|
}, blurBackground: true)
|
||||||
@ -87,6 +90,10 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
|||||||
self?.hapticFeedback.tap()
|
self?.hapticFeedback.tap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestLayoutImpl = { [weak self] in
|
||||||
|
self?.updateLayout()
|
||||||
|
}
|
||||||
|
|
||||||
if content.presentation() == .freeform {
|
if content.presentation() == .freeform {
|
||||||
self.containerNode.isUserInteractionEnabled = false
|
self.containerNode.isUserInteractionEnabled = false
|
||||||
} else {
|
} else {
|
||||||
@ -119,6 +126,12 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
|||||||
self.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
|
self.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateLayout() {
|
||||||
|
if let layout = self.validLayout {
|
||||||
|
self.containerLayoutUpdated(layout, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayout = layout
|
self.validLayout = layout
|
||||||
|
|
||||||
@ -332,6 +345,8 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
|||||||
return self?.controller
|
return self?.controller
|
||||||
}, actionSelected: { [weak self] result in
|
}, actionSelected: { [weak self] result in
|
||||||
self?.requestDismiss()
|
self?.requestDismiss()
|
||||||
|
}, requestLayout: { [weak self] in
|
||||||
|
self?.updateLayout()
|
||||||
}, feedbackTap: { [weak self] in
|
}, feedbackTap: { [weak self] in
|
||||||
self?.hapticFeedback.tap()
|
self?.hapticFeedback.tap()
|
||||||
}, blurBackground: true)
|
}, blurBackground: true)
|
||||||
|
23
submodules/DirectMediaImageCache/BUILD
Normal file
23
submodules/DirectMediaImageCache/BUILD
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "DirectMediaImageCache",
|
||||||
|
module_name = "DirectMediaImageCache",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
|
"//submodules/Postbox:Postbox",
|
||||||
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
|
"//submodules/TinyThumbnail:TinyThumbnail",
|
||||||
|
"//submodules/Display:Display",
|
||||||
|
"//submodules/FastBlur:FastBlur",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
@ -0,0 +1,134 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import UIKit
|
||||||
|
import TinyThumbnail
|
||||||
|
import Display
|
||||||
|
import FastBlur
|
||||||
|
|
||||||
|
private func generateBlurredThumbnail(image: UIImage) -> UIImage? {
|
||||||
|
let thumbnailContextSize = CGSize(width: 32.0, height: 32.0)
|
||||||
|
let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0)
|
||||||
|
|
||||||
|
let filledSize = image.size.aspectFilled(thumbnailContextSize)
|
||||||
|
let imageRect = CGRect(origin: CGPoint(x: (thumbnailContextSize.width - filledSize.width) / 2.0, y: (thumbnailContextSize.height - filledSize.height) / 2.0), size: filledSize)
|
||||||
|
|
||||||
|
thumbnailContext.withFlippedContext { c in
|
||||||
|
c.draw(image.cgImage!, in: imageRect)
|
||||||
|
}
|
||||||
|
telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
|
||||||
|
|
||||||
|
return thumbnailContext.generateImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DirectMediaImageCache {
|
||||||
|
public final class GetMediaResult {
|
||||||
|
public let image: UIImage?
|
||||||
|
public let loadSignal: Signal<UIImage?, NoError>?
|
||||||
|
|
||||||
|
init(image: UIImage?, loadSignal: Signal<UIImage?, NoError>?) {
|
||||||
|
self.image = image
|
||||||
|
self.loadSignal = loadSignal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ImageType {
|
||||||
|
case blurredThumbnail
|
||||||
|
case square(width: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
private let account: Account
|
||||||
|
|
||||||
|
public init(account: Account) {
|
||||||
|
self.account = account
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getCachePath(resourceId: MediaResourceId, imageType: ImageType) -> String {
|
||||||
|
let representationId: String
|
||||||
|
switch imageType {
|
||||||
|
case .blurredThumbnail:
|
||||||
|
representationId = "blurred32"
|
||||||
|
case let .square(width):
|
||||||
|
representationId = "shm\(width)"
|
||||||
|
}
|
||||||
|
return self.account.postbox.mediaBox.cachedRepresentationPathForId(resourceId.stringRepresentation, representationId: representationId, keepDuration: .general)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getLoadSignal(resource: MediaResourceReference, width: Int) -> Signal<UIImage?, NoError>? {
|
||||||
|
let cachePath = self.getCachePath(resourceId: resource.resource.id, imageType: .square(width: width))
|
||||||
|
return Signal { subscriber in
|
||||||
|
let fetch = fetchedMediaResource(mediaBox: self.account.postbox.mediaBox, reference: resource).start()
|
||||||
|
let data = (self.account.postbox.mediaBox.resourceData(resource.resource)
|
||||||
|
|> filter { data in
|
||||||
|
return data.complete
|
||||||
|
}
|
||||||
|
|> take(1)).start(next: { data in
|
||||||
|
if let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: dataValue) {
|
||||||
|
if let scaledImage = generateImage(CGSize(width: CGFloat(width), height: CGFloat(width)), contextGenerator: { size, context in
|
||||||
|
let filledSize = image.size.aspectFilled(size)
|
||||||
|
let imageRect = CGRect(origin: CGPoint(x: (size.width - filledSize.width) / 2.0, y: (size.height - filledSize.height) / 2.0), size: filledSize)
|
||||||
|
context.draw(image.cgImage!, in: imageRect)
|
||||||
|
}, scale: 1.0) {
|
||||||
|
if let resultData = scaledImage.jpegData(compressionQuality: 0.7) {
|
||||||
|
let _ = try? resultData.write(to: URL(fileURLWithPath: cachePath))
|
||||||
|
subscriber.putNext(scaledImage)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
fetch.dispose()
|
||||||
|
data.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getResource(message: Message, image: TelegramMediaImage) -> MediaResourceReference? {
|
||||||
|
guard let representation = image.representations.last else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return MediaReference.message(message: MessageReference(message), media: image).resourceReference(representation.resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getResource(message: Message, file: TelegramMediaFile) -> MediaResourceReference? {
|
||||||
|
if let representation = file.previewRepresentations.last {
|
||||||
|
return MediaReference.message(message: MessageReference(message), media: file).resourceReference(representation.resource)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getImage(message: Message, media: Media, width: Int) -> GetMediaResult? {
|
||||||
|
var immediateThumbnailData: Data?
|
||||||
|
var resource: MediaResourceReference?
|
||||||
|
if let image = media as? TelegramMediaImage {
|
||||||
|
immediateThumbnailData = image.immediateThumbnailData
|
||||||
|
resource = self.getResource(message: message, image: image)
|
||||||
|
} else if let file = media as? TelegramMediaFile {
|
||||||
|
immediateThumbnailData = file.immediateThumbnailData
|
||||||
|
resource = self.getResource(message: message, file: file)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let resource = resource {
|
||||||
|
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.id, imageType: .square(width: width)))), let image = UIImage(data: data) {
|
||||||
|
return GetMediaResult(image: image, loadSignal: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var blurredImage: UIImage?
|
||||||
|
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.id, imageType: .blurredThumbnail))), let image = UIImage(data: data) {
|
||||||
|
blurredImage = image
|
||||||
|
} else if let data = immediateThumbnailData.flatMap(decodeTinyThumbnail), let image = UIImage(data: data) {
|
||||||
|
if let blurredImageValue = generateBlurredThumbnail(image: image) {
|
||||||
|
blurredImage = blurredImageValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetMediaResult(image: blurredImage, loadSignal: self.getLoadSignal(resource: resource, width: width))
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -866,6 +866,36 @@ public extension ContainedViewLayoutTransition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateTransform(node: ASDisplayNode, transform: CGAffineTransform, beginWithCurrentState: Bool = false, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
||||||
|
let transform = CATransform3DMakeAffineTransform(transform)
|
||||||
|
|
||||||
|
if CATransform3DEqualToTransform(node.layer.transform, transform) {
|
||||||
|
if let completion = completion {
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case .immediate:
|
||||||
|
node.layer.transform = transform
|
||||||
|
if let completion = completion {
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
case let .animated(duration, curve):
|
||||||
|
let previousTransform: CATransform3D
|
||||||
|
if beginWithCurrentState, let presentation = node.layer.presentation() {
|
||||||
|
previousTransform = presentation.transform
|
||||||
|
} else {
|
||||||
|
previousTransform = node.layer.transform
|
||||||
|
}
|
||||||
|
node.layer.transform = transform
|
||||||
|
node.layer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: transform), keyPath: "transform", timingFunction: curve.timingFunction, duration: duration, mediaTimingFunction: curve.mediaTimingFunction, completion: { value in
|
||||||
|
completion?(value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateTransformScale(node: ASDisplayNode, scale: CGFloat, beginWithCurrentState: Bool = false, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
func updateTransformScale(node: ASDisplayNode, scale: CGFloat, beginWithCurrentState: Bool = false, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
||||||
let t = node.layer.transform
|
let t = node.layer.transform
|
||||||
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -83,7 +83,6 @@ public final class SparseMessageList {
|
|||||||
}
|
}
|
||||||
private let loadHoleDisposable = MetaDisposable()
|
private let loadHoleDisposable = MetaDisposable()
|
||||||
private var loadingHole: LoadingHole?
|
private var loadingHole: LoadingHole?
|
||||||
private var scheduledLoadingHole: LoadingHole?
|
|
||||||
|
|
||||||
private var loadingPlaceholders: [MessageId: Disposable] = [:]
|
private var loadingPlaceholders: [MessageId: Disposable] = [:]
|
||||||
private var loadedPlaceholders: [MessageId: Message] = [:]
|
private var loadedPlaceholders: [MessageId: Message] = [:]
|
||||||
@ -169,7 +168,13 @@ public final class SparseMessageList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func resetTopSection() {
|
private func resetTopSection() {
|
||||||
self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), anchor: .upperBound, count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: [])
|
let count: Int
|
||||||
|
#if DEBUG
|
||||||
|
count = 20
|
||||||
|
#else
|
||||||
|
count = 200
|
||||||
|
#endif
|
||||||
|
self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), anchor: .upperBound, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: [])
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] view, updateType, _ in
|
|> deliverOn(self.queue)).start(next: { [weak self] view, updateType, _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -337,14 +342,15 @@ public final class SparseMessageList {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadHole(anchor: MessageId, direction: LoadHoleDirection) {
|
func loadHole(anchor: MessageId, direction: LoadHoleDirection, completion: @escaping () -> Void) {
|
||||||
let loadingHole = LoadingHole(anchor: anchor, direction: direction)
|
let loadingHole = LoadingHole(anchor: anchor, direction: direction)
|
||||||
if self.loadingHole == loadingHole {
|
if self.loadingHole == loadingHole {
|
||||||
|
completion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.loadingHole != nil {
|
if self.loadingHole != nil {
|
||||||
self.scheduledLoadingHole = loadingHole
|
completion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,6 +376,7 @@ public final class SparseMessageList {
|
|||||||
}
|
}
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] messages in
|
|> deliverOn(self.queue)).start(next: { [weak self] messages in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
|
completion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,12 +497,9 @@ public final class SparseMessageList {
|
|||||||
|
|
||||||
if strongSelf.loadingHole == loadingHole {
|
if strongSelf.loadingHole == loadingHole {
|
||||||
strongSelf.loadingHole = nil
|
strongSelf.loadingHole = nil
|
||||||
|
}
|
||||||
|
|
||||||
if let scheduledLoadingHole = strongSelf.scheduledLoadingHole {
|
completion()
|
||||||
strongSelf.scheduledLoadingHole = nil
|
|
||||||
strongSelf.loadHole(anchor: scheduledLoadingHole.anchor, direction: scheduledLoadingHole.direction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,9 +638,9 @@ public final class SparseMessageList {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
public func loadHole(anchor: MessageId, direction: LoadHoleDirection) {
|
public func loadHole(anchor: MessageId, direction: LoadHoleDirection, completion: @escaping () -> Void) {
|
||||||
self.impl.with { impl in
|
self.impl.with { impl in
|
||||||
impl.loadHole(anchor: anchor, direction: direction)
|
impl.loadHole(anchor: anchor, direction: direction, completion: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,6 +245,7 @@ swift_library(
|
|||||||
"//submodules/CalendarMessageScreen:CalendarMessageScreen",
|
"//submodules/CalendarMessageScreen:CalendarMessageScreen",
|
||||||
"//submodules/LottieMeshSwift:LottieMeshSwift",
|
"//submodules/LottieMeshSwift:LottieMeshSwift",
|
||||||
"//submodules/MeshAnimationCache:MeshAnimationCache",
|
"//submodules/MeshAnimationCache:MeshAnimationCache",
|
||||||
|
"//submodules/DirectMediaImageCache:DirectMediaImageCache",
|
||||||
] + select({
|
] + select({
|
||||||
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
||||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -386,7 +386,8 @@ private final class PeerInfoPendingPane {
|
|||||||
key: PeerInfoPaneKey,
|
key: PeerInfoPaneKey,
|
||||||
hasBecomeReady: @escaping (PeerInfoPaneKey) -> Void,
|
hasBecomeReady: @escaping (PeerInfoPaneKey) -> Void,
|
||||||
parentController: ViewController?,
|
parentController: ViewController?,
|
||||||
openMediaCalendar: @escaping () -> Void
|
openMediaCalendar: @escaping () -> Void,
|
||||||
|
paneDidScroll: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
let paneNode: PeerInfoPaneNode
|
let paneNode: PeerInfoPaneNode
|
||||||
switch key {
|
switch key {
|
||||||
@ -396,6 +397,9 @@ private final class PeerInfoPendingPane {
|
|||||||
visualPaneNode.openCurrentDate = {
|
visualPaneNode.openCurrentDate = {
|
||||||
openMediaCalendar()
|
openMediaCalendar()
|
||||||
}
|
}
|
||||||
|
visualPaneNode.paneDidScroll = {
|
||||||
|
paneDidScroll()
|
||||||
|
}
|
||||||
case .files:
|
case .files:
|
||||||
paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .file)
|
paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .file)
|
||||||
case .links:
|
case .links:
|
||||||
@ -410,6 +414,9 @@ private final class PeerInfoPendingPane {
|
|||||||
visualPaneNode.openCurrentDate = {
|
visualPaneNode.openCurrentDate = {
|
||||||
openMediaCalendar()
|
openMediaCalendar()
|
||||||
}
|
}
|
||||||
|
visualPaneNode.paneDidScroll = {
|
||||||
|
paneDidScroll()
|
||||||
|
}
|
||||||
case .groupsInCommon:
|
case .groupsInCommon:
|
||||||
paneNode = PeerInfoGroupsInCommonPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, openPeerContextAction: openPeerContextAction, groupsInCommonContext: data.groupsInCommon!)
|
paneNode = PeerInfoGroupsInCommonPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, openPeerContextAction: openPeerContextAction, groupsInCommonContext: data.groupsInCommon!)
|
||||||
case .members:
|
case .members:
|
||||||
@ -478,6 +485,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
|||||||
var requestExpandTabs: (() -> Bool)?
|
var requestExpandTabs: (() -> Bool)?
|
||||||
|
|
||||||
var openMediaCalendar: (() -> Void)?
|
var openMediaCalendar: (() -> Void)?
|
||||||
|
var paneDidScroll: (() -> Void)?
|
||||||
|
|
||||||
private var currentAvailablePanes: [PeerInfoPaneKey]?
|
private var currentAvailablePanes: [PeerInfoPaneKey]?
|
||||||
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||||
@ -779,6 +787,9 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
|||||||
parentController: self.parentController,
|
parentController: self.parentController,
|
||||||
openMediaCalendar: { [weak self] in
|
openMediaCalendar: { [weak self] in
|
||||||
self?.openMediaCalendar?()
|
self?.openMediaCalendar?()
|
||||||
|
},
|
||||||
|
paneDidScroll: { [weak self] in
|
||||||
|
self?.paneDidScroll?()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.pendingPanes[key] = pane
|
self.pendingPanes[key] = pane
|
||||||
|
@ -2311,6 +2311,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
strongSelf.openMediaCalendar()
|
strongSelf.openMediaCalendar()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.paneContainerNode.paneDidScroll = { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let mediaGalleryContextMenu = strongSelf.mediaGalleryContextMenu {
|
||||||
|
strongSelf.mediaGalleryContextMenu = nil
|
||||||
|
mediaGalleryContextMenu.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.paneContainerNode.requestPerformPeerMemberAction = { [weak self] member, action in
|
self.paneContainerNode.requestPerformPeerMemberAction = { [weak self] member, action in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -5983,6 +5993,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private weak var mediaGalleryContextMenu: ContextController?
|
||||||
|
|
||||||
private func displayMediaGalleryContextMenu(source: ContextReferenceContentNode) {
|
private func displayMediaGalleryContextMenu(source: ContextReferenceContentNode) {
|
||||||
guard let controller = self.controller else {
|
guard let controller = self.controller else {
|
||||||
return
|
return
|
||||||
@ -5994,29 +6007,36 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
|
|
||||||
let canZoomIn = pane.zoomLevel.decremented() != pane.zoomLevel
|
var recurseGenerateAction: ((Bool) -> ContextMenuActionItem)?
|
||||||
let canZoomOut = pane.zoomLevel.incremented() != pane.zoomLevel
|
let generateAction: (Bool) -> ContextMenuActionItem = { [weak pane] isZoomIn in
|
||||||
|
var canZoom: Bool = true
|
||||||
items.append(.action(ContextMenuActionItem(text: "Zoom In", textColor: canZoomIn ? .primary : .disabled, icon: { theme in
|
if !"".isEmpty {
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ZoomIn"), color: canZoomIn ? theme.contextMenu.primaryColor : theme.contextMenu.primaryColor.withMultipliedAlpha(0.4))
|
canZoom = false
|
||||||
}, action: canZoomIn ? { [weak pane] _, a in
|
}
|
||||||
a(.default)
|
/*if isZoomIn {
|
||||||
|
canZoom = pane?.availableZoomLevels().increment != nil
|
||||||
guard let pane = pane else {
|
} else {
|
||||||
|
canZoom = pane?.availableZoomLevels().decrement != nil
|
||||||
|
}*/
|
||||||
|
return ContextMenuActionItem(text: isZoomIn ? "Zoom In" : "ZoomOut", 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 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pane.updateZoomLevel(level: pane.zoomLevel.decremented())
|
pane.updateZoomLevel(level: zoomLevel)
|
||||||
} : nil)))
|
if let recurseGenerateAction = recurseGenerateAction {
|
||||||
items.append(.action(ContextMenuActionItem(text: "Zoom Out", textColor : canZoomOut ? .primary : .disabled, icon: { theme in
|
action.updateAction(recurseGenerateAction(isZoomIn))
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ZoomOut"), color: canZoomOut ? theme.contextMenu.primaryColor : theme.contextMenu.primaryColor.withMultipliedAlpha(0.4))
|
|
||||||
}, action: canZoomOut ? { [weak pane] _, a in
|
|
||||||
a(.default)
|
|
||||||
|
|
||||||
guard let pane = pane else {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
pane.updateZoomLevel(level: pane.zoomLevel.incremented())
|
} : nil)
|
||||||
} : nil)))
|
}
|
||||||
|
recurseGenerateAction = { isZoomIn in
|
||||||
|
return generateAction(isZoomIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
items.append(.action(generateAction(true)))
|
||||||
|
items.append(.action(generateAction(false)))
|
||||||
|
|
||||||
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: { [weak self] _, a in
|
||||||
@ -6090,6 +6110,8 @@ 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)
|
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.passthroughTouchEvents = true
|
||||||
|
self.mediaGalleryContextMenu = contextController
|
||||||
controller.presentInGlobalOverlay(contextController)
|
controller.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user