Refine context menu appearance

This commit is contained in:
Peter 2019-08-09 20:43:19 +03:00
parent 781ff5f843
commit 0f72e95e24
117 changed files with 1505 additions and 434 deletions

View File

@ -5,7 +5,7 @@ import TelegramPresentationData
private let textFont = Font.regular(17.0)
enum ContextActionNext {
enum ContextActionSibling {
case none
case item
case separator
@ -18,7 +18,6 @@ final class ContextActionNode: ASDisplayNode {
private let backgroundNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let separatorNode: ASDisplayNode
private let textNode: ImmediateTextNode
private let statusNode: ImmediateTextNode?
private let iconNode: ASImageNode
@ -31,24 +30,12 @@ final class ContextActionNode: ASDisplayNode {
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isAccessibilityElement = false
if theme.chatList.searchBarKeyboardColor == .dark {
self.backgroundNode.backgroundColor = theme.actionSheet.itemBackgroundColor.withAlphaComponent(0.8)
} else {
self.backgroundNode.backgroundColor = UIColor(white: 1.0, alpha: 0.6)
}
self.backgroundNode.backgroundColor = theme.contextMenu.itemBackgroundColor
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isAccessibilityElement = false
if theme.chatList.searchBarKeyboardColor == .dark {
self.highlightedBackgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor
} else {
self.highlightedBackgroundNode.backgroundColor = UIColor(white: 0.8, alpha: 0.6)
}
self.highlightedBackgroundNode.backgroundColor = theme.contextMenu.itemHighlightedBackgroundColor
self.highlightedBackgroundNode.alpha = 0.0
self.separatorNode = ASDisplayNode()
self.separatorNode.isAccessibilityElement = false
self.separatorNode.backgroundColor = UIColor(white: 0.0, alpha: 0.1)
self.textNode = ImmediateTextNode()
self.textNode.isAccessibilityElement = false
self.textNode.isUserInteractionEnabled = false
@ -56,9 +43,9 @@ final class ContextActionNode: ASDisplayNode {
let textColor: UIColor
switch action.textColor {
case .primary:
textColor = theme.actionSheet.primaryTextColor
textColor = theme.contextMenu.primaryColor
case .destructive:
textColor = theme.actionSheet.destructiveActionTextColor
textColor = theme.contextMenu.destructiveColor
}
self.textNode.attributedText = NSAttributedString(string: action.text, font: textFont, textColor: textColor)
@ -75,7 +62,7 @@ final class ContextActionNode: ASDisplayNode {
statusNode.isAccessibilityElement = false
statusNode.isUserInteractionEnabled = false
statusNode.displaysAsynchronously = false
statusNode.attributedText = NSAttributedString(string: value, font: textFont, textColor: theme.actionSheet.secondaryTextColor)
statusNode.attributedText = NSAttributedString(string: value, font: textFont, textColor: theme.contextMenu.secondaryColor)
statusNode.maximumNumberOfLines = 1
self.statusNode = statusNode
}
@ -98,7 +85,6 @@ final class ContextActionNode: ASDisplayNode {
self.addSubnode(self.textNode)
self.statusNode.flatMap(self.addSubnode)
self.addSubnode(self.iconNode)
self.addSubnode(self.separatorNode)
self.addSubnode(self.buttonNode)
self.buttonNode.highligthedChanged = { [weak self] highligted in
@ -115,28 +101,22 @@ final class ContextActionNode: ASDisplayNode {
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
}
func updateLayout(constrainedWidth: CGFloat, next: ContextActionNext) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
func updateLayout(constrainedWidth: CGFloat, previous: ContextActionSibling, next: ContextActionSibling) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let sideInset: CGFloat = 16.0
let iconSideInset: CGFloat = 8.0
let verticalInset: CGFloat = 12.0
let iconSize = self.iconNode.image.flatMap({ $0.size }) ?? CGSize()
let standardIconWidth: CGFloat = 28.0
var rightTextInset: CGFloat = 0.0
let standardIconWidth: CGFloat = 32.0
var rightTextInset: CGFloat = sideInset
if !iconSize.width.isZero {
rightTextInset = max(iconSize.width, standardIconWidth) + sideInset
rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset
}
let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude))
let statusSize = self.statusNode?.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude)) ?? CGSize()
switch next {
case .item:
self.separatorNode.alpha = 1.0
case .none, .separator:
self.separatorNode.alpha = 0.0
}
if !statusSize.width.isZero, let statusNode = self.statusNode {
let verticalSpacing: CGFloat = 2.0
let combinedTextHeight = textSize.height + verticalSpacing + statusSize.height
@ -146,12 +126,11 @@ final class ContextActionNode: ASDisplayNode {
transition.updateFrameAdditive(node: statusNode, frame: CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin + verticalSpacing + textSize.height), size: textSize))
if !iconSize.width.isZero {
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
}
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)))
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
})
} else {
@ -160,18 +139,21 @@ final class ContextActionNode: ASDisplayNode {
transition.updateFrameAdditive(node: self.textNode, frame: CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize))
if !iconSize.width.isZero {
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - sideInset - standardIconWidth + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
}
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)))
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
})
}
}
@objc private func buttonPressed() {
self.performAction()
}
func performAction() {
guard let controller = self.getController() else {
return
}
@ -179,4 +161,12 @@ final class ContextActionNode: ASDisplayNode {
self?.actionSelected(result)
})
}
func setIsHighlighted(_ value: Bool) {
if value {
self.highlightedBackgroundNode.alpha = 1.0
} else {
self.highlightedBackgroundNode.alpha = 0.0
}
}
}

View File

