diff --git a/submodules/ChatListSearchRecentPeersNode/BUCK b/submodules/ChatListSearchRecentPeersNode/BUCK index a0ce302ad2..932bda3d36 100644 --- a/submodules/ChatListSearchRecentPeersNode/BUCK +++ b/submodules/ChatListSearchRecentPeersNode/BUCK @@ -15,6 +15,7 @@ static_library( "//submodules/HorizontalPeerItem:HorizontalPeerItem", "//submodules/MergeLists:MergeLists", "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/ContextUI:ContextUI", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift b/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift index 1bfff23243..db7021d202 100644 --- a/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift +++ b/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift @@ -9,6 +9,7 @@ import TelegramPresentationData import MergeLists import HorizontalPeerItem import ListSectionHeaderNode +import ContextUI private func calculateItemCustomWidth(width: CGFloat) -> CGFloat { let itemInsets = UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 6.0) @@ -76,9 +77,9 @@ private struct ChatListSearchRecentPeersEntry: Comparable, Identifiable { return lhs.index < rhs.index } - func item(account: Account, mode: HorizontalPeerItemMode, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, isPeerSelected: @escaping (PeerId) -> Bool) -> ListViewItem { - return HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: mode, account: account, peer: self.peer, presence: self.presence, unreadBadge: self.unreadBadge, action: peerSelected, longTapAction: { peer in - peerLongTapped(peer) + func item(account: Account, mode: HorizontalPeerItemMode, peerSelected: @escaping (Peer) -> Void, peerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (PeerId) -> Bool) -> ListViewItem { + return HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: mode, account: account, peer: self.peer, presence: self.presence, unreadBadge: self.unreadBadge, action: peerSelected, contextAction: { peer, node, gesture in + peerContextAction(peer, node, gesture) }, isPeerSelected: isPeerSelected, customWidth: self.itemCustomWidth) } } @@ -91,16 +92,12 @@ private struct ChatListSearchRecentNodeTransition { let animated: Bool } -private func preparedRecentPeersTransition(account: Account, mode: HorizontalPeerItemMode, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, share: Bool = false, from fromEntries: [ChatListSearchRecentPeersEntry], to toEntries: [ChatListSearchRecentPeersEntry], firstTime: Bool, animated: Bool) -> ChatListSearchRecentNodeTransition { +private func preparedRecentPeersTransition(account: Account, mode: HorizontalPeerItemMode, peerSelected: @escaping (Peer) -> Void, peerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, share: Bool = false, from fromEntries: [ChatListSearchRecentPeersEntry], to toEntries: [ChatListSearchRecentPeersEntry], firstTime: Bool, animated: Bool) -> ChatListSearchRecentNodeTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, mode: mode, peerSelected: peerSelected, peerLongTapped: { peer in - peerLongTapped(peer) - }, isPeerSelected: isPeerSelected), directionHint: .Down) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, mode: mode, peerSelected: peerSelected, peerLongTapped: { peer in - peerLongTapped(peer) - }, isPeerSelected: isPeerSelected), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, mode: mode, peerSelected: peerSelected, peerContextAction: peerContextAction, isPeerSelected: isPeerSelected), directionHint: .Down) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, mode: mode, peerSelected: peerSelected, peerContextAction: peerContextAction, isPeerSelected: isPeerSelected), directionHint: nil) } return ChatListSearchRecentNodeTransition(deletions: deletions, insertions: insertions, updates: updates, firstTime: firstTime, animated: animated) } @@ -115,7 +112,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { private let share: Bool private let peerSelected: (Peer) -> Void - private let peerLongTapped: (Peer) -> Void + private let peerContextAction: (Peer, ASDisplayNode, ContextGesture?) -> Void private let isPeerSelected: (PeerId) -> Bool private let disposable = MetaDisposable() @@ -124,14 +121,14 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { private var items: [ListViewItem] = [] private var queuedTransitions: [ChatListSearchRecentNodeTransition] = [] - public init(account: Account, theme: PresentationTheme, mode: HorizontalPeerItemMode, strings: PresentationStrings, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, share: Bool = false) { + public init(account: Account, theme: PresentationTheme, mode: HorizontalPeerItemMode, strings: PresentationStrings, peerSelected: @escaping (Peer) -> Void, peerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, share: Bool = false) { self.theme = theme self.strings = strings self.themeAndStringsPromise = Promise((self.theme, self.strings)) self.mode = mode self.share = share self.peerSelected = peerSelected - self.peerLongTapped = peerLongTapped + self.peerContextAction = peerContextAction self.isPeerSelected = isPeerSelected self.sectionHeaderNode = ListSectionHeaderNode(theme: theme) @@ -210,7 +207,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { let animated = !firstTime.swap(false) - let transition = preparedRecentPeersTransition(account: account, mode: mode, peerSelected: peerSelected, peerLongTapped: peerLongTapped, isPeerSelected: isPeerSelected, from: previous.swap(entries), to: entries, firstTime: !animated, animated: animated) + let transition = preparedRecentPeersTransition(account: account, mode: mode, peerSelected: peerSelected, peerContextAction: peerContextAction, isPeerSelected: isPeerSelected, from: previous.swap(entries), to: entries, firstTime: !animated, animated: animated) strongSelf.enqueueTransition(transition) } diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index bfc0bd3926..e67021acff 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -50,7 +50,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, source: ChatC if case let .search(search) = source { switch search { case .recentPeers: - items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in + items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) }, action: { _, f in let _ = (removeRecentPeer(account: context.account, peerId: peerId) |> deliverOnMainQueue).start(completed: { f(.default) diff --git a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift index 3a6723d9fe..10b0ce0ad1 100644 --- a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift @@ -7,6 +7,7 @@ import SwiftSignalKit import TelegramCore import TelegramPresentationData import ChatListSearchRecentPeersNode +import ContextUI class ChatListRecentPeersListItem: ListViewItem { let theme: PresentationTheme @@ -14,17 +15,17 @@ class ChatListRecentPeersListItem: ListViewItem { let account: Account let peers: [Peer] let peerSelected: (Peer) -> Void - let peerLongTapped: (Peer) -> Void + let peerContextAction: (Peer, ASDisplayNode, ContextGesture?) -> Void let header: ListViewItemHeader? - init(theme: PresentationTheme, strings: PresentationStrings, account: Account, peers: [Peer], peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void) { + init(theme: PresentationTheme, strings: PresentationStrings, account: Account, peers: [Peer], peerSelected: @escaping (Peer) -> Void, peerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void) { self.theme = theme self.strings = strings self.account = account self.peers = peers self.peerSelected = peerSelected - self.peerLongTapped = peerLongTapped + self.peerContextAction = peerContextAction self.header = nil } @@ -115,8 +116,8 @@ class ChatListRecentPeersListItemNode: ListViewItemNode { } else { peersNode = ChatListSearchRecentPeersNode(account: item.account, theme: item.theme, mode: .list, strings: item.strings, peerSelected: { peer in self?.item?.peerSelected(peer) - }, peerLongTapped: { peer in - self?.item?.peerLongTapped(peer) + }, peerContextAction: { peer, node, gesture in + self?.item?.peerContextAction(peer, node, gesture) }, isPeerSelected: { _ in return false }) diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 3130901306..8a6e1c1cef 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -78,13 +78,17 @@ private enum ChatListRecentEntry: Comparable, Identifiable { } } - func item(context: AccountContext, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ListViewItem { + func item(context: AccountContext, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ListViewItem { switch self { case let .topPeers(peers, theme, strings): return ChatListRecentPeersListItem(theme: theme, strings: strings, account: context.account, peers: peers, peerSelected: { peer in peerSelected(peer) - }, peerLongTapped: { peer in - peerLongTapped(peer) + }, peerContextAction: { peer, node, gesture in + if let peerContextAction = peerContextAction { + peerContextAction(peer, .recentPeers, node, gesture) + } else { + gesture?.cancel() + } }) case let .peer(_, peer, theme, strings, timeFormat, nameSortOrder, nameDisplayOrder, hasRevealControls): let primaryPeer: Peer @@ -466,12 +470,12 @@ public struct ChatListSearchContainerTransition { } } -private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ChatListSearchContainerRecentTransition { +private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ChatListSearchContainerRecentTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, filter: filter, peerSelected: peerSelected, peerLongTapped: peerLongTapped, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, filter: filter, peerSelected: peerSelected, peerLongTapped: peerLongTapped, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, filter: filter, peerSelected: peerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, filter: filter, peerSelected: peerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) } return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates) } @@ -969,8 +973,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo openPeer(peer, true) let _ = addRecentlySearchedPeer(postbox: context.account.postbox, peerId: peer.id).start() self?.recentListNode.clearHighlightAnimated(true) - }, peerLongTapped: { peer in - openRecentPeerOptions(peer) }, peerContextAction: peerContextAction, clearRecentlySearchedPeers: { self?.clearRecentSearch() diff --git a/submodules/Display/Display/ChildWindowHostView.swift b/submodules/Display/Display/ChildWindowHostView.swift index 6cb11063a1..c5a306ed22 100644 --- a/submodules/Display/Display/ChildWindowHostView.swift +++ b/submodules/Display/Display/ChildWindowHostView.swift @@ -63,7 +63,7 @@ public func childWindowHostView(parent: UIView) -> WindowHostView { let view = ChildWindowHostView() view.autoresizingMask = [.flexibleWidth, .flexibleHeight] - let hostView = WindowHostView(containerView: view, eventView: view, aboveStatusBarView: view, isRotating: { + let hostView = WindowHostView(containerView: view, eventView: view, isRotating: { return false }, updateSupportedInterfaceOrientations: { orientations in }, updateDeferScreenEdgeGestures: { edges in diff --git a/submodules/Display/Display/ContainedViewLayoutTransition.swift b/submodules/Display/Display/ContainedViewLayoutTransition.swift index 8d03e5ed9d..4d4a54ddf0 100644 --- a/submodules/Display/Display/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Display/ContainedViewLayoutTransition.swift @@ -90,6 +90,39 @@ public extension ContainedViewLayoutTransition { } } + func updateFrameAsPositionAndBounds(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) { + if node.frame.equalTo(frame) && !force { + completion?(true) + } else { + switch self { + case .immediate: + node.position = frame.center + node.bounds = CGRect(origin: CGPoint(), size: frame.size) + if let completion = completion { + completion(true) + } + case let .animated(duration, curve): + let previousPosition: CGPoint + let previousBounds: CGRect + if beginWithCurrentState, let presentation = node.layer.presentation() { + previousPosition = presentation.position + previousBounds = presentation.bounds + } else { + previousPosition = node.position + previousBounds = node.bounds + } + node.position = frame.center + node.bounds = CGRect(origin: CGPoint(), size: frame.size) + node.layer.animateFrame(from: + CGRect(origin: CGPoint(x: previousPosition.x - previousBounds.width / 2.0, y: previousPosition.y - previousBounds.height / 2.0), size: previousBounds.size), to: frame, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in + if let completion = completion { + completion(result) + } + }) + } + } + } + func updateFrameAdditive(node: ASDisplayNode, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) { if node.frame.equalTo(frame) && !force { completion?(true) diff --git a/submodules/Display/Display/ListView.swift b/submodules/Display/Display/ListView.swift index 8e628f723a..4c37e3ca5d 100644 --- a/submodules/Display/Display/ListView.swift +++ b/submodules/Display/Display/ListView.swift @@ -125,7 +125,7 @@ public enum GeneralScrollDirection { } open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGestureRecognizerDelegate { - private final let scroller: ListViewScroller + final let scroller: ListViewScroller private final var visibleSize: CGSize = CGSize() public private(set) final var insets = UIEdgeInsets() public final var visualInsets: UIEdgeInsets? diff --git a/submodules/Display/Display/NativeWindowHostView.swift b/submodules/Display/Display/NativeWindowHostView.swift index 99f67a6a1b..1b2212f2a8 100644 --- a/submodules/Display/Display/NativeWindowHostView.swift +++ b/submodules/Display/Display/NativeWindowHostView.swift @@ -339,7 +339,7 @@ private final class NativeWindow: UIWindow, WindowHost { } } -public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView, UIWindow) { +public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView) { let window = NativeWindow(frame: UIScreen.main.bounds) let rootViewController = WindowRootViewController() @@ -348,16 +348,7 @@ public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView, UI rootViewController.view.frame = CGRect(origin: CGPoint(), size: window.bounds.size) rootViewController.viewDidAppear(false) - let aboveStatusbarWindow = AboveStatusBarWindow(frame: UIScreen.main.bounds) - aboveStatusbarWindow.supportedOrientations = { [weak rootViewController] in - if let rootViewController = rootViewController { - return rootViewController.supportedInterfaceOrientations - } else { - return .portrait - } - } - - let hostView = WindowHostView(containerView: rootViewController.view, eventView: window, aboveStatusBarView: rootViewController.view, isRotating: { + let hostView = WindowHostView(containerView: rootViewController.view, eventView: window, isRotating: { return window.isRotating() }, updateSupportedInterfaceOrientations: { orientations in rootViewController.orientations = orientations @@ -433,5 +424,5 @@ public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView, UI } } - return (window, hostView, aboveStatusbarWindow) + return (window, hostView) } diff --git a/submodules/Display/Display/Navigation/NavigationContainer.swift b/submodules/Display/Display/Navigation/NavigationContainer.swift index f208189f68..8f9e665d18 100644 --- a/submodules/Display/Display/Navigation/NavigationContainer.swift +++ b/submodules/Display/Display/Navigation/NavigationContainer.swift @@ -217,37 +217,50 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { } func update(layout: ContainerViewLayout, canBeClosed: Bool, controllers: [ViewController], transition: ContainedViewLayoutTransition) { - let previousControllers = self.controllers - self.controllers = controllers - - for i in 0 ..< controllers.count { - if i == 0 { - if canBeClosed { - controllers[i].navigationBar?.previousItem = .close - } else { - controllers[i].navigationBar?.previousItem = nil - } - } else { - controllers[i].navigationBar?.previousItem = .item(controllers[i - 1].navigationItem) - } - } - self.state.layout = layout self.state.canBeClosed = canBeClosed - if controllers.last !== self.state.top?.value { - if controllers.last !== self.state.pending?.value.value { - self.state.pending = nil - if let last = controllers.last { - let transitionType: PendingChild.TransitionType - if !previousControllers.contains(where: { $0 === last }) { - transitionType = .push + var controllersUpdated = false + if self.controllers.count != controllers.count { + controllersUpdated = true + } else { + for i in 0 ..< controllers.count { + if self.controllers[i] !== controllers[i] { + controllersUpdated = true + break + } + } + } + if controllersUpdated { + let previousControllers = self.controllers + self.controllers = controllers + + for i in 0 ..< controllers.count { + if i == 0 { + if canBeClosed { + controllers[i].navigationBar?.previousItem = .close } else { - transitionType = .pop + controllers[i].navigationBar?.previousItem = nil + } + } else { + controllers[i].navigationBar?.previousItem = .item(controllers[i - 1].navigationItem) + } + } + + if controllers.last !== self.state.top?.value { + if controllers.last !== self.state.pending?.value.value { + self.state.pending = nil + if let last = controllers.last { + let transitionType: PendingChild.TransitionType + if !previousControllers.contains(where: { $0 === last }) { + transitionType = .push + } else { + transitionType = .pop + } + self.state.pending = PendingChild(value: self.makeChild(layout: layout, value: last), transitionType: transitionType, transition: transition, update: { [weak self] pendingChild in + self?.pendingChildIsReady(pendingChild) + }) } - self.state.pending = PendingChild(value: self.makeChild(layout: layout, value: last), transitionType: transitionType, transition: transition, update: { [weak self] pendingChild in - self?.pendingChildIsReady(pendingChild) - }) } } } @@ -357,6 +370,10 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { } private func applyLayout(layout: ContainerViewLayout, to child: Child, transition: ContainedViewLayoutTransition) { + let childFrame = CGRect(origin: CGPoint(), size: layout.size) + if child.value.displayNode.frame != childFrame { + transition.updateFrame(node: child.value.displayNode, frame: childFrame) + } if child.layout != layout { child.layout = layout child.value.containerLayoutUpdated(layout, transition: transition) diff --git a/submodules/Display/Display/Navigation/NavigationController.swift b/submodules/Display/Display/Navigation/NavigationController.swift index 9ea3b4b474..4d8735844b 100644 --- a/submodules/Display/Display/Navigation/NavigationController.swift +++ b/submodules/Display/Display/Navigation/NavigationController.swift @@ -232,7 +232,6 @@ open class NavigationController: UINavigationController, ContainableController, self.loadView() } self.validLayout = layout - transition.updateFrame(view: self.view, frame: CGRect(origin: self.view.frame.origin, size: layout.size)) self.updateContainers(layout: layout, transition: transition) } @@ -254,10 +253,8 @@ open class NavigationController: UINavigationController, ContainableController, } let modalContainer: NavigationModalContainer - let containerTransition: ContainedViewLayoutTransition if let existingModalContainer = existingModalContainer { modalContainer = existingModalContainer - containerTransition = transition } else { modalContainer = NavigationModalContainer(theme: self.theme, controllerRemoved: { [weak self] controller in self?.controllerRemoved(controller) @@ -273,12 +270,12 @@ open class NavigationController: UINavigationController, ContainableController, } } } - modalContainer.updateDismissProgress = { [weak self, weak modalContainer] _ in + modalContainer.updateDismissProgress = { [weak self, weak modalContainer] _, transition in guard let strongSelf = self, let modalContainer = modalContainer else { return } if let layout = strongSelf.validLayout { - strongSelf.updateContainers(layout: layout, transition: .immediate) + strongSelf.updateContainers(layout: layout, transition: transition) } } modalContainer.interactivelyDismissed = { [weak self, weak modalContainer] in @@ -290,11 +287,7 @@ open class NavigationController: UINavigationController, ContainableController, } strongSelf.setViewControllers(controllers, animated: false) } - containerTransition = .immediate } - - containerTransition.updateFrame(node: modalContainer, frame: CGRect(origin: CGPoint(), size: layout.size)) - modalContainer.update(layout: layout, controllers: navigationLayout.modal[i].controllers, transition: containerTransition) modalContainers.append(modalContainer) } @@ -313,6 +306,26 @@ open class NavigationController: UINavigationController, ContainableController, for i in (0 ..< navigationLayout.modal.count).reversed() { let modalContainer = self.modalContainers[i] + + let containerTransition: ContainedViewLayoutTransition + if modalContainer.supernode == nil { + containerTransition = .immediate + } else { + containerTransition = transition + } + + let effectiveModalTransition: CGFloat + if visibleModalCount == 0 { + effectiveModalTransition = 0.0 + } else if visibleModalCount == 1 { + effectiveModalTransition = 1.0 - topModalDismissProgress + } else { + effectiveModalTransition = 1.0 + } + + containerTransition.updateFrame(node: modalContainer, frame: CGRect(origin: CGPoint(), size: layout.size)) + modalContainer.update(layout: layout, controllers: navigationLayout.modal[i].controllers, coveredByModalTransition: effectiveModalTransition, transition: containerTransition) + if modalContainer.supernode == nil && modalContainer.isReady { if let previousModalContainer = previousModalContainer { self.displayNode.insertSubnode(modalContainer, belowSubnode: previousModalContainer) @@ -321,6 +334,7 @@ open class NavigationController: UINavigationController, ContainableController, } modalContainer.animateIn(transition: transition) } + if modalContainer.supernode != nil { visibleModalCount += 1 if previousModalContainer == nil { @@ -398,6 +412,19 @@ open class NavigationController: UINavigationController, ContainableController, switch layout.metrics.widthClass { case .compact: if visibleModalCount != 0 { + let effectiveRootModalDismissProgress: CGFloat + let additionalModalFrameProgress: CGFloat + if visibleModalCount == 1 { + effectiveRootModalDismissProgress = topModalDismissProgress + additionalModalFrameProgress = 0.0 + } else if visibleModalCount == 2 { + effectiveRootModalDismissProgress = 0.0 + additionalModalFrameProgress = 1.0 - topModalDismissProgress + } else { + effectiveRootModalDismissProgress = 0.0 + additionalModalFrameProgress = 1.0 + } + let rootModalFrame: NavigationModalFrame var modalFrameTransition: ContainedViewLayoutTransition = transition var forceStatusBarAnimation = false @@ -405,14 +432,11 @@ open class NavigationController: UINavigationController, ContainableController, rootModalFrame = current transition.updateFrame(node: rootModalFrame, frame: CGRect(origin: CGPoint(), size: layout.size)) rootModalFrame.update(layout: layout, transition: modalFrameTransition) - rootModalFrame.updateDismissal(transition: transition, progress: topModalDismissProgress, completion: {}) + rootModalFrame.updateDismissal(transition: transition, progress: effectiveRootModalDismissProgress, additionalProgress: additionalModalFrameProgress, completion: {}) forceStatusBarAnimation = true } else { rootModalFrame = NavigationModalFrame(theme: self.theme) self.rootModalFrame = rootModalFrame - rootModalFrame.frame = CGRect(origin: CGPoint(), size: layout.size) - rootModalFrame.update(layout: layout, transition: .immediate) - rootModalFrame.animateIn(transition: transition) if let rootContainer = self.rootContainer { var rootContainerNode: ASDisplayNode switch rootContainer { @@ -423,9 +447,11 @@ open class NavigationController: UINavigationController, ContainableController, } self.displayNode.insertSubnode(rootModalFrame, aboveSubnode: rootContainerNode) } - rootModalFrame.updateDismissal(transition: transition, progress: topModalDismissProgress, completion: {}) + rootModalFrame.frame = CGRect(origin: CGPoint(), size: layout.size) + rootModalFrame.update(layout: layout, transition: .immediate) + rootModalFrame.updateDismissal(transition: transition, progress: effectiveRootModalDismissProgress, additionalProgress: additionalModalFrameProgress, completion: {}) } - if topModalDismissProgress < 0.5 { + if effectiveRootModalDismissProgress < 0.5 { self.statusBarHost?.setStatusBarStyle(.lightContent, animated: transition.isAnimated || forceStatusBarAnimation) } else { let normalStatusBarStyle: UIStatusBarStyle @@ -445,21 +471,28 @@ open class NavigationController: UINavigationController, ContainableController, case let .split(container): rootContainerNode = container } - let maxScale = (layout.size.width - 16.0 * 2.0) / layout.size.width var topInset: CGFloat = 0.0 if let statusBarHeight = layout.statusBarHeight { topInset += statusBarHeight } - let maxOffset: CGFloat = (topInset - (layout.size.height - layout.size.height * maxScale) / 2.0) + let maxScale: CGFloat + let maxOffset: CGFloat + if visibleModalCount == 1 { + maxScale = (layout.size.width - 16.0 * 2.0) / layout.size.width + maxOffset = (topInset - (layout.size.height - layout.size.height * maxScale) / 2.0) + } else { + maxScale = (layout.size.width - 16.0 * 2.0 * 2.0) / layout.size.width + maxOffset = (topInset + 10.0 - (layout.size.height - layout.size.height * maxScale) / 2.0) + } - let scale = 1.0 * topModalDismissProgress + (1.0 - topModalDismissProgress) * maxScale - let offset = (1.0 - topModalDismissProgress) * maxOffset + let scale = 1.0 * effectiveRootModalDismissProgress + (1.0 - effectiveRootModalDismissProgress) * maxScale + let offset = (1.0 - effectiveRootModalDismissProgress) * maxOffset transition.updateSublayerTransformScaleAndOffset(node: rootContainerNode, scale: scale, offset: CGPoint(x: 0.0, y: offset)) } } else { if let rootModalFrame = self.rootModalFrame { self.rootModalFrame = nil - rootModalFrame.updateDismissal(transition: transition, progress: 1.0, completion: { [weak rootModalFrame] in + rootModalFrame.updateDismissal(transition: transition, progress: 1.0, additionalProgress: 0.0, completion: { [weak rootModalFrame] in rootModalFrame?.removeFromSupernode() }) let normalStatusBarStyle: UIStatusBarStyle @@ -485,7 +518,7 @@ open class NavigationController: UINavigationController, ContainableController, case .regular: if let rootModalFrame = self.rootModalFrame { self.rootModalFrame = nil - rootModalFrame.updateDismissal(transition: .immediate, progress: 1.0, completion: { [weak rootModalFrame] in + rootModalFrame.updateDismissal(transition: .immediate, progress: 1.0, additionalProgress: 0.0, completion: { [weak rootModalFrame] in rootModalFrame?.removeFromSupernode() }) let normalStatusBarStyle: UIStatusBarStyle diff --git a/submodules/Display/Display/Navigation/NavigationModalContainer.swift b/submodules/Display/Display/Navigation/NavigationModalContainer.swift index d453ae4f0f..913af838c3 100644 --- a/submodules/Display/Display/Navigation/NavigationModalContainer.swift +++ b/submodules/Display/Display/Navigation/NavigationModalContainer.swift @@ -13,17 +13,16 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate { private(set) var isReady: Bool = false private(set) var dismissProgress: CGFloat = 0.0 var isReadyUpdated: (() -> Void)? - var updateDismissProgress: ((CGFloat) -> Void)? + var updateDismissProgress: ((CGFloat, ContainedViewLayoutTransition) -> Void)? var interactivelyDismissed: (() -> Void)? private var ignoreScrolling = false - private var animator: DisplayLinkAnimator? + private var isDismissed = false init(theme: NavigationControllerTheme, controllerRemoved: @escaping (ViewController) -> Void) { self.theme = theme self.dim = ASDisplayNode() - self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.4) self.dim.alpha = 0.0 self.scrollNode = ASScrollNode() @@ -47,10 +46,8 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate { strongSelf.isReadyUpdated?() } } - } - - deinit { - self.animator?.invalidate() + + applySmoothRoundedCorners(self.container.layer) } override func didLoad() { @@ -69,64 +66,92 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate { } func scrollViewDidScroll(_ scrollView: UIScrollView) { - if self.ignoreScrolling { + if self.ignoreScrolling || self.isDismissed { return } var progress = (self.bounds.height - scrollView.bounds.origin.y) / self.bounds.height progress = max(0.0, min(1.0, progress)) self.dismissProgress = progress self.dim.alpha = 1.0 - progress - self.updateDismissProgress?(progress) + self.updateDismissProgress?(progress, .immediate) } - func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + private var endDraggingVelocity: CGPoint? + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + let velocity = self.endDraggingVelocity ?? CGPoint() + self.endDraggingVelocity = nil + var progress = (self.bounds.height - scrollView.bounds.origin.y) / self.bounds.height progress = max(0.0, min(1.0, progress)) - self.ignoreScrolling = true - targetContentOffset.pointee = scrollView.contentOffset - scrollView.setContentOffset(scrollView.contentOffset, animated: false) - self.ignoreScrolling = false - self.animator?.invalidate() let targetOffset: CGFloat - if velocity.y < -0.5 || progress >= 0.5 { - targetOffset = 0.0 - } else { - targetOffset = self.bounds.height - } let velocityFactor: CGFloat = 0.4 / max(1.0, abs(velocity.y)) - self.animator = DisplayLinkAnimator(duration: Double(min(0.3, velocityFactor)), from: scrollView.contentOffset.y, to: targetOffset, update: { [weak self] value in + let duration = Double(min(0.3, velocityFactor)) + let transition: ContainedViewLayoutTransition + let dismissProgress: CGFloat + if velocity.y < -0.5 || progress >= 0.5 { + dismissProgress = 1.0 + targetOffset = 0.0 + transition = .animated(duration: duration, curve: .easeInOut) + self.isDismissed = true + } else { + dismissProgress = 0.0 + targetOffset = self.bounds.height + transition = .animated(duration: 0.5, curve: .spring) + } + self.ignoreScrolling = true + let deltaY = targetOffset - scrollView.contentOffset.y + scrollView.setContentOffset(scrollView.contentOffset, animated: false) + scrollView.setContentOffset(CGPoint(x: 0.0, y: targetOffset), animated: false) + transition.animateOffsetAdditive(layer: self.scrollNode.layer, offset: -deltaY, completion: { [weak self] in guard let strongSelf = self else { return } - strongSelf.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: value) - }, completion: { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.animator = nil if targetOffset == 0.0 { strongSelf.interactivelyDismissed?() } }) + self.ignoreScrolling = false + self.dismissProgress = dismissProgress + transition.updateAlpha(node: self.dim, alpha: 1.0 - dismissProgress) + self.updateDismissProgress?(dismissProgress, transition) + } + + func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + self.endDraggingVelocity = velocity } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { } - func update(layout: ContainerViewLayout, controllers: [ViewController], transition: ContainedViewLayoutTransition) { - transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size)) - transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: layout.size.height * 2.0) - if !self.scrollNode.view.isDecelerating && !self.scrollNode.view.isDragging && self.animator == nil { - transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: layout.size)) + func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { + return false + } + + func update(layout: ContainerViewLayout, controllers: [ViewController], coveredByModalTransition: CGFloat, transition: ContainedViewLayoutTransition) { + if self.isDismissed { + return } + transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size)) + self.ignoreScrolling = true + self.scrollNode.frame = CGRect(origin: CGPoint(), size: layout.size) + self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: layout.size.height * 2.0) + if !self.scrollNode.view.isDecelerating && !self.scrollNode.view.isDragging { + let defaultBounds = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: layout.size) + if self.scrollNode.bounds != defaultBounds { + self.scrollNode.bounds = defaultBounds + } + } + self.ignoreScrolling = false + let containerLayout: ContainerViewLayout let containerFrame: CGRect + let containerScale: CGFloat switch layout.metrics.widthClass { case .compact: - self.dim.isHidden = true + self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25) self.container.clipsToBounds = true self.container.cornerRadius = 10.0 if #available(iOS 11.0, *) { @@ -139,9 +164,14 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate { } containerLayout = ContainerViewLayout(size: CGSize(width: layout.size.width, height: layout.size.height - topInset), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: layout.intrinsicInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.intrinsicInsets.right), safeInsets: UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: layout.safeInsets.bottom, right: layout.safeInsets.right), statusBarHeight: nil, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver) - containerFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset), size: containerLayout.size) + let unscaledFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset - coveredByModalTransition * 10.0), size: containerLayout.size) + let maxScale: CGFloat = (containerLayout.size.width - 16.0 * 2.0) / containerLayout.size.width + containerScale = 1.0 * (1.0 - coveredByModalTransition) + maxScale * coveredByModalTransition + let maxScaledTopInset: CGFloat = topInset - 10.0 + let scaledTopInset: CGFloat = topInset * (1.0 - coveredByModalTransition) + maxScaledTopInset * coveredByModalTransition + containerFrame = unscaledFrame.offsetBy(dx: 0.0, dy: scaledTopInset - (unscaledFrame.midY - containerScale * unscaledFrame.height / 2.0)) case .regular: - self.dim.isHidden = false + self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.4) self.container.clipsToBounds = true self.container.cornerRadius = 10.0 if #available(iOS 11.0, *) { @@ -153,6 +183,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate { let maxSide = max(layout.size.width, layout.size.height) let containerSize = CGSize(width: min(layout.size.width - 20.0, floor(maxSide / 2.0)), height: layout.size.height - verticalInset * 2.0) containerFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize) + containerScale = 1.0 var inputHeight: CGFloat? if let inputHeightValue = layout.inputHeight { @@ -160,7 +191,8 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate { } containerLayout = ContainerViewLayout(size: containerSize, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver) } - transition.updateFrame(node: self.container, frame: containerFrame.offsetBy(dx: 0.0, dy: layout.size.height)) + transition.updateFrameAsPositionAndBounds(node: self.container, frame: containerFrame.offsetBy(dx: 0.0, dy: layout.size.height)) + transition.updateTransformScale(node: self.container, scale: containerScale) self.container.update(layout: containerLayout, canBeClosed: true, controllers: controllers, transition: transition) } @@ -193,4 +225,30 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate { return transition } } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let result = super.hitTest(point, with: event) else { + return nil + } + var currentParent: UIView? = result + while true { + if currentParent == nil { + break + } + if let scrollView = currentParent as? UIScrollView { + if scrollView === self.scrollNode.view { + break + } + if scrollView.isDecelerating && scrollView.contentOffset.y < scrollView.contentInset.top { + return self.scrollNode.view + } + } else if let listView = currentParent as? ListViewBackingView, let listNode = listView.target { + if listNode.scroller.isDecelerating && listNode.scroller.contentOffset.y < listNode.scroller.contentInset.top { + return self.scrollNode.view + } + } + currentParent = currentParent?.superview + } + return result + } } diff --git a/submodules/Display/Display/Navigation/NavigationModalFrame.swift b/submodules/Display/Display/Navigation/NavigationModalFrame.swift index 6155fd3764..683a2f786b 100644 --- a/submodules/Display/Display/Navigation/NavigationModalFrame.swift +++ b/submodules/Display/Display/Navigation/NavigationModalFrame.swift @@ -9,12 +9,14 @@ private func generateCornerImage(radius: CGFloat, mirror: Bool) -> UIImage? { context.fill(CGRect(origin: CGPoint(), size: size)) context.setBlendMode(.copy) context.setFillColor(UIColor.clear.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(x: mirror ? (-radius) : 0.0, y: 0.0), size: CGSize(width: radius * 2.0, height: radius * 2.0))) + + UIGraphicsPushContext(context) + UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: mirror ? (-radius) : 0.0, y: 0.0), size: CGSize(width: radius * 2.0, height: radius * 2.0)), cornerRadius: radius).fill() + UIGraphicsPopContext() }) } final class NavigationModalFrame: ASDisplayNode { - private let dim: ASDisplayNode private let topShade: ASDisplayNode private let leftShade: ASDisplayNode private let rightShade: ASDisplayNode @@ -23,14 +25,11 @@ final class NavigationModalFrame: ASDisplayNode { private var currentMaxCornerRadius: CGFloat? - private var progress: CGFloat = 0.0 + private var progress: CGFloat = 1.0 + private var additionalProgress: CGFloat = 0.0 private var validLayout: ContainerViewLayout? init(theme: NavigationControllerTheme) { - self.dim = ASDisplayNode() - self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.4) - self.dim.alpha = 0.0 - self.topShade = ASDisplayNode() self.topShade.backgroundColor = .black self.leftShade = ASDisplayNode() @@ -47,7 +46,6 @@ final class NavigationModalFrame: ASDisplayNode { super.init() - self.addSubnode(self.dim) self.addSubnode(self.topShade) self.addSubnode(self.leftShade) self.addSubnode(self.rightShade) @@ -58,39 +56,29 @@ final class NavigationModalFrame: ASDisplayNode { func update(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { self.validLayout = layout - transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size)) - - self.updateShades(layout: layout, progress: 1.0 - self.progress, transition: transition) + self.updateShades(layout: layout, progress: 1.0 - self.progress, additionalProgress: self.additionalProgress, transition: transition, completion: {}) } - func animateIn(transition: ContainedViewLayoutTransition) { - transition.updateAlpha(node: self.dim, alpha: 1.0) - - if let layout = self.validLayout { - self.updateShades(layout: layout, progress: 0.0, transition: .immediate) - self.updateShades(layout: layout, progress: 1.0, transition: transition) - } - } - - func updateDismissal(transition: ContainedViewLayoutTransition, progress: CGFloat, completion: @escaping () -> Void) { + func updateDismissal(transition: ContainedViewLayoutTransition, progress: CGFloat, additionalProgress: CGFloat, completion: @escaping () -> Void) { self.progress = progress + self.additionalProgress = additionalProgress - transition.updateAlpha(node: self.dim, alpha: 1.0 - progress, completion: { _ in - completion() - }) if let layout = self.validLayout { - self.updateShades(layout: layout, progress: 1.0 - progress, transition: transition) + self.updateShades(layout: layout, progress: 1.0 - progress, additionalProgress: additionalProgress, transition: transition, completion: completion) + } else { + completion() } } - private func updateShades(layout: ContainerViewLayout, progress: CGFloat, transition: ContainedViewLayoutTransition) { + private func updateShades(layout: ContainerViewLayout, progress: CGFloat, additionalProgress: CGFloat, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { let sideInset: CGFloat = 16.0 var topInset: CGFloat = 0.0 if let statusBarHeight = layout.statusBarHeight { topInset += statusBarHeight } + let additionalTopInset: CGFloat = 10.0 - let cornerRadius: CGFloat = 8.0 + let cornerRadius: CGFloat = 9.0 let initialCornerRadius: CGFloat if !layout.safeInsets.top.isZero { initialCornerRadius = 40.0 @@ -103,11 +91,20 @@ final class NavigationModalFrame: ASDisplayNode { } let cornerSize = progress * cornerRadius + (1.0 - progress) * initialCornerRadius - transition.updateFrame(node: self.topLeftCorner, frame: CGRect(origin: CGPoint(x: progress * sideInset, y: progress * topInset), size: CGSize(width: cornerSize, height: cornerSize))) - transition.updateFrame(node: self.topRightCorner, frame: CGRect(origin: CGPoint(x: layout.size.width - progress * sideInset - cornerSize, y: progress * topInset), size: CGSize(width: cornerSize, height: cornerSize))) + let cornerSideOffset: CGFloat = progress * sideInset + additionalProgress * sideInset + let cornerTopOffset: CGFloat = progress * topInset + additionalProgress * additionalTopInset + transition.updateFrame(node: self.topLeftCorner, frame: CGRect(origin: CGPoint(x: cornerSideOffset, y: cornerTopOffset), size: CGSize(width: cornerSize, height: cornerSize))) + transition.updateFrame(node: self.topRightCorner, frame: CGRect(origin: CGPoint(x: layout.size.width - cornerSideOffset - cornerSize, y: cornerTopOffset), size: CGSize(width: cornerSize, height: cornerSize))) - transition.updateFrame(node: self.topShade, frame: CGRect(origin: CGPoint(x: 0.0, y: (1.0 - progress) * (-topInset)), size: CGSize(width: layout.size.width, height: topInset))) - transition.updateFrame(node: self.leftShade, frame: CGRect(origin: CGPoint(x: (1.0 - progress) * (-sideInset), y: 0.0), size: CGSize(width: sideInset, height: layout.size.height))) - transition.updateFrame(node: self.rightShade, frame: CGRect(origin: CGPoint(x: layout.size.width - sideInset * progress, y: 0.0), size: CGSize(width: sideInset, height: layout.size.height))) + let topShadeOffset: CGFloat = progress * topInset + additionalProgress * additionalTopInset + let leftShadeOffset: CGFloat = progress * sideInset + additionalProgress * sideInset + let rightShadeWidth: CGFloat = progress * sideInset + additionalProgress * sideInset + let rightShadeOffset: CGFloat = layout.size.width - rightShadeWidth + + transition.updateFrame(node: self.topShade, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: topShadeOffset))) + transition.updateFrame(node: self.leftShade, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: leftShadeOffset, height: layout.size.height))) + transition.updateFrame(node: self.rightShade, frame: CGRect(origin: CGPoint(x: rightShadeOffset, y: 0.0), size: CGSize(width: rightShadeWidth, height: layout.size.height)), completion: { _ in + completion() + }) } } diff --git a/submodules/Display/Display/StatusBarProxyNode.swift b/submodules/Display/Display/StatusBarProxyNode.swift index 82472121ce..c94991ad74 100644 --- a/submodules/Display/Display/StatusBarProxyNode.swift +++ b/submodules/Display/Display/StatusBarProxyNode.swift @@ -10,12 +10,12 @@ public enum StatusBarStyle { public init(systemStyle: UIStatusBarStyle) { switch systemStyle { - case .default: - self = .Black - case .lightContent: - self = .White - case .blackOpaque: - self = .Black + case .default: + self = .Black + case .lightContent: + self = .White + case .blackOpaque: + self = .Black } } diff --git a/submodules/Display/Display/UIKitUtils.h b/submodules/Display/Display/UIKitUtils.h index cf8bde430c..71c6a986d7 100644 --- a/submodules/Display/Display/UIKitUtils.h +++ b/submodules/Display/Display/UIKitUtils.h @@ -13,3 +13,4 @@ CGFloat springAnimationValueAt(CABasicAnimation * _Nonnull animation, CGFloat t) void testZoomBlurEffect(UIVisualEffect *effect); UIBlurEffect *makeCustomZoomBlurEffect(); +void applySmoothRoundedCorners(CALayer * _Nonnull layer); diff --git a/submodules/Display/Display/UIKitUtils.m b/submodules/Display/Display/UIKitUtils.m index 7d5210910d..67a10b3660 100644 --- a/submodules/Display/Display/UIKitUtils.m +++ b/submodules/Display/Display/UIKitUtils.m @@ -154,6 +154,20 @@ static void setNilField(CustomBlurEffect *object, NSString *name) { [inv invoke]; } +static void setBoolField(CustomBlurEffect *object, NSString *name, BOOL value) { + SEL selector = NSSelectorFromString(name); + NSMethodSignature *signature = [[object class] instanceMethodSignatureForSelector:selector]; + if (signature == nil) { + return; + } + + NSInvocation *inv = [NSInvocation invocationWithMethodSignature:signature]; + [inv setSelector:selector]; + [inv setArgument:&value atIndex:2]; + [inv setTarget:object]; + [inv invoke]; +} + UIBlurEffect *makeCustomZoomBlurEffect() { if (@available(iOS 11.0, *)) { NSString *string = [@[@"_", @"UI", @"Custom", @"BlurEffect"] componentsJoinedByString:@""]; @@ -178,3 +192,9 @@ UIBlurEffect *makeCustomZoomBlurEffect() { return [UIBlurEffect effectWithStyle:UIBlurEffectStyleRegular]; } } + +void applySmoothRoundedCorners(CALayer * _Nonnull layer) { + if (@available(iOS 11.0, *)) { + setBoolField(layer, encodeText(@"tfuDpoujovpvtDpsofst;", -1), true); + } +} diff --git a/submodules/Display/Display/ViewController.swift b/submodules/Display/Display/ViewController.swift index ad4b6210f4..28c7bea0b6 100644 --- a/submodules/Display/Display/ViewController.swift +++ b/submodules/Display/Display/ViewController.swift @@ -362,7 +362,6 @@ public enum ViewControllerNavigationPresentation { if !self.isViewLoaded { self.loadView() } - transition.updateFrame(node: self.displayNode, frame: CGRect(origin: self.view.frame.origin, size: layout.size)) if let _ = layout.statusBarHeight { self.statusBar.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: 40.0)) } diff --git a/submodules/Display/Display/WindowContent.swift b/submodules/Display/Display/WindowContent.swift index ac1014361b..d3e6010b81 100644 --- a/submodules/Display/Display/WindowContent.swift +++ b/submodules/Display/Display/WindowContent.swift @@ -199,7 +199,6 @@ 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 @@ -221,10 +220,9 @@ public final class WindowHostView { var forEachController: (((ContainableController) -> Void) -> Void)? var getAccessibilityElements: (() -> [Any]?)? - init(containerView: UIView, eventView: UIView, aboveStatusBarView: UIView, isRotating: @escaping () -> Bool, updateSupportedInterfaceOrientations: @escaping (UIInterfaceOrientationMask) -> Void, updateDeferScreenEdgeGestures: @escaping (UIRectEdge) -> Void, updatePreferNavigationUIHidden: @escaping (Bool) -> Void) { + init(containerView: UIView, eventView: 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 @@ -359,7 +357,7 @@ public class Window1 { self.windowLayout = WindowLayout(size: boundsSize, metrics: layoutMetricsForScreenSize(boundsSize), statusBarHeight: statusBarHeight, forceInCallStatusBarText: self.forceInCallStatusBarText, inputHeight: 0.0, safeInsets: safeInsets, 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.aboveStatusBarView) + self.overlayPresentationContext = GlobalOverlayPresentationContext(statusBarHost: statusBarHost, parentView: self.hostView.containerView) self.presentationContext.updateIsInteractionBlocked = { [weak self] value in self?.isInteractionBlocked = value @@ -697,6 +695,7 @@ public class Window1 { rootController.keyboardManager = self.keyboardManager } if !self.windowLayout.size.width.isZero && !self.windowLayout.size.height.isZero { + rootController.displayNode.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size) rootController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate) } @@ -720,6 +719,7 @@ public class Window1 { let layout = containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics) for controller in self._topLevelOverlayControllers { + controller.displayNode.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size) controller.containerLayoutUpdated(layout, transition: .immediate) if let coveringView = self.coveringView { @@ -977,11 +977,15 @@ public class Window1 { if self.presentationContext.isCurrentlyOpaque { rootLayout.inputHeight = nil } - self._rootController?.containerLayoutUpdated(rootLayout, transition: rootTransition) + if let rootController = self._rootController { + rootTransition.updateFrame(node: rootController.displayNode, frame: CGRect(origin: CGPoint(), size: self.windowLayout.size)) + rootController.containerLayoutUpdated(rootLayout, transition: rootTransition) + } self.presentationContext.containerLayoutUpdated(childLayout, transition: updatingLayout.transition) self.overlayPresentationContext.containerLayoutUpdated(childLayout, transition: updatingLayout.transition) for controller in self.topLevelOverlayControllers { + updatingLayout.transition.updateFrame(node: controller.displayNode, frame: CGRect(origin: CGPoint(), size: self.windowLayout.size)) controller.containerLayoutUpdated(childLayout, transition: updatingLayout.transition) } } diff --git a/submodules/HorizontalPeerItem/BUCK b/submodules/HorizontalPeerItem/BUCK index 82120ebfe3..3f152913e8 100644 --- a/submodules/HorizontalPeerItem/BUCK +++ b/submodules/HorizontalPeerItem/BUCK @@ -15,6 +15,7 @@ static_library( "//submodules/SelectablePeerNode:SelectablePeerNode", "//submodules/PeerOnlineMarkerNode:PeerOnlineMarkerNode", "//submodules/TelegramStringFormatting:TelegramStringFormatting", + "//submodules/ContextUI:ContextUI", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift index e93cbe00c0..3463c7d8b2 100644 --- a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift +++ b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift @@ -9,6 +9,7 @@ import TelegramPresentationData import TelegramStringFormatting import PeerOnlineMarkerNode import SelectablePeerNode +import ContextUI public enum HorizontalPeerItemMode { case list @@ -24,20 +25,20 @@ public final class HorizontalPeerItem: ListViewItem { let account: Account public let peer: Peer let action: (Peer) -> Void - let longTapAction: (Peer) -> Void + let contextAction: (Peer, ASDisplayNode, ContextGesture?) -> Void let isPeerSelected: (PeerId) -> Bool let customWidth: CGFloat? let presence: PeerPresence? let unreadBadge: (Int32, Bool)? - public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, account: Account, peer: Peer, presence: PeerPresence?, unreadBadge: (Int32, Bool)?, action: @escaping (Peer) -> Void, longTapAction: @escaping (Peer) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, customWidth: CGFloat?) { + public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, account: Account, peer: Peer, presence: PeerPresence?, unreadBadge: (Int32, Bool)?, action: @escaping (Peer) -> Void, contextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, customWidth: CGFloat?) { self.theme = theme self.strings = strings self.mode = mode self.account = account self.peer = peer self.action = action - self.longTapAction = longTapAction + self.contextAction = contextAction self.isPeerSelected = isPeerSelected self.customWidth = customWidth self.presence = presence @@ -110,9 +111,9 @@ public final class HorizontalPeerItemNode: ListViewItemNode { item.action(item.peer) } } - self.peerNode.longTapAction = { [weak self] in + self.peerNode.contextAction = { [weak self] node, gesture in if let item = self?.item { - item.longTapAction(item.peer) + item.contextAction(item.peer, node, gesture) } } } diff --git a/submodules/InstantPageUI/Sources/InstantPageController.swift b/submodules/InstantPageUI/Sources/InstantPageController.swift index d33f358d9a..f3913033b7 100644 --- a/submodules/InstantPageUI/Sources/InstantPageController.swift +++ b/submodules/InstantPageUI/Sources/InstantPageController.swift @@ -42,6 +42,8 @@ public final class InstantPageController: ViewController { super.init(navigationBarPresentationData: nil) + self.navigationPresentation = .modalInLargeLayout + self.statusBar.statusBarStyle = .White self.webpageDisposable = (actualizedWebpage(postbox: self.context.account.postbox, network: self.context.account.network, webpage: webPage) |> deliverOnMainQueue).start(next: { [weak self] result in diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index eeeddc64b3..e11318bf76 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -1219,7 +1219,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } if let map = media.media as? TelegramMediaMap { - let controller = legacyLocationController(message: nil, mapMedia: map, context: self.context, isModal: false, openPeer: { _ in }, sendLiveLocation: { _, _ in }, stopLiveLocation: { }, openUrl: { _ in }) + let controller = legacyLocationController(message: nil, mapMedia: map, context: self.context, openPeer: { _ in }, sendLiveLocation: { _, _ in }, stopLiveLocation: { }, openUrl: { _ in }) self.pushController(controller) return } diff --git a/submodules/LocationUI/Sources/LegacyLocationController.swift b/submodules/LocationUI/Sources/LegacyLocationController.swift index 4c8903b1bb..f4b1126ba7 100644 --- a/submodules/LocationUI/Sources/LegacyLocationController.swift +++ b/submodules/LocationUI/Sources/LegacyLocationController.swift @@ -127,7 +127,7 @@ func legacyLocationPalette(from theme: PresentationTheme) -> TGLocationPallete { return TGLocationPallete(backgroundColor: listTheme.plainBackgroundColor, selectionColor: listTheme.itemHighlightedBackgroundColor, separatorColor: listTheme.itemPlainSeparatorColor, textColor: listTheme.itemPrimaryTextColor, secondaryTextColor: listTheme.itemSecondaryTextColor, accentColor: listTheme.itemAccentColor, destructiveColor: listTheme.itemDestructiveColor, locationColor: UIColor(rgb: 0x008df2), liveLocationColor: UIColor(rgb: 0xff6464), iconColor: searchTheme.backgroundColor, sectionHeaderBackgroundColor: theme.chatList.sectionHeaderFillColor, sectionHeaderTextColor: theme.chatList.sectionHeaderTextColor, searchBarPallete: TGSearchBarPallete(dark: theme.overallDarkAppearance, backgroundColor: searchTheme.backgroundColor, highContrastBackgroundColor: searchTheme.backgroundColor, textColor: searchTheme.inputTextColor, placeholderColor: searchTheme.inputPlaceholderTextColor, clearIcon: generateClearIcon(color: theme.rootController.navigationSearchBar.inputClearButtonColor), barBackgroundColor: searchTheme.backgroundColor, barSeparatorColor: searchTheme.separatorColor, plainBackgroundColor: searchTheme.backgroundColor, accentColor: searchTheme.accentColor, accentContrastColor: searchTheme.backgroundColor, menuBackgroundColor: searchTheme.backgroundColor, segmentedControlBackgroundImage: nil, segmentedControlSelectedImage: nil, segmentedControlHighlightedImage: nil, segmentedControlDividerImage: nil), avatarPlaceholder: nil) } -public func legacyLocationController(message: Message?, mapMedia: TelegramMediaMap, context: AccountContext, isModal: Bool, openPeer: @escaping (Peer) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, stopLiveLocation: @escaping () -> Void, openUrl: @escaping (String) -> Void) -> ViewController { +public func legacyLocationController(message: Message?, mapMedia: TelegramMediaMap, context: AccountContext, openPeer: @escaping (Peer) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, stopLiveLocation: @escaping () -> Void, openUrl: @escaping (String) -> Void) -> ViewController { let legacyLocation = TGLocationMediaAttachment() legacyLocation.latitude = mapMedia.latitude legacyLocation.longitude = mapMedia.longitude @@ -137,7 +137,8 @@ public func legacyLocationController(message: Message?, mapMedia: TelegramMediaM let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let legacyController = LegacyController(presentation: isModal ? .modal(animateIn: true) : .navigation, theme: presentationData.theme, strings: presentationData.strings) + let legacyController = LegacyController(presentation: .navigation, theme: presentationData.theme, strings: presentationData.strings) + legacyController.navigationPresentation = .modal let controller: TGLocationViewController if let message = message { @@ -234,7 +235,7 @@ public func legacyLocationController(message: Message?, mapMedia: TelegramMediaM let theme = (context.sharedContext.currentPresentationData.with { $0 }).theme controller.pallete = legacyLocationPalette(from: theme) - controller.modalMode = isModal + controller.modalMode = true controller.presentActionsMenu = { [weak legacyController] legacyLocation, directions in if let strongLegacyController = legacyController, let location = legacyLocation { let map = telegramMap(for: location) @@ -256,23 +257,15 @@ public func legacyLocationController(message: Message?, mapMedia: TelegramMediaM } } - if isModal { - let navigationController = TGNavigationController(controllers: [controller])! - legacyController.bind(controller: navigationController) - controller.navigation_setDismiss({ [weak legacyController] in - legacyController?.dismiss() - }, rootController: nil) - } else { - legacyController.navigationItem.title = controller.navigationItem.title - controller.updateRightBarItem = { item, action, animated in - if action { - legacyController.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationShareIcon(theme), style: .plain, target: controller, action: #selector(controller.actionsButtonPressed)) - } else { - legacyController.navigationItem.setRightBarButton(item, animated: animated) - } + legacyController.navigationItem.title = controller.navigationItem.title + controller.updateRightBarItem = { item, action, animated in + if action { + legacyController.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationShareIcon(theme), style: .plain, target: controller, action: #selector(controller.actionsButtonPressed)) + } else { + legacyController.navigationItem.setRightBarButton(item, animated: animated) } - legacyController.bind(controller: controller) } + legacyController.bind(controller: controller) let presentationDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in if let controller = controller { diff --git a/submodules/PeerInfoUI/Sources/GroupInfoController.swift b/submodules/PeerInfoUI/Sources/GroupInfoController.swift index c4a4d4ecf2..06c8beae09 100644 --- a/submodules/PeerInfoUI/Sources/GroupInfoController.swift +++ b/submodules/PeerInfoUI/Sources/GroupInfoController.swift @@ -2028,7 +2028,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId: return } let mapMedia = TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, geoPlace: nil, venue: MapVenue(title: peer.displayTitle, address: location.address, provider: nil, id: nil, type: nil), liveBroadcastingTimeout: nil) - let controller = legacyLocationController(message: nil, mapMedia: mapMedia, context: context, isModal: false, openPeer: { _ in }, sendLiveLocation: { _, _ in }, stopLiveLocation: {}, openUrl: { url in + let controller = legacyLocationController(message: nil, mapMedia: mapMedia, context: context, openPeer: { _ in }, sendLiveLocation: { _, _ in }, stopLiveLocation: {}, openUrl: { url in context.sharedContext.applicationBindings.openUrl(url) }) pushControllerImpl?(controller) diff --git a/submodules/SelectablePeerNode/BUCK b/submodules/SelectablePeerNode/BUCK index 0226d71817..a7aee01d3c 100644 --- a/submodules/SelectablePeerNode/BUCK +++ b/submodules/SelectablePeerNode/BUCK @@ -15,6 +15,7 @@ static_library( "//submodules/PeerOnlineMarkerNode:PeerOnlineMarkerNode", "//submodules/AvatarNode:AvatarNode", "//submodules/LegacyComponents:LegacyComponents", + "//submodules/ContextUI:ContextUI", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift index 4806d1be05..29eae2ebf4 100644 --- a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift +++ b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift @@ -9,6 +9,7 @@ import TelegramPresentationData import AvatarNode import PeerOnlineMarkerNode import LegacyComponents +import ContextUI private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 24.0)! private let textFont = Font.regular(11.0) @@ -62,6 +63,7 @@ public final class SelectablePeerNodeTheme { } public final class SelectablePeerNode: ASDisplayNode { + private let contextContainer: ContextControllerSourceNode private let avatarSelectionNode: ASImageNode private let avatarNodeContainer: ASDisplayNode private let avatarNode: AvatarNode @@ -70,7 +72,11 @@ public final class SelectablePeerNode: ASDisplayNode { private let textNode: ASTextNode public var toggleSelection: (() -> Void)? - public var longTapAction: (() -> Void)? + public var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? { + didSet { + self.contextContainer.isGestureEnabled = self.contextAction != nil + } + } private var currentSelected = false @@ -87,6 +93,9 @@ public final class SelectablePeerNode: ASDisplayNode { } override public init() { + self.contextContainer = ContextControllerSourceNode() + self.contextContainer.isGestureEnabled = false + self.avatarNodeContainer = ASDisplayNode() self.avatarSelectionNode = ASImageNode() @@ -108,11 +117,20 @@ public final class SelectablePeerNode: ASDisplayNode { super.init() + self.addSubnode(self.contextContainer) self.avatarNodeContainer.addSubnode(self.avatarSelectionNode) self.avatarNodeContainer.addSubnode(self.avatarNode) - self.addSubnode(self.avatarNodeContainer) - self.addSubnode(self.textNode) - self.addSubnode(self.onlineNode) + self.contextContainer.addSubnode(self.avatarNodeContainer) + self.contextContainer.addSubnode(self.textNode) + self.contextContainer.addSubnode(self.onlineNode) + + self.contextContainer.activated = { [weak self] gesture in + guard let strongSelf = self, let contextAction = strongSelf.contextAction else { + gesture.cancel() + return + } + contextAction(strongSelf.contextContainer, gesture) + } } public func setup(account: Account, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) { @@ -210,16 +228,6 @@ public final class SelectablePeerNode: ASDisplayNode { super.didLoad() self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) - - let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.longTapGesture(_:))) - longTapRecognizer.minimumPressDuration = 0.3 - self.view.addGestureRecognizer(longTapRecognizer) - } - - @objc private func longTapGesture(_ recognizer: UILongPressGestureRecognizer) { - if case .began = recognizer.state { - self.longTapAction?() - } } @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { @@ -233,6 +241,8 @@ public final class SelectablePeerNode: ASDisplayNode { let bounds = self.bounds + self.contextContainer.frame = bounds + self.avatarNodeContainer.frame = CGRect(origin: CGPoint(x: floor((bounds.size.width - 60.0) / 2.0), y: 4.0), size: CGSize(width: 60.0, height: 60.0)) self.textNode.frame = CGRect(origin: CGPoint(x: 2.0, y: 4.0 + 60.0 + 4.0), size: CGSize(width: bounds.size.width - 4.0, height: 34.0)) diff --git a/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift b/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift index 2b2153982c..acc47ca20a 100644 --- a/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift @@ -61,7 +61,7 @@ final class ShareControllerRecentPeersGridItemNode: GridItemNode { } else { peersNode = ChatListSearchRecentPeersNode(account: account, theme: theme, mode: .actionSheet, strings: strings, peerSelected: { [weak self] peer in self?.controllerInteraction?.togglePeer(RenderedPeer(peer: peer), true) - }, peerLongTapped: {_ in }, isPeerSelected: { [weak self] peerId in + }, peerContextAction: { _, _, gesture in gesture?.cancel() }, isPeerSelected: { [weak self] peerId in return self?.controllerInteraction?.selectedPeerIds.contains(peerId) ?? false }, share: true) self.peersNode = peersNode diff --git a/submodules/TelegramUI/TelegramUI/AppDelegate.swift b/submodules/TelegramUI/TelegramUI/AppDelegate.swift index 78e8087678..35112f5fb8 100644 --- a/submodules/TelegramUI/TelegramUI/AppDelegate.swift +++ b/submodules/TelegramUI/TelegramUI/AppDelegate.swift @@ -69,7 +69,7 @@ private class ApplicationStatusBarHost: StatusBarHost { } var statusBarWindow: UIView? { - return self.application.value(forKey: "statusBarWindow") as? UIView + return nil//self.application.value(forKey: "statusBarWindow") as? UIView } var statusBarView: UIView? { @@ -165,7 +165,6 @@ final class SharedApplicationContext { @objc var window: UIWindow? var nativeWindow: (UIWindow & WindowHost)? var mainWindow: Window1! - var aboveStatusbarWindow: UIWindow? private var dataImportSplash: LegacyDataImportSplash? let episodeId = arc4random() @@ -240,9 +239,8 @@ final class SharedApplicationContext { let statusBarHost = ApplicationStatusBarHost() - let (window, hostView, aboveStatusbarWindow) = nativeWindowHostView() + let (window, hostView) = nativeWindowHostView() self.mainWindow = Window1(hostView: hostView, statusBarHost: statusBarHost) - self.aboveStatusbarWindow = aboveStatusbarWindow hostView.containerView.backgroundColor = UIColor.white self.window = window self.nativeWindow = window @@ -448,7 +446,6 @@ final class SharedApplicationContext { #endif #endif - self.aboveStatusbarWindow?.isHidden = false self.window?.makeKeyAndVisible() self.hasActiveAudioSession.set(MediaManagerImpl.globalAudioSession.isActive()) @@ -1093,7 +1090,9 @@ final class SharedApplicationContext { }) let pushRegistry = PKPushRegistry(queue: .main) - pushRegistry.desiredPushTypes = Set([.voIP]) + if #available(iOS 9.0, *) { + pushRegistry.desiredPushTypes = Set([.voIP]) + } self.pushRegistry = pushRegistry pushRegistry.delegate = self @@ -1341,24 +1340,28 @@ final class SharedApplicationContext { } public func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) { - if case PKPushType.voIP = type { - Logger.shared.log("App \(self.episodeId)", "pushRegistry credentials: \(credentials.token as NSData)") - - self.voipTokenPromise.set(.single(credentials.token)) + if #available(iOS 9.0, *) { + if case PKPushType.voIP = type { + Logger.shared.log("App \(self.episodeId)", "pushRegistry credentials: \(credentials.token as NSData)") + + self.voipTokenPromise.set(.single(credentials.token)) + } } } public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) { - let _ = (self.sharedContextPromise.get() - |> take(1) - |> deliverOnMainQueue).start(next: { sharedApplicationContext in - sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0) - - if case PKPushType.voIP = type { - Logger.shared.log("App \(self.episodeId)", "pushRegistry payload: \(payload.dictionaryPayload)") - sharedApplicationContext.notificationManager.addNotification(payload.dictionaryPayload) - } - }) + if #available(iOS 9.0, *) { + let _ = (self.sharedContextPromise.get() + |> take(1) + |> deliverOnMainQueue).start(next: { sharedApplicationContext in + sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0) + + if case PKPushType.voIP = type { + Logger.shared.log("App \(self.episodeId)", "pushRegistry payload: \(payload.dictionaryPayload)") + sharedApplicationContext.notificationManager.addNotification(payload.dictionaryPayload) + } + }) + } } public func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) { diff --git a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift index 5ac73f87a4..a280fae0b8 100644 --- a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift +++ b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift @@ -253,7 +253,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { case let .map(mapMedia): params.dismissInput() - let controller = legacyLocationController(message: params.message, mapMedia: mapMedia, context: params.context, isModal: params.modal, openPeer: { peer in + let controller = legacyLocationController(message: params.message, mapMedia: mapMedia, context: params.context, openPeer: { peer in params.openPeer(peer, .info) }, sendLiveLocation: { coordinate, period in let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, geoPlace: nil, venue: nil, liveBroadcastingTimeout: period)), replyToMessageId: nil, localGroupingKey: nil) @@ -426,7 +426,6 @@ func openChatInstantPage(context: AccountContext, message: Message, sourcePeerTy } let pageController = InstantPageController(context: context, webPage: webpage, sourcePeerType: sourcePeerType ?? .channel, anchor: anchor) - pageController.navigationPresentation = .modal navigationController.pushViewController(pageController) } break