@ -5,6 +5,7 @@ import TelegramPresentationData
private enum ContextItemNode {
case action(ContextActionNode)
case itemSeparator(ASDisplayNode)
case separator(ASDisplayNode)
}
@ -12,30 +13,38 @@ final class ContextActionsContainerNode: ASDisplayNode {
private var itemNodes: [ContextItemNode]
init(theme: PresentationTheme, items: [ContextMenuItem], getController: @escaping () -> ContextController?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
self.itemNodes = items.map { item in
switch item {
var itemNodes: [ContextItemNode] = []
for i in 0 ..< items.count {
switch items[i] {
case let .action(action):
return .action(ContextActionNode(theme: theme, action: action, getController: getController, actionSelected: actionSelected))
itemNodes.append(.action(ContextActionNode(theme: theme, action: action, getController: getController, actionSelected: actionSelected)))
if i != items.count - 1, case .action = items[i + 1] {
let separatorNode = ASDisplayNode()
separatorNode.backgroundColor = theme.contextMenu.itemSeparatorColor
itemNodes.append(.itemSeparator(separatorNode))
}
case .separator:
let separatorNode = ASDisplayNode()
if theme.chatList.searchBarKeyboardColor == .dark {
separatorNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor.withAlphaComponent(0.8)
} else {
separatorNode.backgroundColor = UIColor(white: 0.8, alpha: 0.6)
}
return .separator(separatorNode)
separatorNode.backgroundColor = theme.contextMenu.sectionSeparatorColor
itemNodes.append(.separator(separatorNode))
}
}
self.itemNodes = itemNodes
super.init()
self.clipsToBounds = true
self.cornerRadius = 14.0
self.backgroundColor = theme.contextMenu.backgroundColor
self.itemNodes.forEach({ itemNode in
switch itemNode {
case let .action(actionNode):
self.addSubnode(actionNode)
case let .itemSeparator(separatorNode):
self.addSubnode(separatorNode)
case let .separator(separatorNode):
self.addSubnode(separatorNode)
}
@ -52,7 +61,15 @@ final class ContextActionsContainerNode: ASDisplayNode {
for i in 0 ..< self.itemNodes.count {
switch self.itemNodes[i] {
case let .action(itemNode):
let next: ContextActionNext
let previous: ContextActionSibling
let next: ContextActionSibling
if i == 0 {
previous = .none
} else if case .separator = self.itemNodes[i - 1] {
previous = .separator
} else {
previous = .item
}
if i == self.itemNodes.count - 1 {
next = .none
} else if case .separator = self.itemNodes[i + 1] {
@ -60,10 +77,13 @@ final class ContextActionsContainerNode: ASDisplayNode {
} else {
next = .item
}
let (minSize, complete) = itemNode.updateLayout(constrainedWidth: constrainedWidth, next: next)
let (minSize, complete) = itemNode.updateLayout(constrainedWidth: constrainedWidth, previous: previous, next: next)
maxWidth = max(maxWidth, minSize.width)
heightsAndCompletions.append((minSize.height, complete))
contentHeight += minSize.height
case .itemSeparator:
heightsAndCompletions.append(nil)
contentHeight += UIScreenPixel
case .separator:
heightsAndCompletions.append(nil)
contentHeight += separatorHeight
@ -82,6 +102,9 @@ final class ContextActionsContainerNode: ASDisplayNode {
itemCompletion(itemSize, transition)
verticalOffset += itemHeight
}
case let .itemSeparator(separatorNode):
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: CGSize(width: maxWidth, height: UIScreenPixel)))
verticalOffset += UIScreenPixel
case let .separator(separatorNode):
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: CGSize(width: maxWidth, height: separatorHeight)))
verticalOffset += separatorHeight
@ -90,4 +113,18 @@ final class ContextActionsContainerNode: ASDisplayNode {
return CGSize(width: maxWidth, height: verticalOffset)
}
func actionNode(at point: CGPoint) -> ContextActionNode? {
for itemNode in self.itemNodes {
switch itemNode {
case let .action(actionNode):
if actionNode.frame.contains(point) {
return actionNode
}
default:
break
}
}
return nil
}
}

View File

@ -10,6 +10,7 @@ public final class ContextContentContainingNode: ASDisplayNode {
public var updateAbsoluteRect: ((CGRect, CGSize) -> Void)?
public var applyAbsoluteOffset: ((CGFloat, ContainedViewLayoutTransitionCurve, Double) -> Void)?
public var applyAbsoluteOffsetSpring: ((CGFloat, Double, CGFloat) -> Void)?
public var layoutUpdated: ((CGSize) -> Void)?
public override init() {
self.contentNode = ContextContentNode()

View File

@ -51,17 +51,27 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
private var validLayout: ContainerViewLayout?
private let effectView: UIVisualEffectView
private var propertyAnimator: AnyObject?
private var displayLinkAnimator: DisplayLinkAnimator?
private let dimNode: ASDisplayNode
private let clippingNode: ASDisplayNode
private let scrollNode: ASScrollNode
private var originalProjectedContentViewFrame: (CGRect, CGRect)?
private var contentAreaInScreenSpace: CGRect?
private var contentParentNode: ContextContentContainingNode?
private let contentContainerNode: ContextContentContainerNode
private var actionsContainerNode: ContextActionsContainerNode
init(controller: ContextController, theme: PresentationTheme, strings: PresentationStrings, source: ContextControllerContentSource, items: [ContextMenuItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void) {
private var didCompleteAnimationIn = false
private var initialContinueGesturePoint: CGPoint?
private var didMoveFromInitialGesturePoint = false
private var highlightedActionNode: ContextActionNode?
private let hapticFeedback = HapticFeedback()
init(controller: ContextController, theme: PresentationTheme, strings: PresentationStrings, source: ContextControllerContentSource, items: [ContextMenuItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) {
self.theme = theme
self.strings = strings
self.source = source
@ -80,11 +90,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
self.dimNode = ASDisplayNode()
if theme.chatList.searchBarKeyboardColor == .dark {
self.dimNode.backgroundColor = theme.chatList.backgroundColor.withAlphaComponent(0.2)
} else {
self.dimNode.backgroundColor = UIColor(rgb: 0xb8bbc1, alpha: 0.5)
}
self.dimNode.backgroundColor = theme.contextMenu.dimColor
self.dimNode.alpha = 0.0
self.clippingNode = ASDisplayNode()
@ -109,6 +115,15 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
super.init()
if #available(iOS 10.0, *) {
let propertyAnimator = UIViewPropertyAnimator(duration: 0.4, curve: .linear)
propertyAnimator.isInterruptible = true
propertyAnimator.addAnimations {
self.effectView.effect = makeCustomZoomBlurEffect()
}
self.propertyAnimator = propertyAnimator
}
self.scrollNode.view.delegate = self
self.view.addSubview(self.effectView)
@ -124,6 +139,71 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
getController = { [weak controller] in
return controller
}
if let recognizer = recognizer {
recognizer.externalUpdated = { [weak self, weak recognizer] view, point in
guard let strongSelf = self, let _ = recognizer else {
return
}
let localPoint = strongSelf.view.convert(point, from: view)
let initialPoint: CGPoint
if let current = strongSelf.initialContinueGesturePoint {
initialPoint = current
} else {
initialPoint = localPoint
strongSelf.initialContinueGesturePoint = localPoint
}
if strongSelf.didCompleteAnimationIn {
if !strongSelf.didMoveFromInitialGesturePoint {
let distance = abs(localPoint.y - initialPoint.y)
if distance > 4.0 {
strongSelf.didMoveFromInitialGesturePoint = true
}
}
if strongSelf.didMoveFromInitialGesturePoint {
let actionPoint = strongSelf.view.convert(localPoint, to: strongSelf.actionsContainerNode.view)
let actionNode = strongSelf.actionsContainerNode.actionNode(at: actionPoint)
if strongSelf.highlightedActionNode !== actionNode {
strongSelf.highlightedActionNode?.setIsHighlighted(false)
strongSelf.highlightedActionNode = actionNode
if let actionNode = actionNode {
actionNode.setIsHighlighted(true)
strongSelf.hapticFeedback.tap()
}
}
}
}
}
recognizer.externalEnded = { [weak self, weak recognizer] viewAndPoint in
guard let strongSelf = self, let recognizer = recognizer else {
return
}
recognizer.externalUpdated = nil
if strongSelf.didMoveFromInitialGesturePoint {
if let (view, point) = viewAndPoint {
let _ = strongSelf.view.convert(point, from: view)
if let highlightedActionNode = strongSelf.highlightedActionNode {
strongSelf.highlightedActionNode = nil
highlightedActionNode.performAction()
}
} else {
if let highlightedActionNode = strongSelf.highlightedActionNode {
strongSelf.highlightedActionNode = nil
highlightedActionNode.setIsHighlighted(false)
}
}
}
}
}
}
deinit {
if let propertyAnimator = self.propertyAnimator {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
propertyAnimator?.stopAnimation(true)
}
}
}
override func didLoad() {
@ -137,11 +217,24 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
func animateIn() {
self.hapticFeedback.impact()
let takenViewInfo = self.source.takeView()
if let takenViewInfo = takenViewInfo, let parentSupernode = takenViewInfo.contentContainingNode.supernode {
self.contentParentNode = takenViewInfo.contentContainingNode
let contentParentNode = takenViewInfo.contentContainingNode
takenViewInfo.contentContainingNode.layoutUpdated = { [weak contentParentNode, weak self] size in
guard let strongSelf = self, let contentParentNode = contentParentNode, let parentSupernode = contentParentNode.supernode else {
return
}
strongSelf.originalProjectedContentViewFrame = (parentSupernode.view.convert(contentParentNode.frame, to: strongSelf.view), contentParentNode.view.convert(contentParentNode.contentRect, to: strongSelf.view))
if let validLayout = strongSelf.validLayout {
strongSelf.updateLayout(layout: validLayout, transition: .animated(duration: 0.2, curve: .easeInOut), previousActionsContainerNode: nil)
}
}
self.contentContainerNode.contentNode = takenViewInfo.contentContainingNode.contentNode
self.contentAreaInScreenSpace = takenViewInfo.contentAreaInScreenSpace
self.contentContainerNode.addSubnode(takenViewInfo.contentContainingNode.contentNode)
takenViewInfo.contentContainingNode.isExtractedToContextPreview = true
takenViewInfo.contentContainingNode.isExtractedToContextPreviewUpdated?(true)
@ -158,45 +251,59 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.dimNode.alpha = 1.0
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
UIView.animate(withDuration: 0.25, animations: {
if #available(iOS 9.0, *) {
if self.theme.chatList.searchBarKeyboardColor == .dark {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
self.effectView.effect = UIBlurEffect(style: .dark)
} else {
self.effectView.effect = UIBlurEffect(style: .regular)
if self.effectView.subviews.count == 2 {
self.effectView.subviews[1].isHidden = true
if let _ = self.propertyAnimator {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.25, from: 0.0, to: 1.0, update: { [weak self] value in
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
}, completion: { [weak self] in
self?.didCompleteAnimationIn = true
self?.hapticFeedback.prepareTap()
})
}
} else {
UIView.animate(withDuration: 0.25, animations: {
if #available(iOS 9.0, *) {
if self.theme.chatList.searchBarKeyboardColor == .dark {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
self.effectView.effect = UIBlurEffect(style: .dark)
} else {
self.effectView.effect = UIBlurEffect(style: .regular)
if self.effectView.subviews.count == 2 {
self.effectView.subviews[1].isHidden = true
}
}
} else {
self.effectView.effect = UIBlurEffect(style: .dark)
}
} else {
self.effectView.effect = UIBlurEffect(style: .dark)
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.effectView.effect = UIBlurEffect(style: .regular)
} else {
self.effectView.effect = UIBlurEffect(style: .light)
}
}
} else {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.effectView.effect = UIBlurEffect(style: .regular)
self.effectView.alpha = 1.0
}
}, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
if strongSelf.theme.chatList.searchBarKeyboardColor == .dark {
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
} else {
self.effectView.effect = UIBlurEffect(style: .light)
if strongSelf.effectView.subviews.count == 2 {
strongSelf.effectView.subviews[1].isHidden = true
}
}
}
} else {
self.effectView.alpha = 1.0
}
}, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
if strongSelf.theme.chatList.searchBarKeyboardColor == .dark {
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
} else {
if strongSelf.effectView.subviews.count == 2 {
strongSelf.effectView.subviews[1].isHidden = true
}
}
}
})
//self.effectView.subviews[1].layer.removeAnimation(forKey: "backgroundColor")
})
}
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
} else {
//self.effectView.subviews[1].layer.removeAnimation(forKey: "backgroundColor")
}
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
@ -247,16 +354,28 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
}
UIView.animate(withDuration: 0.3, animations: {
if #available(iOS 9.0, *) {
self.effectView.effect = nil
} else {
self.effectView.alpha = 0.0
if let propertyAnimator = self.propertyAnimator {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.22, from: (propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete ?? 0.2, to: 0.0, update: { [weak self] value in
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
}, completion: { [weak self] in
self?.effectView.isHidden = true
completedEffect = true
intermediateCompletion()
})
}
}, completion: { _ in
completedEffect = true
intermediateCompletion()
})
} else {
UIView.animate(withDuration: 0.3, animations: {
if #available(iOS 9.0, *) {
self.effectView.effect = nil
} else {
self.effectView.alpha = 0.0
}
}, completion: { _ in
completedEffect = true
intermediateCompletion()
})
}
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
@ -337,13 +456,20 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.contentContainerNode.updateLayout(size: contentSize, transition: transition)
let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height)
let originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - actionsSize.width - actionsSideInset, originalProjectedContentViewFrame.1.minX)), y: min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin)), size: actionsSize)
let originalContentFrame = CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalActionsFrame.minY - contentActionsSpacing - originalProjectedContentViewFrame.1.size.height), size: originalProjectedContentViewFrame.1.size)
var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - actionsSize.width - actionsSideInset, originalProjectedContentViewFrame.1.minX)), y: min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin)), size: actionsSize)
var originalContentFrame = CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalActionsFrame.minY - contentActionsSpacing - originalProjectedContentViewFrame.1.size.height), size: originalProjectedContentViewFrame.1.size)
let topEdge = max(contentTopInset, self.contentAreaInScreenSpace?.minY ?? 0.0)
if originalContentFrame.minY < topEdge {
let requiredOffset = topEdge - originalContentFrame.minY
let availableOffset = max(0.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - originalActionsFrame.maxY)
let offset = min(requiredOffset, availableOffset)
originalActionsFrame = originalActionsFrame.offsetBy(dx: 0.0, dy: offset)
originalContentFrame = originalContentFrame.offsetBy(dx: 0.0, dy: offset)
}
let contentHeight = max(layout.size.height, max(layout.size.height, originalActionsFrame.maxY + actionsBottomInset) - originalContentFrame.minY + contentTopInset)
let scrollContentSize = CGSize(width: layout.size.width, height: contentHeight)
let initialContentOffset = self.scrollNode.view.contentOffset
if self.scrollNode.view.contentSize != scrollContentSize {
self.scrollNode.view.contentSize = scrollContentSize
}
@ -434,24 +560,25 @@ public final class ContextController: ViewController {
private let source: ContextControllerContentSource
private var items: [ContextMenuItem]
private weak var recognizer: TapLongTapOrDoubleTapGestureRecognizer?
private var animatedDidAppear = false
private var wasDismissed = false
private let hapticFeedback = HapticFeedback()
private var controllerNode: ContextControllerNode {
return self.displayNode as! ContextControllerNode
}
public init(theme: PresentationTheme, strings: PresentationStrings, source: ContextControllerContentSource, items: [ContextMenuItem]) {
public init(theme: PresentationTheme, strings: PresentationStrings, source: ContextControllerContentSource, items: [ContextMenuItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil) {
self.theme = theme
self.strings = strings
self.source = source
self.items = items
self.recognizer = recognizer
super.init(navigationBarPresentationData: nil)
self.statusBar.statusBarStyle = .Ignore
self.statusBar.statusBarStyle = .Hide
}
required init(coder aDecoder: NSCoder) {
@ -461,7 +588,7 @@ public final class ContextController: ViewController {
override public func loadDisplayNode() {
self.displayNode = ContextControllerNode(controller: self, theme: self.theme, strings: self.strings, source: self.source, items: self.items, beginDismiss: { [weak self] result in
self?.dismiss(result: result)
})
}, recognizer: self.recognizer)
self.displayNodeDidLoad()
}
@ -473,12 +600,14 @@ public final class ContextController: ViewController {
}
override public func viewDidAppear(_ animated: Bool) {
if self.ignoreAppearanceMethodInvocations() {
return
}
super.viewDidAppear(animated)
if !self.wasDismissed && !self.animatedDidAppear {
self.animatedDidAppear = true
self.controllerNode.animateIn()
self.hapticFeedback.impact()
}
}

View File

@ -63,7 +63,7 @@ public func childWindowHostView(parent: UIView) -> WindowHostView {
let view = ChildWindowHostView()
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
let hostView = WindowHostView(containerView: view, eventView: view, isRotating: {
let hostView = WindowHostView(containerView: view, eventView: view, aboveStatusBarView: view, isRotating: {
return false
}, updateSupportedInterfaceOrientations: { orientations in
}, updateDeferScreenEdgeGestures: { edges in

View File

@ -28,7 +28,6 @@ FOUNDATION_EXPORT const unsigned char DisplayVersionString[];
#import <Display/NavigationBarProxy.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#import <Display/NSWeakReference.h>
#import <Display/FBAnimationPerformanceTracker.h>
#import <Display/CATracingLayer.h>
#import <Display/CASeeThroughTracingLayer.h>
#import <Display/UIMenuItem+Icons.h>

View File

@ -65,7 +65,7 @@ final class GlobalOverlayPresentationContext {
var underStatusBar = false
if let controller = controller as? ViewController {
if case .Ignore = controller.statusBar.statusBarStyle {
if case .Hide = controller.statusBar.statusBarStyle {
underStatusBar = true
}
}
@ -145,7 +145,7 @@ final class GlobalOverlayPresentationContext {
for controller in self.controllers {
var underStatusBar = false
if let controller = controller as? ViewController {
if case .Ignore = controller.statusBar.statusBarStyle {
if case .Hide = controller.statusBar.statusBarStyle {
underStatusBar = true
}
}

View File

@ -24,6 +24,9 @@ private final class HapticFeedbackImpl {
if number == "1" {
return generator
} else {
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
return generator
}
return nil
}
}()

View File

@ -3462,6 +3462,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
let updatedApparentHeight = itemNode.apparentHeight
let apparentHeightDelta = updatedApparentHeight - previousApparentHeight
if abs(apparentHeightDelta) > CGFloat.ulpOfOne {
itemNode.updateFrame(itemNode.frame, within: self.visibleSize)
let visualInsets = self.visualInsets ?? self.insets
if itemNode.apparentFrame.maxY <= visualInsets.top {

View File

@ -337,7 +337,7 @@ private final class NativeWindow: UIWindow, WindowHost {
}
}
public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView) {
public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView, UIWindow) {
let window = NativeWindow(frame: UIScreen.main.bounds)
let rootViewController = WindowRootViewController()
@ -346,7 +346,9 @@ public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView) {
rootViewController.view.frame = CGRect(origin: CGPoint(), size: window.bounds.size)
rootViewController.viewDidAppear(false)
let hostView = WindowHostView(containerView: rootViewController.view, eventView: window, isRotating: {
let aboveStatusbarWindow = AboveStatusBarWindow(frame: UIScreen.main.bounds)
let hostView = WindowHostView(containerView: rootViewController.view, eventView: window, aboveStatusBarView: rootViewController.view, isRotating: {
return window.isRotating()
}, updateSupportedInterfaceOrientations: { orientations in
rootViewController.orientations = orientations
@ -422,5 +424,5 @@ public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView) {
}
}
return (window, hostView)
return (window, hostView, aboveStatusbarWindow)
}

View File

@ -1,6 +1,25 @@
import Foundation
import UIKit
import UIKit.UIGestureRecognizerSubclass
import AsyncDisplayKit
private func cancelScrollViewGestures(view: UIView?) {
if let view = view {
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
if let recognizer = recognizer as? UIPanGestureRecognizer {
switch recognizer.state {
case .began, .possible:
recognizer.state = .ended
default:
break
}
}
}
}
cancelScrollViewGestures(view: view.superview)
}
}
private class TapLongTapOrDoubleTapGestureRecognizerTimerTarget: NSObject {
weak var target: TapLongTapOrDoubleTapGestureRecognizer?
@ -47,6 +66,10 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer,
public private(set) var lastRecognizedGestureAndLocation: (TapLongTapOrDoubleTapGesture, CGPoint)?
public var tapActionAtPoint: ((CGPoint) -> TapLongTapOrDoubleTapGestureRecognizerAction)?
public var longTap: ((CGPoint, TapLongTapOrDoubleTapGestureRecognizer) -> Void)?
private var recognizedLongTap: Bool = false
public var externalUpdated: ((UIView?, CGPoint) -> Void)?
public var externalEnded: (((UIView?, CGPoint)?) -> Void)?
public var highlight: ((CGPoint?) -> Void)?
public var hapticFeedback: HapticFeedback?
@ -73,20 +96,35 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer,
self.tapCount = 0
self.touchCount = 0
self.hapticFeedback = nil
self.recognizedLongTap = false
if self.highlightPoint != nil {
self.highlightPoint = nil
self.highlight?(nil)
}
self.externalUpdated = nil
self.externalEnded = nil
super.reset()
}
public func cancel() {
self.state = .cancelled
}
fileprivate func longTapEvent() {
self.timer?.invalidate()
self.timer = nil
if let (location, _) = self.touchLocationAndTimestamp {
self.lastRecognizedGestureAndLocation = (.longTap, location)
if let longTap = self.longTap {
self.recognizedLongTap = true
longTap(location, self)
cancelScrollViewGestures(view: self.view?.superview)
self.state = .began
return
}
} else {
self.lastRecognizedGestureAndLocation = nil
}
@ -184,6 +222,12 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer,
return
}
if self.recognizedLongTap, let externalUpdated = self.externalUpdated {
let location = touch.location(in: self.view)
externalUpdated(self.view, location)
return
}
if let touch = touches.first, let (touchLocation, _) = self.touchLocationAndTimestamp {
let location = touch.location(in: self.view)
let distance = CGPoint(x: location.x - touchLocation.x, y: location.y - touchLocation.y)
@ -211,6 +255,12 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer,
return
}
if let (gesture, location) = self.lastRecognizedGestureAndLocation, case .longTap = gesture, self.recognizedLongTap {
self.externalEnded?((self.view, location))
self.state = .cancelled
return
}
if self.tapCount == 1 {
var tapAction: TapLongTapOrDoubleTapGestureRecognizerAction = .waitForDoubleTap
if let tapActionAtPoint = self.tapActionAtPoint, let (touchLocation, _) = self.touchLocationAndTimestamp {
@ -252,6 +302,8 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer,
self.highlight?(nil)
}
self.externalEnded?(nil)
self.state = .cancelled
}
}

View File

@ -11,3 +11,5 @@ CABasicAnimation * _Nonnull makeSpringAnimation(NSString * _Nonnull keyPath);
CABasicAnimation * _Nonnull makeSpringBounceAnimation(NSString * _Nonnull keyPath, CGFloat initialVelocity, CGFloat damping);
CGFloat springAnimationValueAt(CABasicAnimation * _Nonnull animation, CGFloat t);
void testZoomBlurEffect(UIVisualEffect *effect);
UIBlurEffect *makeCustomZoomBlurEffect();

View File

@ -87,3 +87,43 @@ CABasicAnimation * _Nonnull makeSpringBounceAnimation(NSString * _Nonnull keyPat
CGFloat springAnimationValueAt(CABasicAnimation * _Nonnull animation, CGFloat t) {
return [(CASpringAnimation *)animation valueAt:t];
}
@interface CustomBlurEffect : UIBlurEffect
@property (nonatomic) double blurRadius;
@property (nonatomic) double colorBurnTintAlpha;
@property (nonatomic) double colorBurnTintLevel;
@property (nonatomic, retain) UIColor *colorTint;
@property (nonatomic) double colorTintAlpha;
@property (nonatomic) bool darkenWithSourceOver;
@property (nonatomic) double darkeningTintAlpha;
@property (nonatomic) double darkeningTintHue;
@property (nonatomic) double darkeningTintSaturation;
@property (nonatomic) double grayscaleTintAlpha;
@property (nonatomic) double grayscaleTintLevel;
@property (nonatomic) bool lightenGrayscaleWithSourceOver;
@property (nonatomic) double saturationDeltaFactor;
@property (nonatomic) double scale;
@property (nonatomic) double zoom;
+ (id)effectWithStyle:(long long)arg1;
@end
void testZoomBlurEffect(UIVisualEffect *effect) {
}
UIBlurEffect *makeCustomZoomBlurEffect() {
NSString *string = [@[@"_", @"UI", @"Custom", @"BlurEffect"] componentsJoinedByString:@""];
CustomBlurEffect *result = (CustomBlurEffect *)[NSClassFromString(string) effectWithStyle:0];
result.blurRadius = 7.0;
result.zoom = 0.015;
result.colorTint = nil;
result.colorTintAlpha = 0.0;
result.darkeningTintAlpha = 0.0;
result.grayscaleTintAlpha = 0.0;
result.saturationDeltaFactor = 1.0;
result.scale = 0.5;
return result;
}

View File

@ -33,3 +33,7 @@ typedef NS_OPTIONS(NSUInteger, UIResponderDisableAutomaticKeyboardHandling) {
@end
void applyKeyboardAutocorrection();
@interface AboveStatusBarWindow : UIWindow
@end

View File

@ -308,3 +308,74 @@ void applyKeyboardAutocorrection() {
#pragma clang diagnostic pop
}
}
@interface AboveStatusBarWindowController : UIViewController
@end
@implementation AboveStatusBarWindowController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nil bundle:nil];
if (self != nil) {
self.extendedLayoutIncludesOpaqueBars = true;
}
return self;
}
- (void)loadView {
self.view = [[UIView alloc] initWithFrame:CGRectZero];
self.view.opaque = false;
self.view.backgroundColor = nil;
[self viewDidLoad];
}
@end
@implementation AboveStatusBarWindow
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self != nil) {
self.windowLevel = UIWindowLevelStatusBar + 1.0f;
self.rootViewController = [[AboveStatusBarWindowController alloc] initWithNibName:nil bundle:nil];
if (self.gestureRecognizers != nil) {
for (UIGestureRecognizer *recognizer in self.gestureRecognizers) {
recognizer.delaysTouchesBegan = false;
}
}
}
return self;
}
- (BOOL)shouldAffectStatusBarAppearance {
return false;
}
- (BOOL)canBecomeKeyWindow {
return false;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *result = [super hitTest:point withEvent:event];
if (result == self || result == self.rootViewController.view) {
return nil;
}
return result;
}
+ (void)initialize {
NSString *canAffectSelectorString = [@[@"_can", @"Affect", @"Status", @"Bar", @"Appearance"] componentsJoinedByString:@""];
SEL canAffectSelector = NSSelectorFromString(canAffectSelectorString);
Method shouldAffectMethod = class_getInstanceMethod(self, @selector(shouldAffectStatusBarAppearance));
IMP canAffectImplementation = method_getImplementation(shouldAffectMethod);
class_addMethod(self, canAffectSelector, canAffectImplementation, method_getTypeEncoding(shouldAffectMethod));
NSString *canBecomeKeySelectorString = [NSString stringWithFormat:@"_%@", NSStringFromSelector(@selector(canBecomeKeyWindow))];
SEL canBecomeKeySelector = NSSelectorFromString(canBecomeKeySelectorString);
Method canBecomeKeyMethod = class_getInstanceMethod(self, @selector(canBecomeKeyWindow));
IMP canBecomeKeyImplementation = method_getImplementation(canBecomeKeyMethod);
class_addMethod(self, canBecomeKeySelector, canBecomeKeyImplementation, method_getTypeEncoding(canBecomeKeyMethod));
}
@end

View File

@ -170,6 +170,7 @@ private func checkIsPreviewingView(_ view: UIView) -> Bool {
private func applyThemeToPreviewingView(_ view: UIView, accentColor: UIColor, darkBlur: Bool) {
if let previewingActionGroupClass = previewingActionGroupClass, view.isKind(of: previewingActionGroupClass) {
view.tintColor = accentColor
testZoomBlurEffect((view.superview?.superview?.subviews[1] as? UIVisualEffectView)?.effect)
if darkBlur {
applyThemeToPreviewingEffectView(view)
}
@ -214,6 +215,7 @@ public func getFirstResponderAndAccessoryHeight(_ view: UIView, _ accessoryHeigh
public final class WindowHostView {
public let containerView: UIView
public let eventView: UIView
public let aboveStatusBarView: UIView
public let isRotating: () -> Bool
let updateSupportedInterfaceOrientations: (UIInterfaceOrientationMask) -> Void
@ -235,9 +237,10 @@ public final class WindowHostView {
var forEachController: (((ContainableController) -> Void) -> Void)?
var getAccessibilityElements: (() -> [Any]?)?
init(containerView: UIView, eventView: UIView, isRotating: @escaping () -> Bool, updateSupportedInterfaceOrientations: @escaping (UIInterfaceOrientationMask) -> Void, updateDeferScreenEdgeGestures: @escaping (UIRectEdge) -> Void, updatePreferNavigationUIHidden: @escaping (Bool) -> Void) {
init(containerView: UIView, eventView: UIView, aboveStatusBarView: UIView, isRotating: @escaping () -> Bool, updateSupportedInterfaceOrientations: @escaping (UIInterfaceOrientationMask) -> Void, updateDeferScreenEdgeGestures: @escaping (UIRectEdge) -> Void, updatePreferNavigationUIHidden: @escaping (Bool) -> Void) {
self.containerView = containerView
self.eventView = eventView
self.aboveStatusBarView = aboveStatusBarView
self.isRotating = isRotating
self.updateSupportedInterfaceOrientations = updateSupportedInterfaceOrientations
self.updateDeferScreenEdgeGestures = updateDeferScreenEdgeGestures
@ -372,7 +375,7 @@ public class Window1 {
self.windowLayout = WindowLayout(size: boundsSize, metrics: layoutMetricsForScreenSize(boundsSize), statusBarHeight: statusBarHeight, forceInCallStatusBarText: self.forceInCallStatusBarText, inputHeight: 0.0, safeInsets: safeInsetsForScreenSize(boundsSize, hasOnScreenNavigation: self.hostView.hasOnScreenNavigation), onScreenNavigationHeight: onScreenNavigationHeight, upperKeyboardInputPositionBound: nil, inVoiceOver: UIAccessibility.isVoiceOverRunning)
self.updatingLayout = UpdatingLayout(layout: self.windowLayout, transition: .immediate)
self.presentationContext = PresentationContext()
self.overlayPresentationContext = GlobalOverlayPresentationContext(statusBarHost: statusBarHost, parentView: self.hostView.containerView)
self.overlayPresentationContext = GlobalOverlayPresentationContext(statusBarHost: statusBarHost, parentView: self.hostView.aboveStatusBarView)
self.presentationContext.updateIsInteractionBlocked = { [weak self] value in
self?.isInteractionBlocked = value

View File

@ -36,6 +36,7 @@
D02958001D6F096000360E5E /* ContextMenuContainerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02957FF1D6F096000360E5E /* ContextMenuContainerNode.swift */; };
D02BDB021B6AC703008AFAD2 /* RuntimeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */; };
D02C61F322A94BD600D4EC86 /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02C61F222A94BD600D4EC86 /* Keyboard.swift */; };
D0329EA822FC8E7600F9F071 /* DisplayLinkAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0329EA722FC8E7600F9F071 /* DisplayLinkAnimator.swift */; };
D03310B3213F232600FC83CD /* ListViewTapGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03310B2213F232600FC83CD /* ListViewTapGestureRecognizer.swift */; };
D033874E223D3E86007A2CE4 /* AccessibilityAreaNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033874D223D3E86007A2CE4 /* AccessibilityAreaNode.swift */; };
D0338750223EE5A4007A2CE4 /* ActionSheetSwitchItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033874F223EE5A4007A2CE4 /* ActionSheetSwitchItem.swift */; };
@ -131,8 +132,6 @@
D0BE93191E8ED71100DCC1E6 /* NativeWindowHostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE93181E8ED71100DCC1E6 /* NativeWindowHostView.swift */; };
D0C0B5991EDF3BC9000F4D2C /* ActionSheetTextItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C0B5981EDF3BC9000F4D2C /* ActionSheetTextItem.swift */; };
D0C0B59D1EE022CC000F4D2C /* NavigationBarContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C0B59C1EE022CC000F4D2C /* NavigationBarContentNode.swift */; };
D0C0D28F1C997110001D2851 /* FBAnimationPerformanceTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C0D28D1C997110001D2851 /* FBAnimationPerformanceTracker.h */; settings = {ATTRIBUTES = (Public, ); }; };
D0C0D2901C997110001D2851 /* FBAnimationPerformanceTracker.mm in Sources */ = {isa = PBXBuildFile; fileRef = D0C0D28E1C997110001D2851 /* FBAnimationPerformanceTracker.mm */; };
D0C12A1A1F3375B400B3F66D /* NavigationBarTitleTransitionNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C12A191F3375B400B3F66D /* NavigationBarTitleTransitionNode.swift */; };
D0C2DFC61CC4431D0044FF83 /* ASTransformLayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBB1CC4431D0044FF83 /* ASTransformLayerNode.swift */; };
D0C2DFC71CC4431D0044FF83 /* ListViewItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBC1CC4431D0044FF83 /* ListViewItemNode.swift */; };
@ -213,6 +212,7 @@
D02957FF1D6F096000360E5E /* ContextMenuContainerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuContainerNode.swift; sourceTree = "<group>"; };
D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuntimeUtils.swift; sourceTree = "<group>"; };
D02C61F222A94BD600D4EC86 /* Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keyboard.swift; sourceTree = "<group>"; };
D0329EA722FC8E7600F9F071 /* DisplayLinkAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayLinkAnimator.swift; sourceTree = "<group>"; };
D03310B2213F232600FC83CD /* ListViewTapGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewTapGestureRecognizer.swift; sourceTree = "<group>"; };
D033874D223D3E86007A2CE4 /* AccessibilityAreaNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityAreaNode.swift; sourceTree = "<group>"; };
D033874F223EE5A4007A2CE4 /* ActionSheetSwitchItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionSheetSwitchItem.swift; sourceTree = "<group>"; };
@ -311,8 +311,6 @@
D0BE93181E8ED71100DCC1E6 /* NativeWindowHostView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeWindowHostView.swift; sourceTree = "<group>"; };
D0C0B5981EDF3BC9000F4D2C /* ActionSheetTextItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetTextItem.swift; sourceTree = "<group>"; };
D0C0B59C1EE022CC000F4D2C /* NavigationBarContentNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBarContentNode.swift; sourceTree = "<group>"; };
D0C0D28D1C997110001D2851 /* FBAnimationPerformanceTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBAnimationPerformanceTracker.h; sourceTree = "<group>"; };
D0C0D28E1C997110001D2851 /* FBAnimationPerformanceTracker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FBAnimationPerformanceTracker.mm; sourceTree = "<group>"; };
D0C12A191F3375B400B3F66D /* NavigationBarTitleTransitionNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBarTitleTransitionNode.swift; sourceTree = "<group>"; };
D0C2DFBB1CC4431D0044FF83 /* ASTransformLayerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASTransformLayerNode.swift; sourceTree = "<group>"; };
D0C2DFBC1CC4431D0044FF83 /* ListViewItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewItemNode.swift; sourceTree = "<group>"; };
@ -620,13 +618,12 @@
D03E7DF61C96C5F200C07816 /* NSWeakReference.h */,
D03E7DF71C96C5F200C07816 /* NSWeakReference.m */,
D03E7E001C974AB300C07816 /* DisplayLinkDispatcher.swift */,
D0C0D28D1C997110001D2851 /* FBAnimationPerformanceTracker.h */,
D0C0D28E1C997110001D2851 /* FBAnimationPerformanceTracker.mm */,
D0D94A161D3814F900740E02 /* UniversalTapRecognizer.swift */,
D01F728121F13891006AB634 /* Accessibility.swift */,
09167E1E229803DC005734A7 /* UIMenuItem+Icons.h */,
09167E1F229803DC005734A7 /* UIMenuItem+Icons.m */,
D02C61F222A94BD600D4EC86 /* Keyboard.swift */,
D0329EA722FC8E7600F9F071 /* DisplayLinkAnimator.swift */,
);
name = Utils;
sourceTree = "<group>";
@ -768,7 +765,6 @@
D05174B31EAA833200A1BF36 /* CASeeThroughTracingLayer.h in Headers */,
D05CC3081B69575900E235A3 /* NSBag.h in Headers */,
09167E20229803DC005734A7 /* UIMenuItem+Icons.h in Headers */,
D0C0D28F1C997110001D2851 /* FBAnimationPerformanceTracker.h in Headers */,
D05CC2671B69316F00E235A3 /* Display.h in Headers */,
D05CC2F91B6955D000E235A3 /* UIViewController+Navigation.h in Headers */,
);
@ -993,7 +989,6 @@
D0CE67921F7DA11700FFB557 /* ActionSheetTheme.swift in Sources */,
D0A134642034DE580059716A /* TabBarTapRecognizer.swift in Sources */,
D03AA4E3202DD52E0056C405 /* PeekControllerMenuItemNode.swift in Sources */,
D0C0D2901C997110001D2851 /* FBAnimationPerformanceTracker.mm in Sources */,
D015F7521D1AE08D00E269B5 /* ContainableController.swift in Sources */,
D036574B1E71C44D00BB1EE4 /* MinimizeKeyboardGestureRecognizer.swift in Sources */,
D0CB78901F9822F8004AB79B /* WindowPanRecognizer.swift in Sources */,
@ -1006,6 +1001,7 @@
D02383861DE0E3B4004018B6 /* ListViewIntermediateState.swift in Sources */,
D0DA444E1E4DCA6E005FDCA7 /* AlertControllerNode.swift in Sources */,
D0B367201C94A53A00346D2E /* StatusBarProxyNode.swift in Sources */,
D0329EA822FC8E7600F9F071 /* DisplayLinkAnimator.swift in Sources */,
09DD88EB21BCA5E0000766BC /* EditableTextNode.swift in Sources */,
D05CC2A21B69326C00E235A3 /* WindowContent.swift in Sources */,
D05CC3151B695A9600E235A3 /* NavigationTransitionCoordinator.swift in Sources */,

View File

@ -291,6 +291,18 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
inputClearButtonColor: UIColor(rgb: 0x8f8f8f),
checkContentColor: .white
)
let contextMenu = PresentationThemeContextMenu(
dimColor: UIColor(rgb: 0x000000, alpha: 0.6),
backgroundColor: UIColor(rgb: 0x252525, alpha: 0.78),
itemSeparatorColor: UIColor(rgb: 0xFFFFFF, alpha: 0.15),
sectionSeparatorColor: UIColor(rgb: 0x000000, alpha: 0.2),
itemBackgroundColor: UIColor(rgb: 0x000000, alpha: 0.0),
itemHighlightedBackgroundColor: UIColor(rgb: 0xFFFFFF, alpha: 0.15),
primaryColor: UIColor(rgb: 0xffffff, alpha: 1.0),
secondaryColor: UIColor(rgb: 0xffffff, alpha: 0.8),
destructiveColor: destructiveColor
)
let inAppNotification = PresentationThemeInAppNotification(
fillColor: UIColor(rgb: 0x1c1c1d),
@ -318,6 +330,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
chatList: chatList,
chat: chat,
actionSheet: actionSheet,
contextMenu: contextMenu,
inAppNotification: inAppNotification,
preview: preview
)

View File

@ -292,6 +292,18 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
inputClearButtonColor: mainSecondaryColor,
checkContentColor: .white
)
let contextMenu = PresentationThemeContextMenu(
dimColor: UIColor(rgb: 0x000000, alpha: 0.6),
backgroundColor: rootNavigationBar.backgroundColor.withAlphaComponent(0.78),
itemSeparatorColor: UIColor(rgb: 0xFFFFFF, alpha: 0.15),
sectionSeparatorColor: UIColor(rgb: 0x000000, alpha: 0.2),
itemBackgroundColor: UIColor(rgb: 0x000000, alpha: 0.0),
itemHighlightedBackgroundColor: UIColor(rgb: 0xFFFFFF, alpha: 0.15),
primaryColor: UIColor(rgb: 0xffffff, alpha: 1.0),
secondaryColor: UIColor(rgb: 0xffffff, alpha: 0.8),
destructiveColor: destructiveColor
)
let inAppNotification = PresentationThemeInAppNotification(
fillColor: mainBackgroundColor,
@ -319,6 +331,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
chatList: chatList,
chat: chat,
actionSheet: actionSheet,
contextMenu: contextMenu,
inAppNotification: inAppNotification,
preview: preview
)

View File

@ -299,6 +299,18 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
checkContentColor: .white
)
let contextMenu = PresentationThemeContextMenu(
dimColor: UIColor(rgb: 0x000A26, alpha: 0.2),
backgroundColor: UIColor(rgb: 0xF9F9F9, alpha: 0.78),
itemSeparatorColor: UIColor(rgb: 0x3C3C43, alpha: 0.2),
sectionSeparatorColor: UIColor(rgb: 0x8A8A8A, alpha: 0.2),
itemBackgroundColor: UIColor(rgb: 0x000000, alpha: 0.0),
itemHighlightedBackgroundColor: UIColor(rgb: 0x3C3C43, alpha: 0.2),
primaryColor: UIColor(rgb: 0x000000, alpha: 1.0),
secondaryColor: UIColor(rgb: 0x000000, alpha: 0.8),
destructiveColor: destructiveColor
)
let inAppNotification = PresentationThemeInAppNotification(
fillColor: .white,
primaryTextColor: .black,
@ -325,6 +337,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
chatList: chatList,
chat: chat,
actionSheet: actionSheet,
contextMenu: contextMenu,
inAppNotification: inAppNotification,
preview: preview
)

View File

@ -200,6 +200,30 @@ public final class PresentationThemeActionSheet {
}
}
public final class PresentationThemeContextMenu {
public let dimColor: UIColor
public let backgroundColor: UIColor
public let itemSeparatorColor: UIColor
public let sectionSeparatorColor: UIColor
public let itemBackgroundColor: UIColor
public let itemHighlightedBackgroundColor: UIColor
public let primaryColor: UIColor
public let secondaryColor: UIColor
public let destructiveColor: UIColor
init(dimColor: UIColor, backgroundColor: UIColor, itemSeparatorColor: UIColor, sectionSeparatorColor: UIColor, itemBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, primaryColor: UIColor, secondaryColor: UIColor, destructiveColor: UIColor) {
self.dimColor = dimColor
self.backgroundColor = backgroundColor
self.itemSeparatorColor = itemSeparatorColor
self.sectionSeparatorColor = sectionSeparatorColor
self.itemBackgroundColor = itemBackgroundColor
self.itemHighlightedBackgroundColor = itemHighlightedBackgroundColor
self.primaryColor = primaryColor
self.secondaryColor = secondaryColor
self.destructiveColor = destructiveColor
}
}
public final class PresentationThemeSwitch {
public let frameColor: UIColor
public let handleColor: UIColor
@ -914,12 +938,13 @@ public final class PresentationTheme: Equatable {
public let chatList: PresentationThemeChatList
public let chat: PresentationThemeChat
public let actionSheet: PresentationThemeActionSheet
public let contextMenu: PresentationThemeContextMenu
public let inAppNotification: PresentationThemeInAppNotification
public let preview: Bool
public let resourceCache: PresentationsResourceCache = PresentationsResourceCache()
public init(name: PresentationThemeName, author: String?, overallDarkAppearance: Bool, baseColor: PresentationThemeBaseColor?, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) {
public init(name: PresentationThemeName, author: String?, overallDarkAppearance: Bool, baseColor: PresentationThemeBaseColor?, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) {
self.name = name
self.author = author
self.overallDarkAppearance = overallDarkAppearance
@ -931,6 +956,7 @@ public final class PresentationTheme: Equatable {
self.chatList = chatList
self.chat = chat
self.actionSheet = actionSheet
self.contextMenu = contextMenu
self.inAppNotification = inAppNotification
self.preview = preview
}

View File

@ -431,6 +431,48 @@ extension PresentationThemeActionSheet: Codable {
}
}
extension PresentationThemeContextMenu: Codable {
enum CodingKeys: String, CodingKey {
case dim
case background
case itemSeparator
case sectionSeparator
case itemBackground
case itemHighlightedBackground
case primary
case secondary
case destructive
}
public convenience init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.init(
dimColor: try decodeColor(values, .dim),
backgroundColor: try decodeColor(values, .background),
itemSeparatorColor: try decodeColor(values, .itemSeparator),
sectionSeparatorColor: try decodeColor(values, .sectionSeparator),
itemBackgroundColor: try decodeColor(values, .itemBackground),
itemHighlightedBackgroundColor: try decodeColor(values, .itemHighlightedBackground),
primaryColor: try decodeColor(values, .primary),
secondaryColor: try decodeColor(values, .secondary),
destructiveColor: try decodeColor(values, .destructive)
)
}
public func encode(to encoder: Encoder) throws {
var values = encoder.container(keyedBy: CodingKeys.self)
try encodeColor(&values, self.dimColor, .dim)
try encodeColor(&values, self.backgroundColor, .background)
try encodeColor(&values, self.itemSeparatorColor, .itemSeparator)
try encodeColor(&values, self.sectionSeparatorColor, .sectionSeparator)
try encodeColor(&values, self.itemBackgroundColor, .itemBackground)
try encodeColor(&values, self.itemHighlightedBackgroundColor, .itemHighlightedBackground)
try encodeColor(&values, self.primaryColor, .primary)
try encodeColor(&values, self.secondaryColor, .secondary)
try encodeColor(&values, self.destructiveColor, .destructive)
}
}
extension PresentationThemeSwitch: Codable {
enum CodingKeys: String, CodingKey {
case frame
@ -1423,6 +1465,7 @@ extension PresentationTheme: Codable {
case chatList
case chat
case actionSheet
case contextMenu
case notification
}
@ -1440,6 +1483,7 @@ extension PresentationTheme: Codable {
chatList: try values.decode(PresentationThemeChatList.self, forKey: .chatList),
chat: try values.decode(PresentationThemeChat.self, forKey: .chat),
actionSheet: try values.decode(PresentationThemeActionSheet.self, forKey: .actionSheet),
contextMenu: try values.decode(PresentationThemeContextMenu.self, forKey: .contextMenu),
inAppNotification: try values.decode(PresentationThemeInAppNotification.self, forKey: .notification)
)
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_add.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_archive.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_browser.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_call.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_clear.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_copy.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_darkmode.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_delete.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_edit.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_forward.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_hidechat.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_info.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_link.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_logout.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_markasread.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_markasunread.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_message.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_more.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_muted.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_daymode.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_pin.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_promote.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_rate.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_reply.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_report.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_resend.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_restrict.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_save.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_schedule.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_share.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_stoppoll.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_timer.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_unarchive.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_unmuted.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_unpin.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_unvote.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_user.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -154,6 +154,7 @@ final class SharedApplicationContext {
@objc var window: UIWindow?
var nativeWindow: (UIWindow & WindowHost)?
var mainWindow: Window1!
var aboveStatusbarWindow: UIWindow?
private var dataImportSplash: LegacyDataImportSplash?
let episodeId = arc4random()
@ -227,8 +228,9 @@ final class SharedApplicationContext {
let launchStartTime = CFAbsoluteTimeGetCurrent()
let statusBarHost = ApplicationStatusBarHost()
let (window, hostView) = nativeWindowHostView()
let (window, hostView, aboveStatusbarWindow) = nativeWindowHostView()
self.mainWindow = Window1(hostView: hostView, statusBarHost: statusBarHost)
self.aboveStatusbarWindow = aboveStatusbarWindow
window.backgroundColor = UIColor.white
self.window = window
self.nativeWindow = window
@ -434,6 +436,7 @@ final class SharedApplicationContext {
#endif
#endif
self.aboveStatusbarWindow?.isHidden = false
self.window?.makeKeyAndVisible()
self.hasActiveAudioSession.set(MediaManagerImpl.globalAudioSession.isActive())

View File

@ -484,7 +484,7 @@ public final class ChatController: TelegramBaseController, GalleryHiddenMediaTar
self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage)
}, openPeerMention: { [weak self] name in
self?.openPeerMention(name)
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame in
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, recognizer in
guard let strongSelf = self, strongSelf.isNodeLoaded else {
return
}
@ -502,7 +502,33 @@ public final class ChatController: TelegramBaseController, GalleryHiddenMediaTar
guard let strongSelf = self, !actions.isEmpty else {
return
}
strongSelf.window?.presentInGlobalOverlay(ContextController(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: ChatMessageContextControllerContentSource(chatNode: strongSelf.chatDisplayNode, message: message), items: actions))
var actions = actions
actions.insert(.action(ContextMenuActionItem(text: "Reaction", icon: { _ in nil }, action: { _, f in
guard let strongSelf = self else {
return
}
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
var items: [ActionSheetItem] = []
let emojis = ["👍", "😊", "🤔", "😔", "❤️"]
for emoji in emojis {
items.append(ActionSheetButtonItem(title: "\(emoji)", color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: updatedMessages[0].id, reactions: [emoji]).start()
}))
}
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])])
strongSelf.chatDisplayNode.dismissInput()
strongSelf.present(actionSheet, in: .window(.root))
f(.dismissWithoutContent)
})), at: 0)
strongSelf.window?.presentInGlobalOverlay(ContextController(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: ChatMessageContextControllerContentSource(chatNode: strongSelf.chatDisplayNode, message: message), items: actions, recognizer: recognizer))
})
}
}, navigateToMessage: { [weak self] fromId, id in
@ -2620,12 +2646,9 @@ public final class ChatController: TelegramBaseController, GalleryHiddenMediaTar
self?.present(c, in: .window(.root), with: a)
}, completion: { _ in }), in: .window(.root))
}
}, reportMessages: { [weak self] messages in
}, reportMessages: { [weak self] messages, contextController in
if let strongSelf = self, !messages.isEmpty {
strongSelf.chatDisplayNode.dismissInput()
strongSelf.present(peerReportOptionsController(context: strongSelf.context, subject: .messages(messages.map({ $0.id }).sorted()), present: { c, a in
self?.present(c, in: .window(.root), with: a)
}, completion: { _ in }), in: .window(.root))
presentPeerReportOptions(context: strongSelf.context, parent: strongSelf, contextController: contextController, subject: .messages(messages.map({ $0.id }).sorted()), completion: { _ in })
}
}, deleteMessages: { [weak self] messages, contextController, completion in
if let strongSelf = self, !messages.isEmpty {

View File

@ -48,7 +48,7 @@ public final class ChatControllerInteraction {
let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool
let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void
let openPeerMention: (String) -> Void
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect) -> Void
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, TapLongTapOrDoubleTapGestureRecognizer?) -> Void
let navigateToMessage: (MessageId, MessageId) -> Void
let clickThroughMessage: () -> Void
let toggleMessagesSelection: ([MessageId], Bool) -> Void
@ -100,7 +100,7 @@ public final class ChatControllerInteraction {
var searchTextHighightState: String?
var seenOneTimeAnimatedMedia = Set<MessageId>()
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, TapLongTapOrDoubleTapGestureRecognizer?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
self.openMessage = openMessage
self.openPeer = openPeer
self.openPeerMention = openPeerMention
@ -154,7 +154,7 @@ public final class ChatControllerInteraction {
static var `default`: ChatControllerInteraction {
return ChatControllerInteraction(openMessage: { _, _ in
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
}, presentController: { _, _ in }, navigationController: {
return nil
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in

View File

@ -850,7 +850,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
let visibleAreaInset = UIEdgeInsets(top: containerInsets.top, left: 0.0, bottom: containerInsets.bottom + contentBottomInset, right: 0.0)
let visibleAreaInset = UIEdgeInsets(top: containerInsets.top, left: 0.0, bottom: containerInsets.bottom + inputPanelsHeight, right: 0.0)
self.visibleAreaInset = visibleAreaInset
self.loadingNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition)

Some files were not shown because too many files have changed in this diff Show More